blob: b9f3b6faa3bb97c19d42cf3413a5abab68f1cb5f [file] [log] [blame]
koder aka kdanilov4af80852015-02-01 23:36:38 +02001import re
2import os
3import sys
4import stat
5import time
6import json
koder aka kdanilove21d7472015-02-14 19:02:04 -08007import Queue
koder aka kdanilov4af80852015-02-01 23:36:38 +02008import os.path
9import argparse
10import warnings
koder aka kdanilove21d7472015-02-14 19:02:04 -080011import threading
koder aka kdanilov4af80852015-02-01 23:36:38 +020012import subprocess
13
14
15class BenchmarkOption(object):
16 def __init__(self, concurence, iodepth, action, blocksize, size):
17 self.iodepth = iodepth
18 self.action = action
19 self.blocksize = blocksize
20 self.concurence = concurence
21 self.size = size
22 self.direct_io = False
23 self.use_hight_io_priority = True
24 self.sync = False
25
26
koder aka kdanilov98615bf2015-02-02 00:59:07 +020027def which(program):
28 def is_exe(fpath):
29 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
30
31 fpath, fname = os.path.split(program)
32 if fpath:
33 if is_exe(program):
34 return program
35 else:
36 for path in os.environ["PATH"].split(os.pathsep):
37 path = path.strip('"')
38 exe_file = os.path.join(path, program)
39 if is_exe(exe_file):
40 return exe_file
41
42 return None
43
44
45# ------------------------------ IOZONE SUPPORT ------------------------------
46
47
koder aka kdanilov4af80852015-02-01 23:36:38 +020048class IOZoneParser(object):
49 "class to parse iozone results"
50
51 start_tests = re.compile(r"^\s+KB\s+reclen\s+")
52 resuts = re.compile(r"[\s0-9]+")
53 mt_iozone_re = re.compile(r"\s+Children see throughput " +
54 r"for\s+\d+\s+(?P<cmd>.*?)\s+=\s+" +
55 r"(?P<perf>[\d.]+)\s+KB/sec")
56
57 cmap = {'initial writers': 'write',
58 'rewriters': 'rewrite',
59 'initial readers': 'read',
60 're-readers': 'reread',
61 'random readers': 'random read',
62 'random writers': 'random write'}
63
64 string1 = " " + \
65 " random random " + \
66 "bkwd record stride "
67
68 string2 = "KB reclen write rewrite " + \
69 "read reread read write " + \
70 "read rewrite read fwrite frewrite fread freread"
71
72 @classmethod
73 def apply_parts(cls, parts, string, sep=' \t\n'):
74 add_offset = 0
75 for part in parts:
76 _, start, stop = part
77 start += add_offset
78 add_offset = 0
79
80 # condition splited to make pylint happy
81 while stop + add_offset < len(string):
82
83 # condition splited to make pylint happy
84 if not (string[stop + add_offset] not in sep):
85 break
86
87 add_offset += 1
88
89 yield part, string[start:stop + add_offset]
90
91 @classmethod
92 def make_positions(cls):
93 items = [i for i in cls.string2.split() if i]
94
95 pos = 0
96 cls.positions = []
97
98 for item in items:
99 npos = cls.string2.index(item, 0 if pos == 0 else pos + 1)
100 cls.positions.append([item, pos, npos + len(item)])
101 pos = npos + len(item)
102
103 for itm, val in cls.apply_parts(cls.positions, cls.string1):
104 if val.strip():
105 itm[0] = val.strip() + " " + itm[0]
106
107 @classmethod
108 def parse_iozone_res(cls, res, mthreads=False):
109 parsed_res = None
110
111 sres = res.split('\n')
112
113 if not mthreads:
114 for pos, line in enumerate(sres[1:]):
115 if line.strip() == cls.string2 and \
116 sres[pos].strip() == cls.string1.strip():
117 add_pos = line.index(cls.string2)
118 parsed_res = {}
119
120 npos = [(name, start + add_pos, stop + add_pos)
121 for name, start, stop in cls.positions]
122
123 for itm, res in cls.apply_parts(npos, sres[pos + 2]):
124 if res.strip() != '':
125 parsed_res[itm[0]] = int(res.strip())
126
127 del parsed_res['KB']
128 del parsed_res['reclen']
129 else:
130 parsed_res = {}
131 for line in sres:
132 rr = cls.mt_iozone_re.match(line)
133 if rr is not None:
134 cmd = rr.group('cmd')
135 key = cls.cmap.get(cmd, cmd)
136 perf = int(float(rr.group('perf')))
137 parsed_res[key] = perf
138 return parsed_res
139
140
koder aka kdanilov4af80852015-02-01 23:36:38 +0200141IOZoneParser.make_positions()
142
143
koder aka kdanilov3f356262015-02-13 08:06:14 -0800144def iozone_do_prepare(params, filename, pattern_base):
koder aka kdanilov4af80852015-02-01 23:36:38 +0200145 all_files = []
146 threads = int(params.concurence)
147 if 1 != threads:
koder aka kdanilov3f356262015-02-13 08:06:14 -0800148 filename = filename + "_{0}"
149 all_files.extend(filename .format(i) for i in range(threads))
koder aka kdanilov4af80852015-02-01 23:36:38 +0200150 else:
koder aka kdanilov4af80852015-02-01 23:36:38 +0200151 all_files.append(filename)
152
153 bsz = 1024 if params.size > 1024 else params.size
154 if params.size % bsz != 0:
155 fsz = (params.size // bsz + 1) * bsz
156 else:
157 fsz = params.size
158
159 for fname in all_files:
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200160 with open(fname, "wb") as fd:
161 if fsz > 1024:
koder aka kdanilov3f356262015-02-13 08:06:14 -0800162 pattern = pattern_base * 1024 * 1024
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200163 for _ in range(int(fsz / 1024) + 1):
164 fd.write(pattern)
165 else:
koder aka kdanilov3f356262015-02-13 08:06:14 -0800166 fd.write(pattern_base * 1024 * fsz)
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200167 fd.flush()
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800168 return all_files
169
170
171VERIFY_PATTERN = "\x6d"
172
173
174def do_run_iozone(params, filename, timeout, iozone_path='iozone',
175 microsecond_mode=False,
176 prepare_only=False):
177
178 cmd = [iozone_path, "-V", str(ord(VERIFY_PATTERN))]
179
180 if params.sync:
181 cmd.append('-o')
182
183 if params.direct_io:
184 cmd.append('-I')
185
186 if microsecond_mode:
187 cmd.append('-N')
188
189 all_files = iozone_do_prepare(params, filename, VERIFY_PATTERN)
190
191 threads = int(params.concurence)
192 if 1 != threads:
193 cmd.extend(('-t', str(threads), '-F'))
194 cmd.extend(all_files)
195 else:
196 cmd.extend(('-f', all_files[0]))
koder aka kdanilov4af80852015-02-01 23:36:38 +0200197
198 cmd.append('-i')
199
200 if params.action == 'write':
201 cmd.append("0")
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200202 elif params.action == 'read':
203 cmd.append("1")
204 elif params.action == 'randwrite' or params.action == 'randread':
koder aka kdanilov4af80852015-02-01 23:36:38 +0200205 cmd.append("2")
206 else:
207 raise ValueError("Unknown action {0!r}".format(params.action))
208
209 cmd.extend(('-s', str(params.size)))
210 cmd.extend(('-r', str(params.blocksize)))
211
koder aka kdanilov78ba8952015-02-03 01:11:23 +0200212 # no retest
213 cmd.append('-+n')
214
koder aka kdanilov4af80852015-02-01 23:36:38 +0200215 raw_res = subprocess.check_output(cmd)
216
217 try:
218 parsed_res = IOZoneParser.parse_iozone_res(raw_res, threads > 1)
219
220 res = {}
221
222 if params.action == 'write':
223 res['bw_mean'] = parsed_res['write']
224 elif params.action == 'randwrite':
225 res['bw_mean'] = parsed_res['random write']
226 elif params.action == 'read':
227 res['bw_mean'] = parsed_res['read']
228 elif params.action == 'randread':
229 res['bw_mean'] = parsed_res['random read']
230 except:
koder aka kdanilov4af80852015-02-01 23:36:38 +0200231 raise
232
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200233 return res, " ".join(cmd)
koder aka kdanilov4af80852015-02-01 23:36:38 +0200234
235
koder aka kdanilov3f356262015-02-13 08:06:14 -0800236def run_iozone(benchmark, iozone_path, tmpname,
237 prepare_only=False,
238 timeout=None):
239
koder aka kdanilov4af80852015-02-01 23:36:38 +0200240 if timeout is not None:
241 benchmark.size = benchmark.blocksize * 50
242 res_time = do_run_iozone(benchmark, tmpname, timeout,
243 iozone_path=iozone_path,
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200244 microsecond_mode=True)[0]
koder aka kdanilov4af80852015-02-01 23:36:38 +0200245
246 size = (benchmark.blocksize * timeout * 1000000)
247 size /= res_time["bw_mean"]
248 size = (size // benchmark.blocksize + 1) * benchmark.blocksize
249 benchmark.size = size
250
251 return do_run_iozone(benchmark, tmpname, timeout,
koder aka kdanilov3f356262015-02-13 08:06:14 -0800252 iozone_path=iozone_path, prepare_only=prepare_only)
koder aka kdanilov4af80852015-02-01 23:36:38 +0200253
254
koder aka kdanilov4af80852015-02-01 23:36:38 +0200255def install_iozone_package():
256 if which('iozone'):
257 return
258
259 is_redhat = os.path.exists('/etc/centos-release')
260 is_redhat = is_redhat or os.path.exists('/etc/fedora-release')
261 is_redhat = is_redhat or os.path.exists('/etc/redhat-release')
262
263 if is_redhat:
264 subprocess.check_output(["yum", "install", 'iozone3'])
265 return
266
267 try:
268 os_release_cont = open('/etc/os-release').read()
269
270 is_ubuntu = "Ubuntu" in os_release_cont
271
272 if is_ubuntu or "Debian GNU/Linux" in os_release_cont:
273 subprocess.check_output(["apt-get", "install", "iozone3"])
274 return
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200275 except (IOError, OSError):
koder aka kdanilov4af80852015-02-01 23:36:38 +0200276 pass
277
278 raise RuntimeError("Unknown host OS.")
279
280
281def install_iozone_static(iozone_url, dst):
282 if not os.path.isfile(dst):
283 import urllib
284 urllib.urlretrieve(iozone_url, dst)
285
286 st = os.stat(dst)
287 os.chmod(dst, st.st_mode | stat.S_IEXEC)
288
289
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200290def locate_iozone():
291 binary_path = which('iozone')
292
293 if binary_path is None:
294 binary_path = which('iozone3')
295
296 if binary_path is None:
297 sys.stderr.write("Can't found neither iozone not iozone3 binary"
Kostiantyn Danylov aka koder02adc1d2015-02-02 01:10:04 +0200298 "Provide --bonary-path or --binary-url option")
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200299 return False, None
300
301 return False, binary_path
302
303# ------------------------------ FIO SUPPORT ---------------------------------
304
305
306def run_fio_once(benchmark, fio_path, tmpname, timeout=None):
koder aka kdanilov3f356262015-02-13 08:06:14 -0800307 if benchmark.size is None:
308 raise ValueError("You need to specify file size for fio")
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200309
310 cmd_line = [fio_path,
311 "--name=%s" % benchmark.action,
312 "--rw=%s" % benchmark.action,
313 "--blocksize=%sk" % benchmark.blocksize,
314 "--iodepth=%d" % benchmark.iodepth,
315 "--filename=%s" % tmpname,
316 "--size={0}k".format(benchmark.size),
317 "--numjobs={0}".format(benchmark.concurence),
318 "--output-format=json",
319 "--sync=" + ('1' if benchmark.sync else '0')]
320
321 if timeout is not None:
322 cmd_line.append("--timeout=%d" % timeout)
323 cmd_line.append("--runtime=%d" % timeout)
324
325 if benchmark.direct_io:
326 cmd_line.append("--direct=1")
327
328 if benchmark.use_hight_io_priority:
329 cmd_line.append("--prio=0")
330
331 raw_out = subprocess.check_output(cmd_line)
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200332 return json.loads(raw_out)["jobs"][0], " ".join(cmd_line)
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200333
334
koder aka kdanilov3f356262015-02-13 08:06:14 -0800335def run_fio(benchmark, fio_path, tmpname, prepare_only=False, timeout=None):
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800336 if prepare_only:
337 return {}, ""
338
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200339 job_output, cmd_line = run_fio_once(benchmark, fio_path, tmpname, timeout)
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200340
341 if benchmark.action in ('write', 'randwrite'):
342 raw_result = job_output['write']
343 else:
344 raw_result = job_output['read']
345
346 res = {}
koder aka kdanilov78ba8952015-02-03 01:11:23 +0200347
348 # 'bw_dev bw_mean bw_max bw_min'.split()
349 for field in ["bw_mean"]:
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200350 res[field] = raw_result[field]
351
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200352 return res, cmd_line
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200353
354
355def locate_fio():
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200356 return False, which('fio')
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200357
358
359# ----------------------------------------------------------------------------
360
361
362def locate_binary(binary_tp, binary_url, binary_path):
363 remove_binary = False
364
365 if binary_url is not None:
366 if binary_path is not None:
367 sys.stderr.write("At most one option from --binary-path and "
368 "--binary-url should be provided")
369 return False, None
370
371 binary_path = os.tmpnam()
372 install_iozone_static(binary_url, binary_path)
373 remove_binary = True
374
375 elif binary_path is not None:
376 if os.path.isfile(binary_path):
377 if not os.access(binary_path, os.X_OK):
378 st = os.stat(binary_path)
379 os.chmod(binary_path, st.st_mode | stat.S_IEXEC)
380 else:
381 binary_path = None
382
383 if binary_path is not None:
384 return remove_binary, binary_path
385
386 if 'iozone' == binary_tp:
387 return locate_iozone()
388 else:
389 return locate_fio()
390
391
392def run_benchmark(binary_tp, *argv, **kwargs):
393 if 'iozone' == binary_tp:
394 return run_iozone(*argv, **kwargs)
395 else:
396 return run_fio(*argv, **kwargs)
397
398
koder aka kdanilov4af80852015-02-01 23:36:38 +0200399def type_size(string):
400 try:
401 return re.match("\d+[KGBM]?", string, re.I).group(0)
402 except:
403 msg = "{0!r} don't looks like size-description string".format(string)
404 raise ValueError(msg)
405
406
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200407def type_size_ext(string):
408 if string.startswith("x"):
409 int(string[1:])
410 return string
411
412 if string.startswith("r"):
413 int(string[1:])
414 return string
415
416 try:
417 return re.match("\d+[KGBM]?", string, re.I).group(0)
418 except:
419 msg = "{0!r} don't looks like size-description string".format(string)
420 raise ValueError(msg)
421
422
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200423def ssize_to_kb(ssize):
424 try:
425 smap = dict(k=1, K=1, M=1024, m=1024, G=1024**2, g=1024**2)
426 for ext, coef in smap.items():
427 if ssize.endswith(ext):
428 return int(ssize[:-1]) * coef
429
430 if int(ssize) % 1024 != 0:
431 raise ValueError()
432
433 return int(ssize) / 1024
434
435 except (ValueError, TypeError, AttributeError):
436 tmpl = "Unknow size format {0!r} (or size not multiples 1024)"
437 raise ValueError(tmpl.format(ssize))
438
439
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200440def get_ram_size():
441 try:
442 with open("/proc/meminfo") as fd:
443 for ln in fd:
444 if "MemTotal:" in ln:
445 sz, kb = ln.split(':')[1].strip().split(" ")
446 assert kb == 'kB'
447 return int(sz)
448 except (ValueError, TypeError, AssertionError):
449 raise
450 # return None
451
452
koder aka kdanilov4af80852015-02-01 23:36:38 +0200453def parse_args(argv):
454 parser = argparse.ArgumentParser(
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200455 description="Run 'iozone' or 'fio' and return result")
456 parser.add_argument(
457 "--type", metavar="BINARY_TYPE",
458 choices=['iozone', 'fio'], required=True)
koder aka kdanilov4af80852015-02-01 23:36:38 +0200459 parser.add_argument(
460 "--iodepth", metavar="IODEPTH", type=int,
461 help="I/O depths to test in kb", required=True)
462 parser.add_argument(
463 '-a', "--action", metavar="ACTION", type=str,
464 help="actions to run", required=True,
465 choices=["read", "write", "randread", "randwrite"])
466 parser.add_argument(
467 "--blocksize", metavar="BLOCKSIZE", type=type_size,
468 help="single operation block size", required=True)
469 parser.add_argument(
470 "--timeout", metavar="TIMEOUT", type=int,
471 help="runtime of a single run", default=None)
472 parser.add_argument(
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200473 "--iosize", metavar="SIZE", type=type_size_ext,
koder aka kdanilov4af80852015-02-01 23:36:38 +0200474 help="file size", default=None)
475 parser.add_argument(
476 "-s", "--sync", default=False, action="store_true",
477 help="exec sync after each write")
478 parser.add_argument(
479 "-d", "--direct-io", default=False, action="store_true",
480 help="use O_DIRECT", dest='directio')
481 parser.add_argument(
482 "-t", "--sync-time", default=None, type=int,
483 help="sleep till sime utc time", dest='sync_time')
484 parser.add_argument(
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200485 "--binary-url", help="static binary url",
486 dest="binary_url", default=None)
koder aka kdanilov4af80852015-02-01 23:36:38 +0200487 parser.add_argument(
488 "--test-file", help="file path to run test on",
489 default=None, dest='test_file')
490 parser.add_argument(
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200491 "--binary-path", help="binary path",
492 default=None, dest='binary_path')
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800493 parser.add_argument(
494 "--prepare-only", default=False, dest='prepare_only',
495 action="store_true")
koder aka kdanilov3f356262015-02-13 08:06:14 -0800496 parser.add_argument("--concurrency", default=1, type=int)
koder aka kdanilove21d7472015-02-14 19:02:04 -0800497
498 parser.add_argument("--with-sensors", default="",
499 dest="with_sensors")
500
koder aka kdanilov4af80852015-02-01 23:36:38 +0200501 return parser.parse_args(argv)
502
503
koder aka kdanilove21d7472015-02-14 19:02:04 -0800504def sensor_thread(sensor_list, cmd_q, data_q):
505 while True:
506 try:
507 cmd_q.get(timeout=0.5)
508 data_q.put([])
509 return
510 except Queue.Empty:
511 pass
512
513
koder aka kdanilov4af80852015-02-01 23:36:38 +0200514def main(argv):
515 argv_obj = parse_args(argv)
516 argv_obj.blocksize = ssize_to_kb(argv_obj.blocksize)
517
518 if argv_obj.iosize is not None:
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200519 if argv_obj.iosize.startswith('x'):
520 argv_obj.iosize = argv_obj.blocksize * int(argv_obj.iosize[1:])
521 elif argv_obj.iosize.startswith('r'):
522 rs = get_ram_size()
523 if rs is None:
524 sys.stderr.write("Can't determine ram size\n")
525 exit(1)
526 argv_obj.iosize = rs * int(argv_obj.iosize[1:])
527 else:
528 argv_obj.iosize = ssize_to_kb(argv_obj.iosize)
koder aka kdanilov4af80852015-02-01 23:36:38 +0200529
koder aka kdanilov3f356262015-02-13 08:06:14 -0800530 benchmark = BenchmarkOption(argv_obj.concurrency,
koder aka kdanilov4af80852015-02-01 23:36:38 +0200531 argv_obj.iodepth,
532 argv_obj.action,
533 argv_obj.blocksize,
534 argv_obj.iosize)
535
536 benchmark.direct_io = argv_obj.directio
537
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200538 if argv_obj.sync:
539 benchmark.sync = True
540
koder aka kdanilov4af80852015-02-01 23:36:38 +0200541 test_file_name = argv_obj.test_file
542 if test_file_name is None:
543 with warnings.catch_warnings():
544 warnings.simplefilter("ignore")
545 test_file_name = os.tmpnam()
546
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200547 remove_binary, binary_path = locate_binary(argv_obj.type,
548 argv_obj.binary_url,
549 argv_obj.binary_path)
koder aka kdanilov4af80852015-02-01 23:36:38 +0200550
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200551 if binary_path is None:
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200552 sys.stderr.write("Can't locate binary {0}\n".format(argv_obj.type))
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200553 return 1
koder aka kdanilov4af80852015-02-01 23:36:38 +0200554
555 try:
556 if argv_obj.sync_time is not None:
557 dt = argv_obj.sync_time - time.time()
558 if dt > 0:
559 time.sleep(dt)
560
koder aka kdanilove21d7472015-02-14 19:02:04 -0800561 if argv_obj.with_sensors != "":
562 oq = Queue.Queue()
563 iq = Queue.Queue()
564 argv = (argv_obj.with_sensors, oq, iq)
565 th = threading.Thread(None, sensor_thread, None, argv)
566 th.daemon = True
567 th.start()
568
koder aka kdanilov4a72f122015-02-09 12:25:54 +0200569 res, cmd = run_benchmark(argv_obj.type,
570 benchmark,
571 binary_path,
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800572 test_file_name,
koder aka kdanilov3f356262015-02-13 08:06:14 -0800573 argv_obj.prepare_only,
574 argv_obj.timeout)
koder aka kdanilove21d7472015-02-14 19:02:04 -0800575 if argv_obj.with_sensors != "":
576 iq.put(None)
577 stats = oq.get()
578 else:
579 stats = None
koder aka kdanilov3f356262015-02-13 08:06:14 -0800580
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800581 if not argv_obj.prepare_only:
582 res['__meta__'] = benchmark.__dict__.copy()
583 res['__meta__']['cmdline'] = cmd
584
koder aka kdanilove21d7472015-02-14 19:02:04 -0800585 if stats is not None:
586 res['__meta__']['sensor_data'] = stats
587
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800588 sys.stdout.write(json.dumps(res))
589
590 if not argv_obj.prepare_only:
591 sys.stdout.write("\n")
592
koder aka kdanilov4af80852015-02-01 23:36:38 +0200593 finally:
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200594 if remove_binary:
595 os.unlink(binary_path)
koder aka kdanilov4af80852015-02-01 23:36:38 +0200596
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800597 if os.path.isfile(test_file_name) and not argv_obj.prepare_only:
koder aka kdanilov4af80852015-02-01 23:36:38 +0200598 os.unlink(test_file_name)
599
600
koder aka kdanilov4af80852015-02-01 23:36:38 +0200601if __name__ == '__main__':
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800602 exit(main(sys.argv[1:]))