blob: 758934681250f4ad05a26e1c8b6b6ee23ede7683 [file] [log] [blame]
koder aka kdanilovda45e882015-04-06 02:24:42 +03001import sys
2import time
3import json
koder aka kdanilovb896f692015-04-07 14:57:55 +03004import random
koder aka kdanilovda45e882015-04-06 02:24:42 +03005import select
6import pprint
7import argparse
8import traceback
9import subprocess
10import itertools
11from collections import OrderedDict
12
13
14SECTION = 0
15SETTING = 1
16
17
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030018def get_test_sync_mode(config):
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +030019 try:
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030020 return config['sync_mode']
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +030021 except KeyError:
22 pass
23
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030024 is_sync = config.get("sync", "0") == "1"
25 is_direct = config.get("direct", "0") == "1"
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +030026
27 if is_sync and is_direct:
28 return 'x'
29 elif is_sync:
30 return 's'
31 elif is_direct:
32 return 'd'
33 else:
34 return 'a'
35
36
koder aka kdanilovda45e882015-04-06 02:24:42 +030037def get_test_summary(params):
38 rw = {"randread": "rr",
39 "randwrite": "rw",
40 "read": "sr",
41 "write": "sw"}[params["rw"]]
42
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +030043 sync_mode = get_test_sync_mode(params)
44 th_count = params.get('numjobs')
45 if th_count is None:
46 th_count = params.get('concurence', '1')
47 th_count = int(th_count)
koder aka kdanilovda45e882015-04-06 02:24:42 +030048
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +030049 return "{0}{1}{2}th{3}".format(rw,
50 sync_mode,
51 params['blocksize'],
52 th_count)
koder aka kdanilovda45e882015-04-06 02:24:42 +030053
54
55counter = [0]
56
57
58def process_section(name, vals, defaults, format_params):
59 vals = vals.copy()
60 params = format_params.copy()
61
62 if '*' in name:
63 name, repeat = name.split('*')
64 name = name.strip()
65 repeat = int(repeat.format(**params))
66 else:
67 repeat = 1
68
69 # this code can be optimized
koder aka kdanilovb896f692015-04-07 14:57:55 +030070 iterable_names = []
71 iterable_values = []
72 processed_vals = {}
koder aka kdanilovda45e882015-04-06 02:24:42 +030073
koder aka kdanilovb896f692015-04-07 14:57:55 +030074 for val_name, val in vals.items():
75 if val is None:
76 processed_vals[val_name] = val
77 # remove hardcode
78 elif val.startswith('{%'):
79 assert val.endswith("%}")
80 content = val[2:-2].format(**params)
81 iterable_names.append(val_name)
82 iterable_values.append(list(i.strip() for i in content.split(',')))
83 else:
84 processed_vals[val_name] = val.format(**params)
koder aka kdanilovda45e882015-04-06 02:24:42 +030085
koder aka kdanilov2e928022015-04-08 13:47:15 +030086 group_report_err_msg = "Group reporting should be set if numjobs != 1"
87
koder aka kdanilovb896f692015-04-07 14:57:55 +030088 if iterable_values == []:
89 params['UNIQ'] = 'UN{0}'.format(counter[0])
90 counter[0] += 1
91 params['TEST_SUMM'] = get_test_summary(processed_vals)
koder aka kdanilov2e928022015-04-08 13:47:15 +030092
93 if processed_vals.get('numjobs', '1') != '1':
94 assert 'group_reporting' in processed_vals, group_report_err_msg
95
koder aka kdanilov652cd802015-04-13 12:21:07 +030096 ramp_time = processed_vals.get('ramp_time')
koder aka kdanilovb896f692015-04-07 14:57:55 +030097 for i in range(repeat):
koder aka kdanilov2e928022015-04-08 13:47:15 +030098 yield name.format(**params), processed_vals.copy()
koder aka kdanilov652cd802015-04-13 12:21:07 +030099
100 if 'ramp_time' in processed_vals:
101 del processed_vals['ramp_time']
102
103 if ramp_time is not None:
104 processed_vals['ramp_time'] = ramp_time
koder aka kdanilovb896f692015-04-07 14:57:55 +0300105 else:
106 for it_vals in itertools.product(*iterable_values):
107 processed_vals.update(dict(zip(iterable_names, it_vals)))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300108 params['UNIQ'] = 'UN{0}'.format(counter[0])
109 counter[0] += 1
110 params['TEST_SUMM'] = get_test_summary(processed_vals)
koder aka kdanilov2e928022015-04-08 13:47:15 +0300111
112 if processed_vals.get('numjobs', '1') != '1':
113 assert 'group_reporting' in processed_vals,\
114 group_report_err_msg
115
koder aka kdanilov66839a92015-04-11 13:22:31 +0300116 ramp_time = processed_vals.get('ramp_time')
117
koder aka kdanilovb896f692015-04-07 14:57:55 +0300118 for i in range(repeat):
119 yield name.format(**params), processed_vals.copy()
koder aka kdanilov66839a92015-04-11 13:22:31 +0300120 if 'ramp_time' in processed_vals:
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300121 processed_vals['_ramp_time'] = ramp_time
122 processed_vals.pop('ramp_time')
koder aka kdanilov66839a92015-04-11 13:22:31 +0300123
124 if ramp_time is not None:
125 processed_vals['ramp_time'] = ramp_time
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300126 processed_vals.pop('_ramp_time')
koder aka kdanilovda45e882015-04-06 02:24:42 +0300127
128
129def calculate_execution_time(combinations):
130 time = 0
131 for _, params in combinations:
132 time += int(params.get('ramp_time', 0))
133 time += int(params.get('runtime', 0))
134 return time
135
136
137def parse_fio_config_full(fio_cfg, params=None):
138 defaults = {}
139 format_params = {}
140
141 if params is None:
142 ext_params = {}
143 else:
144 ext_params = params.copy()
145
146 curr_section = None
147 curr_section_name = None
148
149 for tp, name, val in parse_fio_config_iter(fio_cfg):
150 if tp == SECTION:
151 non_def = curr_section_name != 'defaults'
152 if curr_section_name is not None and non_def:
153 format_params.update(ext_params)
154 for sec in process_section(curr_section_name,
155 curr_section,
156 defaults,
157 format_params):
158 yield sec
159
160 if name == 'defaults':
161 curr_section = defaults
162 else:
163 curr_section = OrderedDict()
164 curr_section.update(defaults)
165 curr_section_name = name
166
167 else:
168 assert tp == SETTING
169 assert curr_section_name is not None, "no section name"
170 if name == name.upper():
171 assert curr_section_name == 'defaults'
172 format_params[name] = val
173 else:
174 curr_section[name] = val
175
176 if curr_section_name is not None and curr_section_name != 'defaults':
177 format_params.update(ext_params)
178 for sec in process_section(curr_section_name,
179 curr_section,
180 defaults,
181 format_params):
182 yield sec
183
184
185def parse_fio_config_iter(fio_cfg):
186 for lineno, line in enumerate(fio_cfg.split("\n")):
187 try:
188 line = line.strip()
189
190 if line.startswith("#") or line.startswith(";"):
191 continue
192
193 if line == "":
194 continue
195
196 if line.startswith('['):
197 assert line.endswith(']'), "name should ends with ]"
198 yield SECTION, line[1:-1], None
199 elif '=' in line:
200 opt_name, opt_val = line.split('=', 1)
201 yield SETTING, opt_name.strip(), opt_val.strip()
202 else:
203 yield SETTING, line, None
204 except Exception as exc:
205 pref = "During parsing line number {0}\n".format(lineno)
206 raise ValueError(pref + exc.message)
207
208
209def format_fio_config(fio_cfg):
210 res = ""
211 for pos, (name, section) in enumerate(fio_cfg):
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300212 if name.startswith('_'):
213 continue
214
koder aka kdanilovda45e882015-04-06 02:24:42 +0300215 if pos != 0:
216 res += "\n"
217
218 res += "[{0}]\n".format(name)
219 for opt_name, opt_val in section.items():
220 if opt_val is None:
221 res += opt_name + "\n"
222 else:
223 res += "{0}={1}\n".format(opt_name, opt_val)
224 return res
225
226
koder aka kdanilovb896f692015-04-07 14:57:55 +0300227count = 0
228
229
230def to_bytes(sz):
231 sz = sz.lower()
232 try:
233 return int(sz)
234 except ValueError:
235 if sz[-1] == 'm':
236 return (1024 ** 2) * int(sz[:-1])
237 if sz[-1] == 'k':
238 return 1024 * int(sz[:-1])
239 raise
240
241
koder aka kdanilovb896f692015-04-07 14:57:55 +0300242def do_run_fio_fake(bconf):
koder aka kdanilov66839a92015-04-11 13:22:31 +0300243 def estimate_iops(sz, bw, lat):
244 return 1 / (lat + float(sz) / bw)
koder aka kdanilovb896f692015-04-07 14:57:55 +0300245 global count
246 count += 1
247 parsed_out = []
248
249 BW = 120.0 * (1024 ** 2)
250 LAT = 0.003
251
252 for name, cfg in bconf:
253 sz = to_bytes(cfg['blocksize'])
254 curr_lat = LAT * ((random.random() - 0.5) * 0.1 + 1)
255 curr_ulat = curr_lat * 1000000
256 curr_bw = BW * ((random.random() - 0.5) * 0.1 + 1)
257 iops = estimate_iops(sz, curr_bw, curr_lat)
258 bw = iops * sz
259
260 res = {'ctx': 10683,
261 'error': 0,
262 'groupid': 0,
263 'jobname': name,
264 'majf': 0,
265 'minf': 30,
266 'read': {'bw': 0,
267 'bw_agg': 0.0,
268 'bw_dev': 0.0,
269 'bw_max': 0,
270 'bw_mean': 0.0,
271 'bw_min': 0,
272 'clat': {'max': 0,
273 'mean': 0.0,
274 'min': 0,
275 'stddev': 0.0},
276 'io_bytes': 0,
277 'iops': 0,
278 'lat': {'max': 0, 'mean': 0.0,
279 'min': 0, 'stddev': 0.0},
280 'runtime': 0,
281 'slat': {'max': 0, 'mean': 0.0,
282 'min': 0, 'stddev': 0.0}
283 },
284 'sys_cpu': 0.64,
285 'trim': {'bw': 0,
286 'bw_agg': 0.0,
287 'bw_dev': 0.0,
288 'bw_max': 0,
289 'bw_mean': 0.0,
290 'bw_min': 0,
291 'clat': {'max': 0,
292 'mean': 0.0,
293 'min': 0,
294 'stddev': 0.0},
295 'io_bytes': 0,
296 'iops': 0,
297 'lat': {'max': 0, 'mean': 0.0,
298 'min': 0, 'stddev': 0.0},
299 'runtime': 0,
300 'slat': {'max': 0, 'mean': 0.0,
301 'min': 0, 'stddev': 0.0}
302 },
303 'usr_cpu': 0.23,
304 'write': {'bw': 0,
305 'bw_agg': 0,
306 'bw_dev': 0,
307 'bw_max': 0,
308 'bw_mean': 0,
309 'bw_min': 0,
310 'clat': {'max': 0, 'mean': 0,
311 'min': 0, 'stddev': 0},
312 'io_bytes': 0,
313 'iops': 0,
314 'lat': {'max': 0, 'mean': 0,
315 'min': 0, 'stddev': 0},
316 'runtime': 0,
317 'slat': {'max': 0, 'mean': 0.0,
318 'min': 0, 'stddev': 0.0}
319 }
320 }
321
322 if cfg['rw'] in ('read', 'randread'):
323 key = 'read'
324 elif cfg['rw'] in ('write', 'randwrite'):
325 key = 'write'
326 else:
327 raise ValueError("Uknown op type {0}".format(key))
328
329 res[key]['bw'] = bw
330 res[key]['iops'] = iops
331 res[key]['runtime'] = 30
332 res[key]['io_bytes'] = res[key]['runtime'] * bw
333 res[key]['bw_agg'] = bw
334 res[key]['bw_dev'] = bw / 30
335 res[key]['bw_max'] = bw * 1.5
336 res[key]['bw_min'] = bw / 1.5
337 res[key]['bw_mean'] = bw
338 res[key]['clat'] = {'max': curr_ulat * 10, 'mean': curr_ulat,
339 'min': curr_ulat / 2, 'stddev': curr_ulat}
340 res[key]['lat'] = res[key]['clat'].copy()
341 res[key]['slat'] = res[key]['clat'].copy()
342
343 parsed_out.append(res)
344
345 return zip(parsed_out, bconf)
346
347
koder aka kdanilovda45e882015-04-06 02:24:42 +0300348def do_run_fio(bconf):
349 benchmark_config = format_fio_config(bconf)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300350 cmd = ["fio", "--output-format=json", "--alloc-size=262144", "-"]
koder aka kdanilovda45e882015-04-06 02:24:42 +0300351 p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
352 stdout=subprocess.PIPE,
353 stderr=subprocess.STDOUT)
354
355 # set timeout
356 raw_out, _ = p.communicate(benchmark_config)
357
358 try:
359 parsed_out = json.loads(raw_out)["jobs"]
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300360 except KeyError:
361 msg = "Can't parse fio output {0!r}: no 'jobs' found"
362 raw_out = raw_out[:100]
363 raise ValueError(msg.format(raw_out))
364
365 except Exception as exc:
koder aka kdanilovda45e882015-04-06 02:24:42 +0300366 msg = "Can't parse fio output: {0!r}\nError: {1}"
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300367 raw_out = raw_out[:100]
368 raise ValueError(msg.format(raw_out, exc.message))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300369
370 return zip(parsed_out, bconf)
371
koder aka kdanilovda45e882015-04-06 02:24:42 +0300372# limited by fio
373MAX_JOBS = 1000
374
375
376def next_test_portion(whole_conf, runcycle):
377 jcount = 0
378 runtime = 0
379 bconf = []
380
381 for pos, (name, sec) in enumerate(whole_conf):
382 jc = int(sec.get('numjobs', '1'))
383
384 if runcycle is not None:
385 curr_task_time = calculate_execution_time([(name, sec)])
386 else:
387 curr_task_time = 0
388
389 if jc > MAX_JOBS:
390 err_templ = "Can't process job {0!r} - too large numjobs"
391 raise ValueError(err_templ.format(name))
392
393 if runcycle is not None and len(bconf) != 0:
394 rc_ok = curr_task_time + runtime <= runcycle
395 else:
396 rc_ok = True
397
398 if jc + jcount <= MAX_JOBS and rc_ok:
399 runtime += curr_task_time
400 jcount += jc
401 bconf.append((name, sec))
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300402 if '_ramp_time' in sec:
403 del sec['_ramp_time']
koder aka kdanilovda45e882015-04-06 02:24:42 +0300404 continue
405
406 assert len(bconf) != 0
407 yield bconf
408
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300409 if '_ramp_time' in sec:
410 sec['ramp_time'] = sec.pop('_ramp_time')
411 curr_task_time = calculate_execution_time([(name, sec)])
412
koder aka kdanilovda45e882015-04-06 02:24:42 +0300413 runtime = curr_task_time
414 jcount = jc
415 bconf = [(name, sec)]
416
417 if bconf != []:
418 yield bconf
419
420
421def add_job_results(jname, job_output, jconfig, res):
422 if job_output['write']['iops'] != 0:
423 raw_result = job_output['write']
424 else:
425 raw_result = job_output['read']
426
427 if jname not in res:
428 j_res = {}
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300429 j_res["rw"] = jconfig["rw"]
koder aka kdanilov652cd802015-04-13 12:21:07 +0300430 j_res["sync_mode"] = get_test_sync_mode(jconfig)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300431 j_res["concurence"] = int(jconfig.get("numjobs", 1))
koder aka kdanilov2e928022015-04-08 13:47:15 +0300432 j_res["blocksize"] = jconfig["blocksize"]
koder aka kdanilovda45e882015-04-06 02:24:42 +0300433 j_res["jobname"] = job_output["jobname"]
koder aka kdanilov66839a92015-04-11 13:22:31 +0300434 j_res["timings"] = [int(jconfig.get("runtime", 0)),
435 int(jconfig.get("ramp_time", 0))]
koder aka kdanilovda45e882015-04-06 02:24:42 +0300436 else:
437 j_res = res[jname]
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300438 assert j_res["rw"] == jconfig["rw"]
439 assert j_res["rw"] == jconfig["rw"]
koder aka kdanilov652cd802015-04-13 12:21:07 +0300440 assert j_res["sync_mode"] == get_test_sync_mode(jconfig)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300441 assert j_res["concurence"] == int(jconfig.get("numjobs", 1))
koder aka kdanilov2e928022015-04-08 13:47:15 +0300442 assert j_res["blocksize"] == jconfig["blocksize"]
koder aka kdanilovda45e882015-04-06 02:24:42 +0300443 assert j_res["jobname"] == job_output["jobname"]
koder aka kdanilov652cd802015-04-13 12:21:07 +0300444
445 # ramp part is skipped for all tests, except first
446 # assert j_res["timings"] == (jconfig.get("runtime"),
447 # jconfig.get("ramp_time"))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300448
449 def j_app(name, x):
450 j_res.setdefault(name, []).append(x)
451
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300452 j_app("bw", raw_result["bw"])
koder aka kdanilovda45e882015-04-06 02:24:42 +0300453 j_app("iops", raw_result["iops"])
454 j_app("lat", raw_result["lat"]["mean"])
455 j_app("clat", raw_result["clat"]["mean"])
456 j_app("slat", raw_result["slat"]["mean"])
457
458 res[jname] = j_res
459
460
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300461def compile(benchmark_config, params, runcycle=None):
462 whole_conf = list(parse_fio_config_full(benchmark_config, params))
463 res = ""
464
465 for bconf in next_test_portion(whole_conf, runcycle):
466 res += format_fio_config(bconf)
467
468 return res
469
470
koder aka kdanilovda45e882015-04-06 02:24:42 +0300471def run_fio(benchmark_config,
472 params,
473 runcycle=None,
474 raw_results_func=None,
koder aka kdanilovb896f692015-04-07 14:57:55 +0300475 skip_tests=0,
476 fake_fio=False):
koder aka kdanilovda45e882015-04-06 02:24:42 +0300477
478 whole_conf = list(parse_fio_config_full(benchmark_config, params))
479 whole_conf = whole_conf[skip_tests:]
480 res = {}
481 curr_test_num = skip_tests
koder aka kdanilov2e928022015-04-08 13:47:15 +0300482 executed_tests = 0
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300483 ok = True
koder aka kdanilovda45e882015-04-06 02:24:42 +0300484 try:
485 for bconf in next_test_portion(whole_conf, runcycle):
koder aka kdanilovb896f692015-04-07 14:57:55 +0300486
487 if fake_fio:
488 res_cfg_it = do_run_fio_fake(bconf)
489 else:
490 res_cfg_it = do_run_fio(bconf)
491
koder aka kdanilovda45e882015-04-06 02:24:42 +0300492 res_cfg_it = enumerate(res_cfg_it, curr_test_num)
493
494 for curr_test_num, (job_output, (jname, jconfig)) in res_cfg_it:
koder aka kdanilov2e928022015-04-08 13:47:15 +0300495 executed_tests += 1
koder aka kdanilovda45e882015-04-06 02:24:42 +0300496 if raw_results_func is not None:
koder aka kdanilov2e928022015-04-08 13:47:15 +0300497 raw_results_func(executed_tests,
koder aka kdanilovda45e882015-04-06 02:24:42 +0300498 [job_output, jname, jconfig])
499
koder aka kdanilovb896f692015-04-07 14:57:55 +0300500 assert jname == job_output["jobname"], \
501 "{0} != {1}".format(jname, job_output["jobname"])
koder aka kdanilovda45e882015-04-06 02:24:42 +0300502
503 if jname.startswith('_'):
504 continue
505
506 add_job_results(jname, job_output, jconfig, res)
507
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300508 msg_template = "Done {0} tests from {1}. ETA: {2}"
509 exec_time = estimate_cfg(benchmark_config, params, curr_test_num)
510
511 print msg_template.format(curr_test_num - skip_tests,
512 len(whole_conf),
513 sec_to_str(exec_time))
514
koder aka kdanilovda45e882015-04-06 02:24:42 +0300515 except (SystemExit, KeyboardInterrupt):
koder aka kdanilov652cd802015-04-13 12:21:07 +0300516 raise
koder aka kdanilovda45e882015-04-06 02:24:42 +0300517
518 except Exception:
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300519 print "=========== ERROR ============="
koder aka kdanilovda45e882015-04-06 02:24:42 +0300520 traceback.print_exc()
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300521 print "======== END OF ERROR ========="
522 ok = False
koder aka kdanilovda45e882015-04-06 02:24:42 +0300523
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300524 return res, executed_tests, ok
koder aka kdanilovda45e882015-04-06 02:24:42 +0300525
526
527def run_benchmark(binary_tp, *argv, **kwargs):
528 if 'fio' == binary_tp:
529 return run_fio(*argv, **kwargs)
530 raise ValueError("Unknown behcnmark {0}".format(binary_tp))
531
532
koder aka kdanilov652cd802015-04-13 12:21:07 +0300533def read_config(fd, timeout=10):
534 job_cfg = ""
535 etime = time.time() + timeout
536 while True:
537 wtime = etime - time.time()
538 if wtime <= 0:
539 raise IOError("No config provided")
540
541 r, w, x = select.select([fd], [], [], wtime)
542 if len(r) == 0:
543 raise IOError("No config provided")
544
545 char = fd.read(1)
546 if '' == char:
547 return job_cfg
548
549 job_cfg += char
550
551
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300552def estimate_cfg(job_cfg, params, skip_tests=0):
553 bconf = list(parse_fio_config_full(job_cfg, params))[skip_tests:]
koder aka kdanilov652cd802015-04-13 12:21:07 +0300554 return calculate_execution_time(bconf)
555
556
557def sec_to_str(seconds):
558 h = seconds // 3600
559 m = (seconds % 3600) // 60
560 s = seconds % 60
561 return "{0}:{1:02d}:{2:02d}".format(h, m, s)
562
563
koder aka kdanilovda45e882015-04-06 02:24:42 +0300564def parse_args(argv):
565 parser = argparse.ArgumentParser(
566 description="Run fio' and return result")
567 parser.add_argument("--type", metavar="BINARY_TYPE",
568 choices=['fio'], default='fio',
569 help=argparse.SUPPRESS)
570 parser.add_argument("--start-at", metavar="START_AT_UTC", type=int,
571 help="Start execution at START_AT_UTC")
572 parser.add_argument("--json", action="store_true", default=False,
573 help="Json output format")
574 parser.add_argument("--output", default='-', metavar="FILE_PATH",
575 help="Store results to FILE_PATH")
576 parser.add_argument("--estimate", action="store_true", default=False,
577 help="Only estimate task execution time")
578 parser.add_argument("--compile", action="store_true", default=False,
579 help="Compile config file to fio config")
580 parser.add_argument("--num-tests", action="store_true", default=False,
581 help="Show total number of tests")
582 parser.add_argument("--runcycle", type=int, default=None,
583 metavar="MAX_CYCLE_SECONDS",
584 help="Max cycle length in seconds")
585 parser.add_argument("--show-raw-results", action='store_true',
586 default=False, help="Output raw input and results")
587 parser.add_argument("--skip-tests", type=int, default=0, metavar="NUM",
588 help="Skip NUM tests")
koder aka kdanilovb896f692015-04-07 14:57:55 +0300589 parser.add_argument("--faked-fio", action='store_true',
590 default=False, help="Emulate fio with 0 test time")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300591 parser.add_argument("--params", nargs="*", metavar="PARAM=VAL",
592 default=[],
593 help="Provide set of pairs PARAM=VAL to" +
594 "format into job description")
595 parser.add_argument("jobfile")
596 return parser.parse_args(argv)
597
598
koder aka kdanilovda45e882015-04-06 02:24:42 +0300599def main(argv):
600 argv_obj = parse_args(argv)
601
602 if argv_obj.jobfile == '-':
603 job_cfg = read_config(sys.stdin)
604 else:
605 job_cfg = open(argv_obj.jobfile).read()
606
607 if argv_obj.output == '-':
608 out_fd = sys.stdout
609 else:
610 out_fd = open(argv_obj.output, "w")
611
612 params = {}
613 for param_val in argv_obj.params:
614 assert '=' in param_val
615 name, val = param_val.split("=", 1)
616 params[name] = val
617
koder aka kdanilov652cd802015-04-13 12:21:07 +0300618 if argv_obj.estimate:
619 print sec_to_str(estimate_cfg(job_cfg, params))
620 return 0
621
622 if argv_obj.num_tests or argv_obj.compile:
koder aka kdanilovda45e882015-04-06 02:24:42 +0300623 bconf = list(parse_fio_config_full(job_cfg, params))
624 bconf = bconf[argv_obj.skip_tests:]
625
626 if argv_obj.compile:
627 out_fd.write(format_fio_config(bconf))
628 out_fd.write("\n")
629
630 if argv_obj.num_tests:
631 print len(bconf)
632
koder aka kdanilovda45e882015-04-06 02:24:42 +0300633 return 0
634
635 if argv_obj.start_at is not None:
636 ctime = time.time()
637 if argv_obj.start_at >= ctime:
638 time.sleep(ctime - argv_obj.start_at)
639
640 def raw_res_func(test_num, data):
641 pref = "========= RAW_RESULTS({0}) =========\n".format(test_num)
642 out_fd.write(pref)
643 out_fd.write(json.dumps(data))
644 out_fd.write("\n========= END OF RAW_RESULTS =========\n")
645 out_fd.flush()
646
647 rrfunc = raw_res_func if argv_obj.show_raw_results else None
648
649 stime = time.time()
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300650 job_res, num_tests, ok = run_benchmark(argv_obj.type,
651 job_cfg,
652 params,
653 argv_obj.runcycle,
654 rrfunc,
655 argv_obj.skip_tests,
656 argv_obj.faked_fio)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300657 etime = time.time()
658
koder aka kdanilov652cd802015-04-13 12:21:07 +0300659 res = {'__meta__': {'raw_cfg': job_cfg, 'params': params}, 'res': job_res}
koder aka kdanilovda45e882015-04-06 02:24:42 +0300660
661 oformat = 'json' if argv_obj.json else 'eval'
koder aka kdanilov652cd802015-04-13 12:21:07 +0300662 out_fd.write("\nRun {0} tests in {1} seconds\n".format(num_tests,
663 int(etime - stime)))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300664 out_fd.write("========= RESULTS(format={0}) =========\n".format(oformat))
665 if argv_obj.json:
666 out_fd.write(json.dumps(res))
667 else:
668 out_fd.write(pprint.pformat(res) + "\n")
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300669 out_fd.write("\n========= END OF RESULTS =========\n")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300670
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300671 return 0 if ok else 1
672
673
674def fake_main(x):
675 import yaml
676 time.sleep(60)
677 out_fd = sys.stdout
678 fname = "/tmp/perf_tests/metempirical_alisha/raw_results.yaml"
679 res = yaml.load(open(fname).read())[0][1]
680 out_fd.write("========= RESULTS(format=json) =========\n")
681 out_fd.write(json.dumps(res))
682 out_fd.write("\n========= END OF RESULTS =========\n")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300683 return 0
684
685
686if __name__ == '__main__':
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300687 # exit(fake_main(sys.argv[1:]))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300688 exit(main(sys.argv[1:]))