blob: 3c3e4365bb14fe5be9329486786d0a9b253d7ec7 [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:
koder aka kdanilov416b87a2015-05-12 00:26:04 +030062 yield lineno, SETTING, line, '1'
koder aka kdanilov0c598a12015-04-21 03:01:40 +030063 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):
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300110 try:
111 return int(val)
112 except ValueError:
113 pass
114
115 try:
116 return float(val)
117 except ValueError:
118 pass
119
120 if val.startswith('{%'):
121 assert val.endswith("%}")
122 content = val[2:-2]
123 vals = list(i.strip() for i in content.split(','))
124 return map(parse_value, vals)
125 return val
126
127
128def process_repeats(sec_iter):
129
130 for sec in sec_iter:
131 if '*' in sec.name:
132 msg = "Only one '*' allowed in section name"
133 assert sec.name.count('*') == 1, msg
134
135 name, count = sec.name.split("*")
136 sec.name = name.strip()
137 count = count.strip()
138
139 try:
140 count = int(count.strip().format(**sec.format_params))
141 except KeyError:
142 raise ValueError("No parameter {0} given".format(count[1:-1]))
143 except ValueError:
144 msg = "Parameter {0} nas non-int value {1!r}"
145 raise ValueError(msg.format(count[1:-1],
146 count.format(**sec.format_params)))
147
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300148 yield sec.copy()
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300149
150 if 'ramp_time' in sec.vals:
151 sec = sec.copy()
152 sec.vals['_ramp_time'] = sec.vals.pop('ramp_time')
153
154 for _ in range(count - 1):
155 yield sec.copy()
156 else:
157 yield sec
158
159
160def process_cycles(sec_iter):
161 # insert parametrized cycles
162 sec_iter = try_format_params_into_section(sec_iter)
163
164 for sec in sec_iter:
165
166 cycles_var_names = []
167 cycles_var_values = []
168
169 for name, val in sec.vals.items():
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300170 if isinstance(val, (list, tuple)):
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300171 cycles_var_names.append(name)
172 cycles_var_values.append(val)
173
174 if len(cycles_var_names) == 0:
175 yield sec
176 else:
177 for combination in itertools.product(*cycles_var_values):
178 new_sec = sec.copy()
179 new_sec.vals.update(zip(cycles_var_names, combination))
180 yield new_sec
181
182
183def try_format_params_into_section(sec_iter):
184 for sec in sec_iter:
185 params = sec.format_params
186 for name, val in sec.vals.items():
187 if isinstance(val, basestring):
188 try:
189 sec.vals[name] = parse_value(val.format(**params))
190 except:
191 pass
192
193 yield sec
194
195
196def format_params_into_section_finall(sec_iter, counter=[0]):
197 group_report_err_msg = "Group reporting should be set if numjobs != 1"
198
199 for sec in sec_iter:
200
201 num_jobs = int(sec.vals.get('numjobs', '1'))
202 if num_jobs != 1:
203 assert 'group_reporting' in sec.vals, group_report_err_msg
204
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300205 assert sec.vals.get('unified_rw_reporting', '1') in (1, '1')
206 sec.vals['unified_rw_reporting'] = '1'
207
208 params = sec.format_params.copy()
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300209
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:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300217 assert isinstance(val, (int, float))
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300218
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
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300222 params['TEST_SUMM'] = get_test_summary(sec.vals,
223 params.get('VM_COUNT', 1))
224 params.update(sec.vals)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300225 sec.name = sec.name.format(**params)
226
227 yield sec
228
229
230def fio_config_to_str(sec_iter):
231 res = ""
232
233 for pos, sec in enumerate(sec_iter):
234 if pos != 0:
235 res += "\n"
236
237 res += "[{0}]\n".format(sec.name)
238
239 for name, val in sec.vals.items():
240 if name.startswith('_'):
241 continue
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300242 res += "{0}={1}\n".format(name, val)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300243
244 return res
245
246
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300247def get_test_sync_mode(config):
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300248 try:
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300249 return config['sync_mode']
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300250 except KeyError:
251 pass
252
koder aka kdanilovea22c3d2015-04-21 03:42:22 +0300253 is_sync = str(config.get("sync", "0")) == "1"
254 is_direct = str(config.get("direct", "0")) == "1"
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300255
256 if is_sync and is_direct:
257 return 'x'
258 elif is_sync:
259 return 's'
260 elif is_direct:
261 return 'd'
262 else:
263 return 'a'
264
265
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300266def get_test_summary(params, testnodes_count):
koder aka kdanilovda45e882015-04-06 02:24:42 +0300267 rw = {"randread": "rr",
268 "randwrite": "rw",
269 "read": "sr",
270 "write": "sw"}[params["rw"]]
271
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300272 sync_mode = get_test_sync_mode(params)
273 th_count = params.get('numjobs')
koder aka kdanilovea22c3d2015-04-21 03:42:22 +0300274
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300275 if th_count is None:
koder aka kdanilovea22c3d2015-04-21 03:42:22 +0300276 th_count = params.get('concurence', 1)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300277
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300278 return "{0}{1}{2}th{3}vm{4}".format(rw,
279 sync_mode,
280 params['blocksize'],
281 th_count,
282 testnodes_count)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300283
284
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300285def calculate_execution_time(sec_iter):
koder aka kdanilovda45e882015-04-06 02:24:42 +0300286 time = 0
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300287 for sec in sec_iter:
288 time += sec.vals.get('ramp_time', 0)
289 time += sec.vals.get('runtime', 0)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300290 return time
291
292
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300293def slice_config(sec_iter, runcycle=None, max_jobs=1000,
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300294 soft_runcycle=None, split_on_names=False):
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300295 jcount = 0
296 runtime = 0
297 curr_slice = []
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300298 prev_name = None
koder aka kdanilovda45e882015-04-06 02:24:42 +0300299
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300300 for pos, sec in enumerate(sec_iter):
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300301
302 if prev_name is not None:
303 split_here = False
304
305 if soft_runcycle is not None and prev_name != sec.name:
306 split_here = (runtime > soft_runcycle)
307
308 if split_on_names and prev_name != sec.name:
309 split_here = True
310
311 if split_here:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300312 yield curr_slice
313 curr_slice = []
314 runtime = 0
315 jcount = 0
316
317 prev_name = sec.name
koder aka kdanilovda45e882015-04-06 02:24:42 +0300318
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300319 jc = sec.vals.get('numjobs', 1)
320 msg = "numjobs should be integer, not {0!r}".format(jc)
321 assert isinstance(jc, int), msg
koder aka kdanilovda45e882015-04-06 02:24:42 +0300322
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300323 curr_task_time = calculate_execution_time([sec])
koder aka kdanilovda45e882015-04-06 02:24:42 +0300324
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300325 if jc > max_jobs:
326 err_templ = "Can't process job {0!r} - too large numjobs"
327 raise ValueError(err_templ.format(sec.name))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300328
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300329 if runcycle is not None and len(curr_slice) != 0:
330 rc_ok = curr_task_time + runtime <= runcycle
koder aka kdanilovda45e882015-04-06 02:24:42 +0300331 else:
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300332 rc_ok = True
koder aka kdanilovda45e882015-04-06 02:24:42 +0300333
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300334 if jc + jcount <= max_jobs and rc_ok:
335 runtime += curr_task_time
336 jcount += jc
337 curr_slice.append(sec)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300338 continue
339
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300340 assert len(curr_slice) != 0
341 yield curr_slice
koder aka kdanilovda45e882015-04-06 02:24:42 +0300342
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300343 if '_ramp_time' in sec.vals:
344 sec.vals['ramp_time'] = sec.vals.pop('_ramp_time')
345 curr_task_time = calculate_execution_time([sec])
346
347 runtime = curr_task_time
348 jcount = jc
349 curr_slice = [sec]
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300350 prev_name = None
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300351
352 if curr_slice != []:
353 yield curr_slice
koder aka kdanilovda45e882015-04-06 02:24:42 +0300354
355
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300356def parse_all_in_1(source, test_params):
357 lexer_it = fio_config_lexer(source)
358 sec_it = fio_config_parse(lexer_it, test_params)
359 sec_it = process_cycles(sec_it)
360 sec_it = process_repeats(sec_it)
361 return format_params_into_section_finall(sec_it)
koder aka kdanilovb896f692015-04-07 14:57:55 +0300362
363
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300364def parse_and_slice_all_in_1(source, test_params, **slice_params):
365 sec_it = parse_all_in_1(source, test_params)
366 return slice_config(sec_it, **slice_params)
koder aka kdanilovb896f692015-04-07 14:57:55 +0300367
368
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300369def compile_all_in_1(source, test_params, **slice_params):
370 slices_it = parse_and_slice_all_in_1(source, test_params, **slice_params)
371 for slices in slices_it:
372 yield fio_config_to_str(slices)
koder aka kdanilovb896f692015-04-07 14:57:55 +0300373
374
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300375def do_run_fio(config_slice):
376 benchmark_config = fio_config_to_str(config_slice)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300377 cmd = ["fio", "--output-format=json", "--alloc-size=262144", "-"]
koder aka kdanilove87ae652015-04-20 02:14:35 +0300378 p = subprocess.Popen(cmd,
379 stdin=subprocess.PIPE,
koder aka kdanilovda45e882015-04-06 02:24:42 +0300380 stdout=subprocess.PIPE,
koder aka kdanilove87ae652015-04-20 02:14:35 +0300381 stderr=subprocess.PIPE)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300382
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300383 start_time = time.time()
koder aka kdanilovda45e882015-04-06 02:24:42 +0300384 # set timeout
koder aka kdanilove87ae652015-04-20 02:14:35 +0300385 raw_out, raw_err = p.communicate(benchmark_config)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300386 end_time = time.time()
koder aka kdanilove87ae652015-04-20 02:14:35 +0300387
388 if 0 != p.returncode:
389 msg = "Fio failed with code: {0}\nOutput={1}"
390 raise OSError(msg.format(p.returncode, raw_err))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300391
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300392 # HACK
393 raw_out = "{" + raw_out.split('{', 1)[1]
394
koder aka kdanilovda45e882015-04-06 02:24:42 +0300395 try:
396 parsed_out = json.loads(raw_out)["jobs"]
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300397 except KeyError:
398 msg = "Can't parse fio output {0!r}: no 'jobs' found"
399 raw_out = raw_out[:100]
400 raise ValueError(msg.format(raw_out))
401
402 except Exception as exc:
koder aka kdanilovec1b9732015-04-23 20:43:29 +0300403 msg = "Can't parse fio output: {0!r}\nError: {1!s}"
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300404 raw_out = raw_out[:100]
koder aka kdanilovec1b9732015-04-23 20:43:29 +0300405 raise ValueError(msg.format(raw_out, exc))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300406
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300407 return zip(parsed_out, config_slice), (start_time, end_time)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300408
409
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300410class FioResult(object):
411 def __init__(self, name, params, run_interval, results):
412 self.params = params.copy()
413 self.name = name
414 self.run_interval = run_interval
415 self.results = results
koder aka kdanilovda45e882015-04-06 02:24:42 +0300416
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300417 def json_obj(self):
418 return self.__dict__
koder aka kdanilov652cd802015-04-13 12:21:07 +0300419
koder aka kdanilovda45e882015-04-06 02:24:42 +0300420
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300421def make_job_results(section, job_output, slice_timings):
422 # merge by section.merge_id
koder aka kdanilovda45e882015-04-06 02:24:42 +0300423
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300424 raw_result = job_output['mixed']
koder aka kdanilovda45e882015-04-06 02:24:42 +0300425
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300426 res = {
427 "bw": raw_result["bw"],
428 "iops": raw_result["iops"],
429 "lat": raw_result["lat"]["mean"],
430 "clat": raw_result["clat"]["mean"],
431 "slat": raw_result["slat"]["mean"]
432 }
433
434 vls = section.vals.copy()
435
436 vls['sync_mode'] = get_test_sync_mode(vls)
437 vls['concurence'] = vls.get('numjobs', 1)
438
439 return FioResult(section.name, vls, slice_timings, res)
440
441
442def get_slice_parts_offset(test_slice, real_inteval):
443 calc_exec_time = calculate_execution_time(test_slice)
444 coef = (real_inteval[1] - real_inteval[0]) / calc_exec_time
445 curr_offset = real_inteval[0]
446 for section in test_slice:
447 slen = calculate_execution_time([section]) * coef
448 yield (curr_offset, curr_offset + slen)
449 curr_offset += slen
koder aka kdanilovda45e882015-04-06 02:24:42 +0300450
451
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300452def run_fio(sliced_it, raw_results_func=None):
453 sliced_list = list(sliced_it)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300454
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300455 curr_test_num = 0
456 executed_tests = 0
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300457 result = []
koder aka kdanilovb896f692015-04-07 14:57:55 +0300458
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300459 for i, test_slice in enumerate(sliced_list):
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300460 test_slice = list(test_slice)
461
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300462 res_cfg_it, slice_timings = do_run_fio(test_slice)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300463 sec_intervals = get_slice_parts_offset(test_slice,
464 slice_timings)
465 res_cfg_it = enumerate(zip(res_cfg_it, sec_intervals),
466 curr_test_num)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300467
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300468 section_names = []
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300469 for curr_test_num, ((job_output, section), interval) in res_cfg_it:
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300470 executed_tests += 1
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300471 section_names.append(section.name)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300472
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300473 if raw_results_func is not None:
474 raw_results_func(executed_tests,
475 [job_output, section])
koder aka kdanilovda45e882015-04-06 02:24:42 +0300476
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300477 msg = "{0} != {1}".format(section.name, job_output["jobname"])
478 assert section.name == job_output["jobname"], msg
koder aka kdanilovda45e882015-04-06 02:24:42 +0300479
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300480 result.append(make_job_results(section, job_output, interval))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300481
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300482 curr_test_num += 1
483 msg_template = "Done {0} tests from {1}. ETA: {2}"
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300484
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300485 rest = sliced_list[i:]
486 time_eta = sum(map(calculate_execution_time, rest))
487 test_left = sum(map(len, rest))
488 print msg_template.format(curr_test_num,
489 test_left,
490 sec_to_str(time_eta))
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300491
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300492 return result
koder aka kdanilovda45e882015-04-06 02:24:42 +0300493
494
495def run_benchmark(binary_tp, *argv, **kwargs):
496 if 'fio' == binary_tp:
497 return run_fio(*argv, **kwargs)
498 raise ValueError("Unknown behcnmark {0}".format(binary_tp))
499
500
koder aka kdanilov652cd802015-04-13 12:21:07 +0300501def read_config(fd, timeout=10):
502 job_cfg = ""
503 etime = time.time() + timeout
504 while True:
505 wtime = etime - time.time()
506 if wtime <= 0:
507 raise IOError("No config provided")
508
509 r, w, x = select.select([fd], [], [], wtime)
510 if len(r) == 0:
511 raise IOError("No config provided")
512
513 char = fd.read(1)
514 if '' == char:
515 return job_cfg
516
517 job_cfg += char
518
519
koder aka kdanilov652cd802015-04-13 12:21:07 +0300520def sec_to_str(seconds):
521 h = seconds // 3600
522 m = (seconds % 3600) // 60
523 s = seconds % 60
524 return "{0}:{1:02d}:{2:02d}".format(h, m, s)
525
526
koder aka kdanilovda45e882015-04-06 02:24:42 +0300527def parse_args(argv):
528 parser = argparse.ArgumentParser(
529 description="Run fio' and return result")
530 parser.add_argument("--type", metavar="BINARY_TYPE",
531 choices=['fio'], default='fio',
532 help=argparse.SUPPRESS)
533 parser.add_argument("--start-at", metavar="START_AT_UTC", type=int,
534 help="Start execution at START_AT_UTC")
535 parser.add_argument("--json", action="store_true", default=False,
536 help="Json output format")
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300537 parser.add_argument("-o", "--output", default='-', metavar="FILE_PATH",
koder aka kdanilovda45e882015-04-06 02:24:42 +0300538 help="Store results to FILE_PATH")
539 parser.add_argument("--estimate", action="store_true", default=False,
540 help="Only estimate task execution time")
541 parser.add_argument("--compile", action="store_true", default=False,
542 help="Compile config file to fio config")
543 parser.add_argument("--num-tests", action="store_true", default=False,
544 help="Show total number of tests")
545 parser.add_argument("--runcycle", type=int, default=None,
546 metavar="MAX_CYCLE_SECONDS",
547 help="Max cycle length in seconds")
548 parser.add_argument("--show-raw-results", action='store_true',
549 default=False, help="Output raw input and results")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300550 parser.add_argument("--params", nargs="*", metavar="PARAM=VAL",
551 default=[],
552 help="Provide set of pairs PARAM=VAL to" +
553 "format into job description")
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300554 parser.add_argument("-p", "--pid-file", metavar="FILE_TO_STORE_PID",
555 default=None, help="Store pid to FILE_TO_STORE_PID " +
556 "and remove this file on exit")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300557 parser.add_argument("jobfile")
558 return parser.parse_args(argv)
559
560
koder aka kdanilovda45e882015-04-06 02:24:42 +0300561def main(argv):
562 argv_obj = parse_args(argv)
563
564 if argv_obj.jobfile == '-':
565 job_cfg = read_config(sys.stdin)
566 else:
567 job_cfg = open(argv_obj.jobfile).read()
568
569 if argv_obj.output == '-':
570 out_fd = sys.stdout
571 else:
572 out_fd = open(argv_obj.output, "w")
573
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300574 if argv_obj.pid_file is not None:
575 with open(argv_obj.pid_file, "w") as fd:
576 fd.write(str(os.getpid()))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300577
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300578 try:
579 params = {}
580 for param_val in argv_obj.params:
581 assert '=' in param_val
582 name, val = param_val.split("=", 1)
583 params[name] = val
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300584
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300585 slice_params = {
586 'runcycle': argv_obj.runcycle,
587 }
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300588
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300589 sliced_it = parse_and_slice_all_in_1(job_cfg, params, **slice_params)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300590
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300591 if argv_obj.estimate:
592 it = map(calculate_execution_time, sliced_it)
593 print sec_to_str(sum(it))
594 return 0
koder aka kdanilovda45e882015-04-06 02:24:42 +0300595
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300596 if argv_obj.num_tests or argv_obj.compile:
597 if argv_obj.compile:
598 for test_slice in sliced_it:
599 out_fd.write(fio_config_to_str(test_slice))
600 out_fd.write("\n#" + "-" * 70 + "\n\n")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300601
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300602 if argv_obj.num_tests:
603 print len(list(sliced_it))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300604
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300605 return 0
koder aka kdanilovda45e882015-04-06 02:24:42 +0300606
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300607 if argv_obj.start_at is not None:
608 ctime = time.time()
609 if argv_obj.start_at >= ctime:
610 time.sleep(ctime - argv_obj.start_at)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300611
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300612 def raw_res_func(test_num, data):
613 pref = "========= RAW_RESULTS({0}) =========\n".format(test_num)
614 out_fd.write(pref)
615 out_fd.write(json.dumps(data))
616 out_fd.write("\n========= END OF RAW_RESULTS =========\n")
617 out_fd.flush()
koder aka kdanilovda45e882015-04-06 02:24:42 +0300618
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300619 rrfunc = raw_res_func if argv_obj.show_raw_results else None
koder aka kdanilovda45e882015-04-06 02:24:42 +0300620
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300621 job_res = run_benchmark(argv_obj.type,
622 sliced_it, rrfunc)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300623
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300624 res = {'__meta__': {'params': params,
625 'testnodes_count': int(params.get('VM_COUNT', 1))},
626 'res': [j.json_obj() for j in job_res]}
koder aka kdanilovda45e882015-04-06 02:24:42 +0300627
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300628 oformat = 'json' if argv_obj.json else 'eval'
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300629
630 msg = "========= RESULTS(format={0}) =========\n"
631 out_fd.write(msg.format(oformat))
632 if argv_obj.json:
633 out_fd.write(json.dumps(res))
634 else:
635 out_fd.write(pprint.pformat(res) + "\n")
636 out_fd.write("\n========= END OF RESULTS =========\n")
637
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300638 return 0
639 except:
640 out_fd.write("============ ERROR =============\n")
641 out_fd.write(traceback.format_exc() + "\n")
642 out_fd.write("============ END OF ERROR =============\n")
643 return 1
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300644 finally:
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300645 try:
646 if out_fd is not sys.stdout:
647 out_fd.flush()
648 os.fsync(out_fd)
649 out_fd.close()
650 except Exception:
651 traceback.print_exc()
652
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300653 if argv_obj.pid_file is not None:
654 if os.path.exists(argv_obj.pid_file):
655 os.unlink(argv_obj.pid_file)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300656
657
658def fake_main(x):
659 import yaml
660 time.sleep(60)
661 out_fd = sys.stdout
662 fname = "/tmp/perf_tests/metempirical_alisha/raw_results.yaml"
663 res = yaml.load(open(fname).read())[0][1]
664 out_fd.write("========= RESULTS(format=json) =========\n")
665 out_fd.write(json.dumps(res))
666 out_fd.write("\n========= END OF RESULTS =========\n")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300667 return 0
668
669
670if __name__ == '__main__':
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300671 # exit(fake_main(sys.argv[1:]))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300672 exit(main(sys.argv[1:]))