blob: e3b7bcfea7eddb096496eed98ef72741849c41cc [file] [log] [blame]
koder aka kdanilov4af80852015-02-01 23:36:38 +02001import os
2import re
3import sys
4import time
5import yaml
6import json
7import os.path
koder aka kdanilov80cb6192015-02-02 03:06:08 +02008import argparse
koder aka kdanilov4af80852015-02-01 23:36:38 +02009import datetime
10import warnings
11import functools
12import contextlib
13import multiprocessing
14
15from rally import exceptions
16from rally.cmd import cliutils
17from rally.cmd.main import categories
18from rally.benchmark.scenarios.vm.utils import VMScenario
19
koder aka kdanilov80cb6192015-02-02 03:06:08 +020020from ssh_copy_directory import put_dir_recursively, ssh_copy_file
21
koder aka kdanilov4af80852015-02-01 23:36:38 +020022
23def log(x):
24 dt_str = datetime.datetime.now().strftime("%H:%M:%S")
25 pref = dt_str + " " + str(os.getpid()) + " >>>> "
26 sys.stderr.write(pref + x.replace("\n", "\n" + pref) + "\n")
27
28
29def get_barrier(count):
30 val = multiprocessing.Value('i', count)
31 cond = multiprocessing.Condition()
32
33 def closure(timeout):
koder aka kdanilov80cb6192015-02-02 03:06:08 +020034 me_released = False
koder aka kdanilov4af80852015-02-01 23:36:38 +020035 with cond:
36 val.value -= 1
koder aka kdanilov4af80852015-02-01 23:36:38 +020037 if val.value == 0:
koder aka kdanilov80cb6192015-02-02 03:06:08 +020038 me_released = True
koder aka kdanilov4af80852015-02-01 23:36:38 +020039 cond.notify_all()
40 else:
41 cond.wait(timeout)
42 return val.value == 0
43
koder aka kdanilov80cb6192015-02-02 03:06:08 +020044 if me_released:
45 log("Test begins!")
46
koder aka kdanilov4af80852015-02-01 23:36:38 +020047 return closure
48
49
koder aka kdanilov4af80852015-02-01 23:36:38 +020050@contextlib.contextmanager
51def patch_VMScenario_run_command_over_ssh(paths,
koder aka kdanilov80cb6192015-02-02 03:06:08 +020052 on_result_cb,
koder aka kdanilov4af80852015-02-01 23:36:38 +020053 barrier=None,
54 latest_start_time=None):
55
56 orig = VMScenario.run_command_over_ssh
57
58 @functools.wraps(orig)
59 def closure(self, ssh, *args, **kwargs):
60 try:
61 sftp = ssh._client.open_sftp()
62 except AttributeError:
63 # rally code was changed
64 log("Prototype of VMScenario.run_command_over_ssh "
65 "was changed. Update patch code.")
koder aka kdanilov80cb6192015-02-02 03:06:08 +020066 raise exceptions.ScriptError("monkeypatch code fails on "
koder aka kdanilov4af80852015-02-01 23:36:38 +020067 "ssh._client.open_sftp()")
koder aka kdanilov80cb6192015-02-02 03:06:08 +020068 try:
69 for src, dst in paths.items():
70 try:
71 if os.path.isfile(src):
72 ssh_copy_file(sftp, src, dst)
73 elif os.path.isdir(src):
74 put_dir_recursively(sftp, src, dst)
75 else:
76 templ = "Can't copy {0!r} - " + \
77 "it neither file or directory"
78 msg = templ.format(src)
79 log(msg)
80 raise exceptions.ScriptError(msg)
81 except exceptions.ScriptError:
82 raise
83 except Exception as exc:
84 tmpl = "Scp {0!r} => {1!r} failed - {2!r}"
85 msg = tmpl.format(src, dst, exc)
86 log(msg)
87 raise exceptions.ScriptError(msg)
88 finally:
89 sftp.close()
koder aka kdanilov4af80852015-02-01 23:36:38 +020090
91 log("Start io test")
92
93 if barrier is not None:
94 if latest_start_time is not None:
95 timeout = latest_start_time - time.time()
96 else:
97 timeout = None
98
99 if timeout is not None and timeout > 0:
100 msg = "Ready and waiting on barrier. " + \
101 "Will wait at most {0} seconds"
102 log(msg.format(int(timeout)))
103
104 if not barrier(timeout):
105 log("Barrier timeouted")
106
107 try:
108 code, out, err = orig(self, ssh, *args, **kwargs)
109 except Exception as exc:
110 log("Rally raises exception {0}".format(exc.message))
111 raise
112
113 if 0 != code:
114 templ = "Script returns error! code={0}\n {1}"
115 log(templ.format(code, err.rstrip()))
116 else:
117 log("Test finished")
118 try:
119 try:
120 result = json.loads(out)
121 except:
122 pass
123 else:
124 if '__meta__' in result:
koder aka kdanilov80cb6192015-02-02 03:06:08 +0200125 on_result_cb(result)
126 result.pop('__meta__')
koder aka kdanilov4af80852015-02-01 23:36:38 +0200127 out = json.dumps(result)
128 except Exception as err:
129 log("Error during postprocessing results: {0!r}".format(err))
130
131 return code, out, err
132
133 VMScenario.run_command_over_ssh = closure
134
135 try:
136 yield
137 finally:
138 VMScenario.run_command_over_ssh = orig
139
140
141def run_rally(rally_args):
142 return cliutils.run(['rally'] + rally_args, categories)
143
144
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200145def prepare_files(testtool_py_argv, dst_testtool_path, files_dir):
koder aka kdanilov4af80852015-02-01 23:36:38 +0200146
147 # we do need temporary named files
148 with warnings.catch_warnings():
149 warnings.simplefilter("ignore")
150 py_file = os.tmpnam()
151 yaml_file = os.tmpnam()
152
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200153 testtool_py_inp_path = os.path.join(files_dir, "io.py")
154 py_src_cont = open(testtool_py_inp_path).read()
155 args_repl_rr = r'INSERT_TOOL_ARGS\(sys\.argv.*?\)'
156 py_dst_cont = re.sub(args_repl_rr, repr(testtool_py_argv), py_src_cont)
koder aka kdanilov4af80852015-02-01 23:36:38 +0200157
158 if py_dst_cont == args_repl_rr:
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200159 templ = "Can't find replace marker in file {0}"
160 log(templ.format(testtool_py_inp_path))
koder aka kdanilov4af80852015-02-01 23:36:38 +0200161 exit(1)
162
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200163 yaml_src_cont = open(os.path.join(files_dir, "io.yaml")).read()
koder aka kdanilov4af80852015-02-01 23:36:38 +0200164 task_params = yaml.load(yaml_src_cont)
165 rcd_params = task_params['VMTasks.boot_runcommand_delete']
166 rcd_params[0]['args']['script'] = py_file
167 yaml_dst_cont = yaml.dump(task_params)
168
169 open(py_file, "w").write(py_dst_cont)
170 open(yaml_file, "w").write(yaml_dst_cont)
171
172 return yaml_file, py_file
173
174
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200175def run_test(tool, testtool_py_argv, dst_testtool_path, files_dir):
176 path = 'iozone' if 'iozone' == tool else 'fio'
177 testtool_local = os.path.join(files_dir, path)
koder aka kdanilov4af80852015-02-01 23:36:38 +0200178
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200179 yaml_file, py_file = prepare_files(testtool_py_argv,
180 dst_testtool_path,
koder aka kdanilov4af80852015-02-01 23:36:38 +0200181 files_dir)
182
183 config = yaml.load(open(yaml_file).read())
184
185 vm_sec = 'VMTasks.boot_runcommand_delete'
186 concurrency = config[vm_sec][0]['runner']['concurrency']
187
188 max_preparation_time = 300
189
190 try:
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200191 copy_files = {testtool_local: dst_testtool_path}
koder aka kdanilov4af80852015-02-01 23:36:38 +0200192
193 result_queue = multiprocessing.Queue()
koder aka kdanilov80cb6192015-02-02 03:06:08 +0200194 results_cb = result_queue.put
koder aka kdanilov4af80852015-02-01 23:36:38 +0200195
196 do_patch = patch_VMScenario_run_command_over_ssh
197
198 barrier = get_barrier(concurrency)
199 max_release_time = time.time() + max_preparation_time
200
koder aka kdanilov80cb6192015-02-02 03:06:08 +0200201 with do_patch(copy_files, results_cb, barrier, max_release_time):
koder aka kdanilov4af80852015-02-01 23:36:38 +0200202 log("Start rally with 'task start {0}'".format(yaml_file))
203 rally_result = run_rally(['task', 'start', yaml_file])
204
205 # while not result_queue.empty():
206 # log("meta = {0!r}\n".format(result_queue.get()))
207
208 return rally_result
209
210 finally:
211 os.unlink(yaml_file)
212 os.unlink(py_file)
koder aka kdanilov80cb6192015-02-02 03:06:08 +0200213
214
215def parse_args(argv):
216 parser = argparse.ArgumentParser(
217 description="Run rally disk io performance test")
218 parser.add_argument("tool_type", help="test tool type",
219 choices=['iozone', 'fio'])
220 parser.add_argument("test_directory", help="directory with test")
221 parser.add_argument("-l", dest='extra_logs',
222 action='store_true', default=False,
223 help="print some extra log info")
224 parser.add_argument("-o", "--io-opts", dest='io_opts',
225 default=None, help="cmd line options for io.py")
226 return parser.parse_args(argv)
koder aka kdanilov4af80852015-02-01 23:36:38 +0200227
228
229def main(argv):
koder aka kdanilov80cb6192015-02-02 03:06:08 +0200230 opts = parse_args(argv)
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200231 dst_testtool_path = '/tmp/io_tool'
232
koder aka kdanilov80cb6192015-02-02 03:06:08 +0200233 if not opts.extra_logs:
234 global log
235
236 def nolog(x):
237 pass
238
239 log = nolog
240
241 if opts.io_opts is None:
242 testtool_py_argv = ['--type', opts.tool_type,
243 '-a', 'randwrite',
244 '--iodepth', '2',
245 '--blocksize', '4k',
246 '--iosize', '20M',
247 '--binary-path', dst_testtool_path,
248 '-d']
249 else:
250 testtool_py_argv = opts.io_opts.split(" ")
251
252 run_test(opts.tool_type,
253 testtool_py_argv,
254 dst_testtool_path,
255 opts.test_directory)
256 return 0
koder aka kdanilov4af80852015-02-01 23:36:38 +0200257
258# ubuntu cloud image
259# https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img
260
261# glance image-create --name 'ubuntu' --disk-format qcow2
262# --container-format bare --is-public true --copy-from
263# https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img
264
265if __name__ == '__main__':
koder aka kdanilov98615bf2015-02-02 00:59:07 +0200266 exit(main(sys.argv[1:]))