blob: d1e7c005479f48a34daab451a25ba612a9831037 [file] [log] [blame]
koder aka kdanilov4643fd62015-02-10 16:20:13 -08001import abc
koder aka kdanilov66839a92015-04-11 13:22:31 +03002import time
koder aka kdanilov4643fd62015-02-10 16:20:13 -08003import os.path
koder aka kdanilove21d7472015-02-14 19:02:04 -08004import logging
5
koder aka kdanilov2e928022015-04-08 13:47:15 +03006from disk_perf_test_tool.tests import disk_test_agent
koder aka kdanilov66839a92015-04-11 13:22:31 +03007from disk_perf_test_tool.tests.disk_test_agent import parse_fio_config_full
koder aka kdanilov652cd802015-04-13 12:21:07 +03008from disk_perf_test_tool.tests.disk_test_agent import estimate_cfg, sec_to_str
koder aka kdanilov1c2b5112015-04-10 16:53:51 +03009from disk_perf_test_tool.tests.io_results_loader import parse_output
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030010from disk_perf_test_tool.ssh_utils import copy_paths, run_over_ssh, delete_file
koder aka kdanilov652cd802015-04-13 12:21:07 +030011from disk_perf_test_tool.utils import ssize_to_b
12
koder aka kdanilov4643fd62015-02-10 16:20:13 -080013
koder aka kdanilove21d7472015-02-14 19:02:04 -080014logger = logging.getLogger("io-perf-tool")
15
16
koder aka kdanilov4643fd62015-02-10 16:20:13 -080017class IPerfTest(object):
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030018 def __init__(self, on_result_cb, log_directory=None, node=None):
koder aka kdanilov4643fd62015-02-10 16:20:13 -080019 self.on_result_cb = on_result_cb
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030020 self.log_directory = log_directory
21 self.node = node
koder aka kdanilov4643fd62015-02-10 16:20:13 -080022
koder aka kdanilov4643fd62015-02-10 16:20:13 -080023 def pre_run(self, conn):
24 pass
25
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030026 def cleanup(self, conn):
27 pass
28
koder aka kdanilov4643fd62015-02-10 16:20:13 -080029 @abc.abstractmethod
koder aka kdanilov2c473092015-03-29 17:12:13 +030030 def run(self, conn, barrier):
koder aka kdanilov4643fd62015-02-10 16:20:13 -080031 pass
32
33
Yulia Portnova7ddfa732015-02-24 17:32:58 +020034class TwoScriptTest(IPerfTest):
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030035 def __init__(self, opts, on_result_cb, log_directory=None, node=None):
36 super(TwoScriptTest, self).__init__(on_result_cb, log_directory,
37 node=node)
Yulia Portnova7ddfa732015-02-24 17:32:58 +020038 self.opts = opts
39 self.pre_run_script = None
40 self.run_script = None
41 self.tmp_dir = "/tmp/"
42 self.set_run_script()
43 self.set_pre_run_script()
44
45 def set_run_script(self):
46 self.pre_run_script = self.opts.pre_run_script
47
48 def set_pre_run_script(self):
49 self.run_script = self.opts.run_script
50
51 def get_remote_for_script(self, script):
52 return os.path.join(self.tmp_dir, script.rpartition('/')[2])
53
54 def copy_script(self, conn, src):
55 remote_path = self.get_remote_for_script(src)
56 copy_paths(conn, {src: remote_path})
57 return remote_path
58
59 def pre_run(self, conn):
60 remote_script = self.copy_script(conn, self.pre_run_script)
61 cmd = remote_script
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030062 run_over_ssh(conn, cmd, node=self.node)
Yulia Portnova7ddfa732015-02-24 17:32:58 +020063
koder aka kdanilov2c473092015-03-29 17:12:13 +030064 def run(self, conn, barrier):
Yulia Portnova7ddfa732015-02-24 17:32:58 +020065 remote_script = self.copy_script(conn, self.run_script)
Yulia Portnova886a2562015-04-07 11:16:13 +030066 cmd_opts = ' '.join(["%s %s" % (key, val) for key, val
67 in self.opts.items()])
68 cmd = remote_script + ' ' + cmd_opts
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030069 out_err = run_over_ssh(conn, cmd, node=self.node)
koder aka kdanilov66839a92015-04-11 13:22:31 +030070 self.on_result(out_err, cmd)
Yulia Portnova7ddfa732015-02-24 17:32:58 +020071
72 def parse_results(self, out):
73 for line in out.split("\n"):
74 key, separator, value = line.partition(":")
75 if key and value:
76 self.on_result_cb((key, float(value)))
77
koder aka kdanilov66839a92015-04-11 13:22:31 +030078 def on_result(self, out_err, cmd):
79 try:
80 self.parse_results(out_err)
81 except Exception as exc:
82 msg_templ = "Error during postprocessing results: {0!r}. {1}"
83 raise RuntimeError(msg_templ.format(exc.message, out_err))
Yulia Portnova7ddfa732015-02-24 17:32:58 +020084
85
86class PgBenchTest(TwoScriptTest):
87
88 def set_run_script(self):
Yulia Portnova886a2562015-04-07 11:16:13 +030089 self.pre_run_script = "tests/postgres/prepare.sh"
Yulia Portnova7ddfa732015-02-24 17:32:58 +020090
91 def set_pre_run_script(self):
Yulia Portnova886a2562015-04-07 11:16:13 +030092 self.run_script = "tests/postgres/run.sh"
Yulia Portnova7ddfa732015-02-24 17:32:58 +020093
94
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030095def open_for_append_or_create(fname):
96 if not os.path.exists(fname):
97 return open(fname, "w")
98
99 fd = open(fname, 'r+')
100 fd.seek(0, os.SEEK_END)
101 return fd
102
103
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800104class IOPerfTest(IPerfTest):
koder aka kdanilovda45e882015-04-06 02:24:42 +0300105 io_py_remote = "/tmp/disk_test_agent.py"
koder aka kdanilov2c473092015-03-29 17:12:13 +0300106
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300107 def __init__(self, test_options, on_result_cb,
108 log_directory=None, node=None):
109 IPerfTest.__init__(self, on_result_cb, log_directory, node=node)
koder aka kdanilov2c473092015-03-29 17:12:13 +0300110 self.options = test_options
koder aka kdanilovda45e882015-04-06 02:24:42 +0300111 self.config_fname = test_options['cfg']
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300112 self.alive_check_interval = test_options.get('alive_check_interval')
koder aka kdanilovda45e882015-04-06 02:24:42 +0300113 self.config_params = test_options.get('params', {})
114 self.tool = test_options.get('tool', 'fio')
115 self.raw_cfg = open(self.config_fname).read()
koder aka kdanilov652cd802015-04-13 12:21:07 +0300116 self.configs = list(parse_fio_config_full(self.raw_cfg,
117 self.config_params))
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800118
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300119 cmd_log = os.path.join(self.log_directory, "task_compiled.cfg")
120 raw_res = os.path.join(self.log_directory, "raw_results.txt")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300121
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300122 fio_command_file = open_for_append_or_create(cmd_log)
123 fio_command_file.write(disk_test_agent.compile(self.raw_cfg,
124 self.config_params,
125 None))
126 self.fio_raw_results_file = open_for_append_or_create(raw_res)
127
128 def cleanup(self, conn):
129 delete_file(conn, self.io_py_remote)
130
131 def pre_run(self, conn):
koder aka kdanilov652cd802015-04-13 12:21:07 +0300132 try:
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300133 run_over_ssh(conn, 'which fio', node=self.node)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300134 except OSError:
135 # TODO: install fio, if not installed
136 cmd = "sudo apt-get -y install fio"
koder aka kdanilov66839a92015-04-11 13:22:31 +0300137
koder aka kdanilov652cd802015-04-13 12:21:07 +0300138 for i in range(3):
139 try:
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300140 run_over_ssh(conn, cmd, node=self.node)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300141 break
142 except OSError as err:
143 time.sleep(3)
144 else:
145 raise OSError("Can't install fio - " + err.message)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300146
147 local_fname = disk_test_agent.__file__.rsplit('.')[0] + ".py"
koder aka kdanilov2c473092015-03-29 17:12:13 +0300148 self.files_to_copy = {local_fname: self.io_py_remote}
koder aka kdanilov50f18642015-02-11 08:54:44 -0800149 copy_paths(conn, self.files_to_copy)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800150
koder aka kdanilovcee43342015-04-14 22:52:53 +0300151 cmd_templ = "sudo dd if=/dev/zero of={0} bs={1} count={2}"
koder aka kdanilov652cd802015-04-13 12:21:07 +0300152 files = {}
153
koder aka kdanilov2c473092015-03-29 17:12:13 +0300154 for secname, params in self.configs:
155 sz = ssize_to_b(params['size'])
156 msz = msz = sz / (1024 ** 2)
157 if sz % (1024 ** 2) != 0:
158 msz += 1
koder aka kdanilov6e2ae792015-03-04 18:02:24 -0800159
koder aka kdanilov652cd802015-04-13 12:21:07 +0300160 fname = params['filename']
161 files[fname] = max(files.get(fname, 0), msz)
162
163 for fname, sz in files.items():
164 cmd = cmd_templ.format(fname, 1024 ** 2, msz)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300165 run_over_ssh(conn, cmd, timeout=msz, node=self.node)
koder aka kdanilov6e2ae792015-03-04 18:02:24 -0800166
koder aka kdanilov2c473092015-03-29 17:12:13 +0300167 def run(self, conn, barrier):
koder aka kdanilovcee43342015-04-14 22:52:53 +0300168 cmd_templ = "sudo env python2 {0} --type {1} {2} --json -"
koder aka kdanilov66839a92015-04-11 13:22:31 +0300169
170 params = " ".join("{0}={1}".format(k, v)
171 for k, v in self.config_params.items())
172
173 if "" != params:
174 params = "--params " + params
175
176 cmd = cmd_templ.format(self.io_py_remote, self.tool, params)
177 logger.debug("Waiting on barrier")
koder aka kdanilov652cd802015-04-13 12:21:07 +0300178
179 exec_time = estimate_cfg(self.raw_cfg, self.config_params)
180 exec_time_str = sec_to_str(exec_time)
181
koder aka kdanilov2c473092015-03-29 17:12:13 +0300182 try:
koder aka kdanilov652cd802015-04-13 12:21:07 +0300183 if barrier.wait():
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300184 templ = "Test should takes about {0}. Will wait at most {1}"
185 timeout = int(exec_time * 1.1 + 300)
186 logger.info(templ.format(exec_time_str, sec_to_str(timeout)))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300187
188 out_err = run_over_ssh(conn, cmd,
189 stdin_data=self.raw_cfg,
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300190 timeout=timeout,
191 node=self.node)
koder aka kdanilov12ae0632015-04-15 01:13:43 +0300192 logger.info("Done")
koder aka kdanilov2c473092015-03-29 17:12:13 +0300193 finally:
194 barrier.exit()
195
koder aka kdanilov652cd802015-04-13 12:21:07 +0300196 self.on_result(out_err, cmd)
197
koder aka kdanilov66839a92015-04-11 13:22:31 +0300198 def on_result(self, out_err, cmd):
199 try:
200 for data in parse_output(out_err):
201 self.on_result_cb(data)
202 except Exception as exc:
203 msg_templ = "Error during postprocessing results: {0!r}"
204 raise RuntimeError(msg_templ.format(exc.message))
205
206 def merge_results(self, results):
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300207 if len(results) == 0:
208 return None
209
koder aka kdanilov66839a92015-04-11 13:22:31 +0300210 merged_result = results[0]
211 merged_data = merged_result['res']
212 expected_keys = set(merged_data.keys())
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300213 mergable_fields = ['bw', 'clat', 'iops', 'lat', 'slat']
koder aka kdanilov66839a92015-04-11 13:22:31 +0300214
215 for res in results[1:]:
216 assert res['__meta__'] == merged_result['__meta__']
217
218 data = res['res']
219 diff = set(data.keys()).symmetric_difference(expected_keys)
220
221 msg = "Difference: {0}".format(",".join(diff))
222 assert len(diff) == 0, msg
223
224 for testname, test_data in data.items():
225 res_test_data = merged_data[testname]
226
227 diff = set(test_data.keys()).symmetric_difference(
228 res_test_data.keys())
229
230 msg = "Difference: {0}".format(",".join(diff))
231 assert len(diff) == 0, msg
232
233 for k, v in test_data.items():
234 if k in mergable_fields:
235 res_test_data[k].extend(v)
236 else:
237 msg = "{0!r} != {1!r}".format(res_test_data[k], v)
238 assert res_test_data[k] == v, msg
239
240 return merged_result