blob: b1d5a59b49f47828e153ef927a8d2fc5fa4451d6 [file] [log] [blame]
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -08001import os
2import sys
3import json
4import time
koder aka kdanilov4ec0f712015-02-19 19:19:27 -08005import shutil
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -08006import pprint
koder aka kdanilov4ec0f712015-02-19 19:19:27 -08007import weakref
koder aka kdanilove21d7472015-02-14 19:02:04 -08008import logging
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -08009import os.path
10import argparse
koder aka kdanilov3f356262015-02-13 08:06:14 -080011import traceback
koder aka kdanilov4ec0f712015-02-19 19:19:27 -080012import subprocess
13import contextlib
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080014
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080015
16import ssh_runner
koder aka kdanilov4ec0f712015-02-19 19:19:27 -080017import io_scenario
18from utils import log_error
19from rest_api import add_test
20from itest import IOPerfTest, run_test_iter
21from starts_vms import nova_connect, create_vms_mt, clear_all
koder aka kdanilov7dec9df2015-02-15 21:35:19 -080022
23try:
24 import rally_runner
25except ImportError:
26 rally_runner = None
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080027
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080028
koder aka kdanilove21d7472015-02-14 19:02:04 -080029logger = logging.getLogger("io-perf-tool")
koder aka kdanilov4ec0f712015-02-19 19:19:27 -080030logger.setLevel(logging.INFO)
koder aka kdanilove21d7472015-02-14 19:02:04 -080031ch = logging.StreamHandler()
koder aka kdanilov4ec0f712015-02-19 19:19:27 -080032ch.setLevel(logging.INFO)
koder aka kdanilove21d7472015-02-14 19:02:04 -080033logger.addHandler(ch)
34
35log_format = '%(asctime)s - %(levelname)s - %(name)s - %(message)s'
36formatter = logging.Formatter(log_format,
37 "%H:%M:%S")
38ch.setFormatter(formatter)
39
40
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080041def run_io_test(tool,
42 script_args,
43 test_runner,
44 keep_temp_files=False):
45
46 files_dir = os.path.dirname(io_scenario.__file__)
47
48 path = 'iozone' if 'iozone' == tool else 'fio'
49 src_testtool_path = os.path.join(files_dir, path)
50
51 obj = IOPerfTest(script_args,
52 src_testtool_path,
53 None,
54 keep_temp_files)
55
56 return test_runner(obj)
57
58
koder aka kdanilov4ec0f712015-02-19 19:19:27 -080059class FileWrapper(object):
60 def __init__(self, fd, conn):
61 self.fd = fd
62 self.channel_wr = weakref.ref(conn)
63
64 def read(self):
65 return self.fd.read()
66
67 @property
68 def channel(self):
69 return self.channel_wr()
70
71
72class LocalConnection(object):
73 def __init__(self):
74 self.proc = None
75
76 def exec_command(self, cmd):
77 PIPE = subprocess.PIPE
78 self.proc = subprocess.Popen(cmd,
79 shell=True,
80 stdout=PIPE,
81 stderr=PIPE,
82 stdin=PIPE)
83 res = (self.proc.stdin,
84 FileWrapper(self.proc.stdout, self),
85 self.proc.stderr)
86
87 return res
88
89 def recv_exit_status(self):
90 return self.proc.wait()
91
92 def open_sftp(self):
93 return self
94
95 def close(self):
96 pass
97
98 def put(self, localfile, remfile):
99 return shutil.copy(localfile, remfile)
100
101 def mkdir(self, remotepath, mode):
102 os.mkdir(remotepath)
103 os.chmod(remotepath, mode)
104
105 def chmod(self, remotepath, mode):
106 os.chmod(remotepath, mode)
107
108 def copytree(self, src, dst):
109 shutil.copytree(src, dst)
110
111
112def get_local_runner(clear_tmp_files=True):
113 def closure(obj):
114 res = []
115 obj.set_result_cb(res.append)
116 test_iter = run_test_iter(obj,
117 LocalConnection())
118 next(test_iter)
119
120 with log_error("!Run test"):
121 next(test_iter)
122 return res
123
124 return closure
125
126
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800127def parse_args(argv):
128 parser = argparse.ArgumentParser(
129 description="Run disk io performance test")
130
131 parser.add_argument("tool_type", help="test tool type",
132 choices=['iozone', 'fio'])
133
134 parser.add_argument("-l", dest='extra_logs',
135 action='store_true', default=False,
136 help="print some extra log info")
137
138 parser.add_argument("-o", "--io-opts", dest='io_opts',
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800139 help="cmd line options for io.py")
140
koder aka kdanilov3f356262015-02-13 08:06:14 -0800141 parser.add_argument("-f", "--io-opts-file", dest='io_opts_file',
142 type=argparse.FileType('r'), default=None,
143 help="file with cmd line options for io.py")
144
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800145 parser.add_argument("-t", "--test-directory", help="directory with test",
146 dest="test_directory", required=True)
147
148 parser.add_argument("--max-preparation-time", default=300,
149 type=int, dest="max_preparation_time")
150
gstepanov8053b012015-02-16 17:25:27 +0200151 parser.add_argument("-b", "--build-info", default=None,
152 dest="build_name")
153
154 parser.add_argument("-d", "--data-server-url", default=None,
155 dest="data_server_url")
156
157 parser.add_argument("-n", "--lab-name", default=None,
158 dest="lab_name")
159
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800160 parser.add_argument("--create-vms-opts", default=None,
161 help="Creating vm's before run ssh runner",
162 dest="create_vms_opts")
163
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800164 parser.add_argument("-k", "--keep", default=False,
165 help="keep temporary files",
166 dest="keep_temp_files", action='store_true')
167
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800168 choices = ["local", "ssh"]
169
koder aka kdanilov7dec9df2015-02-15 21:35:19 -0800170 if rally_runner is not None:
171 choices.append("rally")
172
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800173 parser.add_argument("--runner", required=True,
koder aka kdanilov7dec9df2015-02-15 21:35:19 -0800174 choices=choices, help="runner type")
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800175
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800176 parser.add_argument("--runner-extra-opts", default=None,
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800177 dest="runner_opts", help="runner extra options")
178
179 return parser.parse_args(argv)
180
181
koder aka kdanilove21d7472015-02-14 19:02:04 -0800182def format_measurements_stat(res):
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800183 if len(res) != 0:
184 bw_mean = 0.0
185 for measurement in res:
186 bw_mean += measurement["bw_mean"]
187
188 bw_mean /= len(res)
189
190 it = ((bw_mean - measurement["bw_mean"]) ** 2 for measurement in res)
191 bw_dev = sum(it) ** 0.5
192
193 meta = res[0]['__meta__']
koder aka kdanilov7dec9df2015-02-15 21:35:19 -0800194
195 sync = meta['sync']
196 direct = meta['direct_io']
197
198 if sync and direct:
199 ss = "d+"
200 elif sync:
201 ss = "s"
202 elif direct:
203 ss = "d"
204 else:
205 ss = "a"
206
207 key = "{0} {1} {2} {3}k".format(meta['action'], ss,
208 meta['concurence'],
209 meta['blocksize'])
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800210
koder aka kdanilove21d7472015-02-14 19:02:04 -0800211 data = json.dumps({key: (int(bw_mean), int(bw_dev))})
212
gstepanov8053b012015-02-16 17:25:27 +0200213 return data
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800214
koder aka kdanilov3f356262015-02-13 08:06:14 -0800215
216def get_io_opts(io_opts_file, io_opts):
217 if io_opts_file is not None and io_opts is not None:
218 print "Options --io-opts-file and --io-opts can't be " + \
219 "provided same time"
220 exit(1)
221
222 if io_opts_file is None and io_opts is None:
223 print "Either --io-opts-file or --io-opts should " + \
224 "be provided"
225 exit(1)
226
227 if io_opts_file is not None:
228 io_opts = []
229
230 opt_lines = io_opts_file.readlines()
231 opt_lines = [i for i in opt_lines if i != "" and not i.startswith("#")]
232
233 for opt_line in opt_lines:
koder aka kdanilove21d7472015-02-14 19:02:04 -0800234 if opt_line.strip() != "":
235 io_opts.append([opt.strip()
236 for opt in opt_line.strip().split(" ")
237 if opt.strip() != ""])
koder aka kdanilov3f356262015-02-13 08:06:14 -0800238 else:
239 io_opts = [[opt.strip()
240 for opt in io_opts.split(" ")
241 if opt.strip() != ""]]
242
243 if len(io_opts) == 0:
244 print "Can't found parameters for io. Check" + \
245 "--io-opts-file or --io-opts options"
246 exit(1)
247
248 return io_opts
249
250
koder aka kdanilove21d7472015-02-14 19:02:04 -0800251def format_result(res):
252 data = "\n{0}\n".format("=" * 80)
253 data += pprint.pformat(res) + "\n"
254 data += "{0}\n".format("=" * 80)
koder aka kdanilovfe056622015-02-19 08:46:15 -0800255 templ = "{0}\n\n====> {1}\n\n{2}\n\n"
256 return templ.format(data, format_measurements_stat(res), "=" * 80)
koder aka kdanilove21d7472015-02-14 19:02:04 -0800257
258
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800259@contextlib.contextmanager
260def start_test_vms(opts):
261 create_vms_opts = {}
262 for opt in opts.split(","):
263 name, val = opt.split("=", 1)
264 create_vms_opts[name] = val
265
266 user = create_vms_opts.pop("user")
267 key_file = create_vms_opts.pop("key_file")
268 aff_group = create_vms_opts.pop("aff_group", None)
269 raw_count = create_vms_opts.pop("count", "x1")
270
271 logger.debug("Connection to nova")
272 nova = nova_connect()
273
274 if raw_count.startswith("x"):
275 logger.debug("Getting amount of compute services")
276 count = len(nova.services.list(binary="nova-compute"))
277 count *= int(raw_count[1:])
278 else:
279 count = int(raw_count)
280
281 if aff_group is not None:
282 scheduler_hints = {'group': aff_group}
283 else:
284 scheduler_hints = None
285
286 create_vms_opts['scheduler_hints'] = scheduler_hints
287
288 logger.debug("Will start {0} vms".format(count))
289
290 try:
291 ips = [i[0] for i in create_vms_mt(nova, count, **create_vms_opts)]
292
293 uris = ["{0}@{1}::{2}".format(user, ip, key_file) for ip in ips]
294
295 yield uris
296 except:
297 traceback.print_exc()
298 finally:
299 logger.debug("Clearing")
300 clear_all(nova)
301
302
koder aka kdanilov3f356262015-02-13 08:06:14 -0800303def main(argv):
304 opts = parse_args(argv)
305
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800306 if opts.extra_logs:
307 logger.setLevel(logging.DEBUG)
308 ch.setLevel(logging.DEBUG)
koder aka kdanilov3f356262015-02-13 08:06:14 -0800309
koder aka kdanilov3f356262015-02-13 08:06:14 -0800310 io_opts = get_io_opts(opts.io_opts_file, opts.io_opts)
311
312 if opts.runner == "rally":
koder aka kdanilove21d7472015-02-14 19:02:04 -0800313 logger.debug("Use rally runner")
koder aka kdanilov3f356262015-02-13 08:06:14 -0800314 for script_args in io_opts:
koder aka kdanilove21d7472015-02-14 19:02:04 -0800315
316 cmd_line = " ".join(script_args)
317 logger.debug("Run test with {0!r} params".format(cmd_line))
318
koder aka kdanilov3f356262015-02-13 08:06:14 -0800319 runner = rally_runner.get_rally_runner(
320 files_dir=os.path.dirname(io_scenario.__file__),
321 rally_extra_opts=opts.runner_opts.split(" "),
322 max_preparation_time=opts.max_preparation_time,
323 keep_temp_files=opts.keep_temp_files)
324
325 res = run_io_test(opts.tool_type,
326 script_args,
327 runner,
328 opts.keep_temp_files)
koder aka kdanilove21d7472015-02-14 19:02:04 -0800329 logger.debug(format_result(res))
koder aka kdanilov3f356262015-02-13 08:06:14 -0800330
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800331 elif opts.runner == "local":
332 logger.debug("Run on local computer")
333 try:
334 for script_args in io_opts:
335 cmd_line = " ".join(script_args)
336 logger.debug("Run test with {0!r} params".format(cmd_line))
337 runner = get_local_runner(opts.keep_temp_files)
338 res = run_io_test(opts.tool_type,
339 script_args,
340 runner,
341 opts.keep_temp_files)
342 logger.debug(format_result(res))
343 except:
344 traceback.print_exc()
345 return 1
346
koder aka kdanilov3f356262015-02-13 08:06:14 -0800347 elif opts.runner == "ssh":
koder aka kdanilove21d7472015-02-14 19:02:04 -0800348 logger.debug("Use ssh runner")
koder aka kdanilov3f356262015-02-13 08:06:14 -0800349
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800350 uris = []
koder aka kdanilov3f356262015-02-13 08:06:14 -0800351
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800352 if opts.create_vms_opts is not None:
353 vm_context = start_test_vms(opts.create_vms_opts)
354 uris += vm_context.__enter__()
koder aka kdanilov3f356262015-02-13 08:06:14 -0800355 else:
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800356 vm_context = None
koder aka kdanilov3f356262015-02-13 08:06:14 -0800357
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800358 if opts.runner_opts is not None:
359 uris += opts.runner_opts.split(";")
koder aka kdanilov3f356262015-02-13 08:06:14 -0800360
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800361 if len(uris) == 0:
362 logger.critical("You need to provide at least" +
363 " vm spawn params or ssh params")
364 return 1
koder aka kdanilove21d7472015-02-14 19:02:04 -0800365
koder aka kdanilov3f356262015-02-13 08:06:14 -0800366 try:
koder aka kdanilov3f356262015-02-13 08:06:14 -0800367 for script_args in io_opts:
koder aka kdanilove21d7472015-02-14 19:02:04 -0800368 cmd_line = " ".join(script_args)
369 logger.debug("Run test with {0!r} params".format(cmd_line))
370 latest_start_time = opts.max_preparation_time + time.time()
koder aka kdanilov3f356262015-02-13 08:06:14 -0800371 runner = ssh_runner.get_ssh_runner(uris,
372 latest_start_time,
373 opts.keep_temp_files)
374 res = run_io_test(opts.tool_type,
375 script_args,
376 runner,
377 opts.keep_temp_files)
koder aka kdanilove21d7472015-02-14 19:02:04 -0800378 logger.debug(format_result(res))
koder aka kdanilov3f356262015-02-13 08:06:14 -0800379
koder aka kdanilov3f356262015-02-13 08:06:14 -0800380 except:
381 traceback.print_exc()
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800382 return 1
koder aka kdanilov3f356262015-02-13 08:06:14 -0800383 finally:
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800384 if vm_context is not None:
385 vm_context.__exit__()
386 logger.debug("Clearing")
koder aka kdanilov3f356262015-02-13 08:06:14 -0800387
koder aka kdanilov4ec0f712015-02-19 19:19:27 -0800388 if opts.data_server_url:
389 result = json.loads(format_measurements_stat(res))
390 result['name'] = opts.build_name
391 add_test(opts.build_name, result, opts.data_server_url)
392
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800393 return 0
394
395
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800396if __name__ == '__main__':
397 exit(main(sys.argv[1:]))