blob: 1e948fbc4d1c01c22ee81f3f0a3864cb213e4a36 [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 kdanilov783b4542015-04-23 18:57:04 +03003import socket
koder aka kdanilov4d4771c2015-04-23 01:32:02 +03004import random
koder aka kdanilov4643fd62015-02-10 16:20:13 -08005import os.path
koder aka kdanilove21d7472015-02-14 19:02:04 -08006import logging
koder aka kdanilovea22c3d2015-04-21 03:42:22 +03007import datetime
koder aka kdanilove21d7472015-02-14 19:02:04 -08008
koder aka kdanilov783b4542015-04-23 18:57:04 +03009from paramiko import SSHException
10
koder aka kdanilove2de58c2015-04-24 22:59:36 +030011from wally.utils import (ssize_to_b, open_for_append_or_create,
12 sec_to_str, StopTestError)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030013
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030014from wally.ssh_utils import (copy_paths, run_over_ssh,
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030015 save_to_remote,
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030016 # delete_file,
17 connect, read_from_remote, Local)
18
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030019from . import postgres
20from .io import agent as io_agent
21from .io import formatter as io_formatter
22from .io.results_loader import parse_output
koder aka kdanilov652cd802015-04-13 12:21:07 +030023
koder aka kdanilov4643fd62015-02-10 16:20:13 -080024
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030025logger = logging.getLogger("wally")
koder aka kdanilove21d7472015-02-14 19:02:04 -080026
27
koder aka kdanilov4643fd62015-02-10 16:20:13 -080028class IPerfTest(object):
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030029 def __init__(self, options, is_primary, on_result_cb, test_uuid, node,
koder aka kdanilov2066daf2015-04-23 21:05:41 +030030 log_directory=None,
31 coordination_queue=None,
32 remote_dir="/tmp/wally"):
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030033 self.options = options
koder aka kdanilov4643fd62015-02-10 16:20:13 -080034 self.on_result_cb = on_result_cb
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030035 self.log_directory = log_directory
36 self.node = node
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030037 self.test_uuid = test_uuid
koder aka kdanilovec1b9732015-04-23 20:43:29 +030038 self.coordination_queue = coordination_queue
koder aka kdanilov2066daf2015-04-23 21:05:41 +030039 self.remote_dir = remote_dir
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030040 self.is_primary = is_primary
koder aka kdanilove2de58c2015-04-24 22:59:36 +030041 self.stop_requested = False
42
43 def request_stop(self):
44 self.stop_requested = True
koder aka kdanilov2066daf2015-04-23 21:05:41 +030045
46 def join_remote(self, path):
47 return os.path.join(self.remote_dir, path)
koder aka kdanilovec1b9732015-04-23 20:43:29 +030048
49 def coordinate(self, data):
50 if self.coordination_queue is not None:
koder aka kdanilove2de58c2015-04-24 22:59:36 +030051 self.coordination_queue.put((self.node.get_conn_id(), data))
koder aka kdanilov4643fd62015-02-10 16:20:13 -080052
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030053 def pre_run(self):
koder aka kdanilov4643fd62015-02-10 16:20:13 -080054 pass
55
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030056 def cleanup(self):
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030057 pass
58
koder aka kdanilov4643fd62015-02-10 16:20:13 -080059 @abc.abstractmethod
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030060 def run(self, barrier):
koder aka kdanilov4643fd62015-02-10 16:20:13 -080061 pass
62
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030063 @classmethod
64 def format_for_console(cls, data):
65 msg = "{0}.format_for_console".format(cls.__name__)
66 raise NotImplementedError(msg)
67
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030068 def run_over_ssh(self, cmd, **kwargs):
69 return run_over_ssh(self.node.connection, cmd,
70 node=self.node.get_conn_id(), **kwargs)
71
koder aka kdanilovec1b9732015-04-23 20:43:29 +030072 @classmethod
73 def coordination_th(cls, coord_q, barrier, num_threads):
74 pass
75
koder aka kdanilov4643fd62015-02-10 16:20:13 -080076
Yulia Portnova7ddfa732015-02-24 17:32:58 +020077class TwoScriptTest(IPerfTest):
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030078 def __init__(self, *dt, **mp):
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030079 IPerfTest.__init__(self, *dt, **mp)
Yulia Portnova7ddfa732015-02-24 17:32:58 +020080
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030081 if 'run_script' in self.options:
82 self.run_script = self.options['run_script']
83 self.prepare_script = self.options['prepare_script']
Yulia Portnova7ddfa732015-02-24 17:32:58 +020084
85 def get_remote_for_script(self, script):
86 return os.path.join(self.tmp_dir, script.rpartition('/')[2])
87
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030088 def copy_script(self, src):
Yulia Portnova7ddfa732015-02-24 17:32:58 +020089 remote_path = self.get_remote_for_script(src)
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030090 copy_paths(self.node.connection, {src: remote_path})
Yulia Portnova7ddfa732015-02-24 17:32:58 +020091 return remote_path
92
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030093 def pre_run(self):
94 remote_script = self.copy_script(self.node.connection,
95 self.pre_run_script)
Yulia Portnova7ddfa732015-02-24 17:32:58 +020096 cmd = remote_script
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030097 self.run_over_ssh(cmd)
Yulia Portnova7ddfa732015-02-24 17:32:58 +020098
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030099 def run(self, barrier):
100 remote_script = self.copy_script(self.node.connection, self.run_script)
Yulia Portnova886a2562015-04-07 11:16:13 +0300101 cmd_opts = ' '.join(["%s %s" % (key, val) for key, val
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300102 in self.options.items()])
Yulia Portnova886a2562015-04-07 11:16:13 +0300103 cmd = remote_script + ' ' + cmd_opts
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300104 out_err = self.run_over_ssh(cmd)
koder aka kdanilov66839a92015-04-11 13:22:31 +0300105 self.on_result(out_err, cmd)
Yulia Portnova7ddfa732015-02-24 17:32:58 +0200106
107 def parse_results(self, out):
108 for line in out.split("\n"):
109 key, separator, value = line.partition(":")
110 if key and value:
111 self.on_result_cb((key, float(value)))
112
koder aka kdanilov66839a92015-04-11 13:22:31 +0300113 def on_result(self, out_err, cmd):
114 try:
115 self.parse_results(out_err)
116 except Exception as exc:
koder aka kdanilovec1b9732015-04-23 20:43:29 +0300117 msg_templ = "Error during postprocessing results: {0!s}. {1}"
118 raise RuntimeError(msg_templ.format(exc, out_err))
Yulia Portnova7ddfa732015-02-24 17:32:58 +0200119
120
121class PgBenchTest(TwoScriptTest):
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300122 root = os.path.dirname(postgres.__file__)
123 prepare_script = os.path.join(root, "prepare.sh")
124 run_script = os.path.join(root, "run.sh")
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300125
126
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800127class IOPerfTest(IPerfTest):
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300128 tcp_conn_timeout = 30
129 max_pig_timeout = 30
koder aka kdanilov2c473092015-03-29 17:12:13 +0300130
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300131 def __init__(self, *dt, **mp):
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300132 IPerfTest.__init__(self, *dt, **mp)
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300133 self.config_fname = self.options['cfg']
134 self.alive_check_interval = self.options.get('alive_check_interval')
135 self.config_params = self.options.get('params', {})
136 self.tool = self.options.get('tool', 'fio')
koder aka kdanilovda45e882015-04-06 02:24:42 +0300137 self.raw_cfg = open(self.config_fname).read()
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300138 self.configs = list(io_agent.parse_all_in_1(self.raw_cfg,
139 self.config_params))
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800140
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300141 cmd_log = os.path.join(self.log_directory, "task_compiled.cfg")
142 raw_res = os.path.join(self.log_directory, "raw_results.txt")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300143
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300144 self.io_py_remote = self.join_remote("agent.py")
145 self.log_fl = self.join_remote("log.txt")
146 self.pid_file = self.join_remote("pid")
147 self.task_file = self.join_remote("task.cfg")
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300148 self.use_sudo = self.options.get("use_sudo", True)
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300149
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300150 fio_command_file = open_for_append_or_create(cmd_log)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300151
152 cfg_s_it = io_agent.compile_all_in_1(self.raw_cfg, self.config_params)
153 splitter = "\n\n" + "-" * 60 + "\n\n"
154 fio_command_file.write(splitter.join(cfg_s_it))
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300155 self.fio_raw_results_file = open_for_append_or_create(raw_res)
156
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300157 def __str__(self):
158 return "{0}({1})".format(self.__class__.__name__,
159 self.node.get_conn_id())
160
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300161 def cleanup(self):
162 # delete_file(conn, self.io_py_remote)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300163 # Need to remove tempo files, used for testing
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300164 pass
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300165
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300166 def prefill_test_files(self):
167 files = {}
168
169 for section in self.configs:
170 sz = ssize_to_b(section.vals['size'])
171 msz = sz / (1024 ** 2)
172
173 if sz % (1024 ** 2) != 0:
174 msz += 1
175
176 fname = section.vals['filename']
177
178 # if already has other test with the same file name
179 # take largest size
180 files[fname] = max(files.get(fname, 0), msz)
181
182 cmd_templ = "dd oflag=direct " + \
183 "if=/dev/zero of={0} bs={1} count={2}"
184
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300185 if self.use_sudo:
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300186 cmd_templ = "sudo " + cmd_templ
187
188 ssize = 0
189 stime = time.time()
190
191 for fname, curr_sz in files.items():
192 cmd = cmd_templ.format(fname, 1024 ** 2, curr_sz)
193 ssize += curr_sz
194 self.run_over_ssh(cmd, timeout=curr_sz)
195
196 ddtime = time.time() - stime
197 if ddtime > 1E-3:
198 fill_bw = int(ssize / ddtime)
199 mess = "Initiall dd fill bw is {0} MiBps for this vm"
200 logger.info(mess.format(fill_bw))
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300201 self.coordinate(('init_bw', fill_bw))
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300202
203 def install_utils(self, max_retry=3, timeout=5):
204 need_install = []
205 for bin_name, package in (('fio', 'fio'), ('screen', 'screen')):
206 try:
207 self.run_over_ssh('which ' + bin_name, nolog=True)
208 except OSError:
209 need_install.append(package)
210
koder aka kdanilovafd98742015-04-24 01:27:22 +0300211 if len(need_install) == 0:
212 return
213
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300214 cmd = "sudo apt-get -y install " + " ".join(need_install)
215
216 for i in range(max_retry):
217 try:
218 self.run_over_ssh(cmd)
219 break
220 except OSError as err:
221 time.sleep(timeout)
222 else:
223 raise OSError("Can't install - " + str(err))
224
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300225 def pre_run(self):
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300226 try:
227 cmd = 'mkdir -p "{0}"'.format(self.remote_dir)
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300228 if self.use_sudo:
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300229 cmd = "sudo " + cmd
230 cmd += " ; sudo chown {0} {1}".format(self.node.get_user(),
231 self.remote_dir)
232
233 self.run_over_ssh(cmd)
234 except Exception as exc:
235 msg = "Failed to create folder {0} on remote {1}. Error: {2!s}"
236 msg = msg.format(self.remote_dir, self.node.get_conn_id(), exc)
237 logger.error(msg)
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300238 raise StopTestError(msg, exc)
koder aka kdanilov783b4542015-04-23 18:57:04 +0300239
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300240 self.install_utils()
koder aka kdanilovda45e882015-04-06 02:24:42 +0300241
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300242 local_fname = os.path.splitext(io_agent.__file__)[0] + ".py"
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300243 files_to_copy = {local_fname: self.io_py_remote}
244 copy_paths(self.node.connection, files_to_copy)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800245
koder aka kdanilove87ae652015-04-20 02:14:35 +0300246 if self.options.get('prefill_files', True):
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300247 self.prefill_test_files()
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300248 elif self.is_primary:
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300249 logger.warning("Prefilling of test files is disabled")
koder aka kdanilov6e2ae792015-03-04 18:02:24 -0800250
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300251 def check_process_is_running(self, sftp, pid):
252 try:
253 sftp.stat("/proc/{0}".format(pid))
254 return True
255 except OSError:
256 return False
257
258 def kill_remote_process(self, conn, pid, soft=True):
259 try:
260 if soft:
261 cmd = "kill {0}"
262 else:
263 cmd = "kill -9 {0}"
264
265 if self.use_sudo:
266 cmd = "sudo " + cmd
267
268 self.run_over_ssh(cmd.format(pid))
269 return True
270 except OSError:
271 return False
272
273 def get_test_status(self, die_timeout=3):
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300274 is_connected = None
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300275 is_running = None
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300276 pid = None
277 err = None
278
279 try:
280 conn = connect(self.node.conn_url,
281 conn_timeout=self.tcp_conn_timeout)
282 with conn:
283 with conn.open_sftp() as sftp:
284 try:
285 pid = read_from_remote(sftp, self.pid_file)
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300286 is_running = True
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300287 except (NameError, IOError) as exc:
288 pid = None
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300289 is_running = False
290
291 if is_running:
292 if not self.check_process_is_running(sftp, pid):
293 sftp.remove(self.pid_file)
294 is_running = False
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300295
296 is_connected = True
297
298 except (socket.error, SSHException, EOFError) as exc:
299 err = str(exc)
300 is_connected = False
301
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300302 return is_connected, is_running, pid, err
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300303
304 def wait_till_finished(self, timeout):
305 conn_id = self.node.get_conn_id()
306 end_of_wait_time = timeout + time.time()
307
308 # time_till_check = random.randint(30, 90)
309 time_till_check = 5
310 pid = None
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300311 is_running = False
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300312 pid_get_timeout = self.max_pig_timeout + time.time()
313 curr_connected = True
314
315 while end_of_wait_time > time.time():
316 time.sleep(time_till_check)
317
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300318 is_connected, is_running, pid, err = self.get_test_status()
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300319
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300320 if not is_running:
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300321 if pid is None and time.time() > pid_get_timeout:
322 msg = ("On node {0} pid file doesn't " +
323 "appears in time")
324 logger.error(msg.format(conn_id))
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300325 raise StopTestError("Start timeout")
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300326 else:
327 # execution finished
328 break
329
330 if is_connected and not curr_connected:
331 msg = "Connection with {0} is restored"
332 logger.debug(msg.format(conn_id))
333 elif not is_connected and curr_connected:
334 msg = "Lost connection with " + conn_id + ". Error: " + err
335 logger.debug(msg)
336
337 curr_connected = is_connected
338
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300339 def run(self, barrier):
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300340 conn_id = self.node.get_conn_id()
341
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300342 cmd_templ = "screen -S {screen_name} -d -m " + \
343 "env python2 {0} -p {pid_file} -o {results_file} " + \
344 "--type {1} {2} --json {3}"
345
346 if self.options.get("use_sudo", True):
347 cmd_templ = "sudo " + cmd_templ
koder aka kdanilov66839a92015-04-11 13:22:31 +0300348
349 params = " ".join("{0}={1}".format(k, v)
350 for k, v in self.config_params.items())
351
352 if "" != params:
353 params = "--params " + params
354
koder aka kdanilov783b4542015-04-23 18:57:04 +0300355 with self.node.connection.open_sftp() as sftp:
356 save_to_remote(sftp,
357 self.task_file, self.raw_cfg)
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300358
359 screen_name = self.test_uuid
360 cmd = cmd_templ.format(self.io_py_remote,
361 self.tool,
362 params,
363 self.task_file,
364 pid_file=self.pid_file,
365 results_file=self.log_fl,
366 screen_name=screen_name)
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300367 msg = "Thread for node {0} is waiting on barrier"
368 logger.debug(msg.format(conn_id))
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300369 exec_time = io_agent.calculate_execution_time(self.configs)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300370 exec_time_str = sec_to_str(exec_time)
371
koder aka kdanilov2c473092015-03-29 17:12:13 +0300372 try:
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300373 timeout = int(exec_time * 2 + 300)
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300374 barrier.wait()
375
376 if self.is_primary:
koder aka kdanilovea22c3d2015-04-21 03:42:22 +0300377 templ = "Test should takes about {0}." + \
378 " Should finish at {1}," + \
379 " will wait at most till {2}"
380 now_dt = datetime.datetime.now()
381 end_dt = now_dt + datetime.timedelta(0, exec_time)
382 wait_till = now_dt + datetime.timedelta(0, timeout)
383
384 logger.info(templ.format(exec_time_str,
385 end_dt.strftime("%H:%M:%S"),
386 wait_till.strftime("%H:%M:%S")))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300387
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300388 self.run_over_ssh(cmd)
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300389
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300390 msg = "Test on node {0} started in screen {1}"
391 logger.debug(msg.format(conn_id, screen_name))
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300392
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300393 # TODO: add monitoring socket
394 if self.node.connection is not Local:
395 self.node.connection.close()
396
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300397 self.wait_till_finished(timeout)
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300398 logger.debug("Done")
399
400 if self.node.connection is not Local:
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300401 conn_timeout = self.tcp_conn_timeout * 3
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300402 self.node.connection = connect(self.node.conn_url,
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300403 conn_timeout=conn_timeout)
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300404
koder aka kdanilov783b4542015-04-23 18:57:04 +0300405 with self.node.connection.open_sftp() as sftp:
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300406 out_err = read_from_remote(sftp, self.log_fl)
407
koder aka kdanilov2c473092015-03-29 17:12:13 +0300408 finally:
409 barrier.exit()
410
koder aka kdanilov652cd802015-04-13 12:21:07 +0300411 self.on_result(out_err, cmd)
412
koder aka kdanilov66839a92015-04-11 13:22:31 +0300413 def on_result(self, out_err, cmd):
414 try:
415 for data in parse_output(out_err):
416 self.on_result_cb(data)
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300417 except (OSError, StopTestError):
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300418 raise
koder aka kdanilov66839a92015-04-11 13:22:31 +0300419 except Exception as exc:
koder aka kdanilovec1b9732015-04-23 20:43:29 +0300420 msg_templ = "Error during postprocessing results: {0!s}"
421 raise RuntimeError(msg_templ.format(exc))
koder aka kdanilov66839a92015-04-11 13:22:31 +0300422
423 def merge_results(self, results):
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300424 if len(results) == 0:
425 return None
426
koder aka kdanilov66839a92015-04-11 13:22:31 +0300427 merged_result = results[0]
428 merged_data = merged_result['res']
429 expected_keys = set(merged_data.keys())
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300430 mergable_fields = ['bw', 'clat', 'iops', 'lat', 'slat']
koder aka kdanilov66839a92015-04-11 13:22:31 +0300431
432 for res in results[1:]:
433 assert res['__meta__'] == merged_result['__meta__']
434
435 data = res['res']
436 diff = set(data.keys()).symmetric_difference(expected_keys)
437
438 msg = "Difference: {0}".format(",".join(diff))
439 assert len(diff) == 0, msg
440
441 for testname, test_data in data.items():
442 res_test_data = merged_data[testname]
443
444 diff = set(test_data.keys()).symmetric_difference(
445 res_test_data.keys())
446
447 msg = "Difference: {0}".format(",".join(diff))
448 assert len(diff) == 0, msg
449
450 for k, v in test_data.items():
451 if k in mergable_fields:
452 res_test_data[k].extend(v)
453 else:
454 msg = "{0!r} != {1!r}".format(res_test_data[k], v)
455 assert res_test_data[k] == v, msg
456
457 return merged_result
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300458
459 @classmethod
460 def format_for_console(cls, data):
461 return io_formatter.format_results_for_console(data)