blob: 36d3fcfd1f502d0fc9bdce8b2b8a97ef71da5ab3 [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 kdanilova855f902015-04-26 14:31:45 +03009from paramiko import SSHException, SFTPError
Yulia Portnovab1a15072015-05-06 14:59:25 +030010import texttable
koder aka kdanilov783b4542015-04-23 18:57:04 +030011
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030012from wally.utils import (ssize2b, open_for_append_or_create,
koder aka kdanilove2de58c2015-04-24 22:59:36 +030013 sec_to_str, StopTestError)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030014
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030015from wally.ssh_utils import (copy_paths, run_over_ssh,
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030016 save_to_remote,
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030017 # delete_file,
koder aka kdanilov416b87a2015-05-12 00:26:04 +030018 connect, read_from_remote, Local,
19 exists)
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030020
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030021from . import postgres
Yulia Portnovab1a15072015-05-06 14:59:25 +030022from . import mysql
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030023from .io import agent as io_agent
24from .io import formatter as io_formatter
25from .io.results_loader import parse_output
koder aka kdanilov652cd802015-04-13 12:21:07 +030026
koder aka kdanilov4643fd62015-02-10 16:20:13 -080027
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030028logger = logging.getLogger("wally")
koder aka kdanilove21d7472015-02-14 19:02:04 -080029
30
koder aka kdanilov4643fd62015-02-10 16:20:13 -080031class IPerfTest(object):
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030032 def __init__(self, options, is_primary, on_result_cb, test_uuid, node,
koder aka kdanilov2066daf2015-04-23 21:05:41 +030033 log_directory=None,
34 coordination_queue=None,
35 remote_dir="/tmp/wally"):
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030036 self.options = options
koder aka kdanilov4643fd62015-02-10 16:20:13 -080037 self.on_result_cb = on_result_cb
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030038 self.log_directory = log_directory
39 self.node = node
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030040 self.test_uuid = test_uuid
koder aka kdanilovec1b9732015-04-23 20:43:29 +030041 self.coordination_queue = coordination_queue
koder aka kdanilov2066daf2015-04-23 21:05:41 +030042 self.remote_dir = remote_dir
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030043 self.is_primary = is_primary
koder aka kdanilove2de58c2015-04-24 22:59:36 +030044 self.stop_requested = False
45
46 def request_stop(self):
47 self.stop_requested = True
koder aka kdanilov2066daf2015-04-23 21:05:41 +030048
49 def join_remote(self, path):
50 return os.path.join(self.remote_dir, path)
koder aka kdanilovec1b9732015-04-23 20:43:29 +030051
52 def coordinate(self, data):
53 if self.coordination_queue is not None:
koder aka kdanilove2de58c2015-04-24 22:59:36 +030054 self.coordination_queue.put((self.node.get_conn_id(), data))
koder aka kdanilov4643fd62015-02-10 16:20:13 -080055
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030056 def pre_run(self):
koder aka kdanilov4643fd62015-02-10 16:20:13 -080057 pass
58
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030059 def cleanup(self):
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030060 pass
61
koder aka kdanilov4643fd62015-02-10 16:20:13 -080062 @abc.abstractmethod
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030063 def run(self, barrier):
koder aka kdanilov4643fd62015-02-10 16:20:13 -080064 pass
65
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030066 @classmethod
67 def format_for_console(cls, data):
68 msg = "{0}.format_for_console".format(cls.__name__)
69 raise NotImplementedError(msg)
70
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030071 def run_over_ssh(self, cmd, **kwargs):
72 return run_over_ssh(self.node.connection, cmd,
73 node=self.node.get_conn_id(), **kwargs)
74
koder aka kdanilovec1b9732015-04-23 20:43:29 +030075 @classmethod
76 def coordination_th(cls, coord_q, barrier, num_threads):
77 pass
78
koder aka kdanilov4643fd62015-02-10 16:20:13 -080079
Yulia Portnova7ddfa732015-02-24 17:32:58 +020080class TwoScriptTest(IPerfTest):
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030081 def __init__(self, *dt, **mp):
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030082 IPerfTest.__init__(self, *dt, **mp)
Yulia Portnova7ddfa732015-02-24 17:32:58 +020083
Yulia Portnovab1a15072015-05-06 14:59:25 +030084 if 'scripts_path' in self.options:
85 self.root = self.options['scripts_path']
koder aka kdanilovabd6ead2015-04-24 02:03:07 +030086 self.run_script = self.options['run_script']
Yulia Portnovab1a15072015-05-06 14:59:25 +030087 self.prerun_script = self.options['prerun_script']
Yulia Portnova7ddfa732015-02-24 17:32:58 +020088
89 def get_remote_for_script(self, script):
Yulia Portnovab1a15072015-05-06 14:59:25 +030090 return os.path.join(self.remote_dir, script.rpartition('/')[2])
Yulia Portnova7ddfa732015-02-24 17:32:58 +020091
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030092 def pre_run(self):
Yulia Portnovab1a15072015-05-06 14:59:25 +030093 copy_paths(self.node.connection, {self.root: self.remote_dir})
94 cmd = self.get_remote_for_script(self.pre_run_script)
95 self.run_over_ssh(cmd, timeout=2000)
Yulia Portnova7ddfa732015-02-24 17:32:58 +020096
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030097 def run(self, barrier):
Yulia Portnovab1a15072015-05-06 14:59:25 +030098 remote_script = self.get_remote_for_script(self.run_script)
Yulia Portnova886a2562015-04-07 11:16:13 +030099 cmd_opts = ' '.join(["%s %s" % (key, val) for key, val
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300100 in self.options.items()])
Yulia Portnova886a2562015-04-07 11:16:13 +0300101 cmd = remote_script + ' ' + cmd_opts
Yulia Portnovab1a15072015-05-06 14:59:25 +0300102 out_err = self.run_over_ssh(cmd, timeout=6000)
koder aka kdanilov66839a92015-04-11 13:22:31 +0300103 self.on_result(out_err, cmd)
Yulia Portnova7ddfa732015-02-24 17:32:58 +0200104
105 def parse_results(self, out):
106 for line in out.split("\n"):
107 key, separator, value = line.partition(":")
108 if key and value:
109 self.on_result_cb((key, float(value)))
110
koder aka kdanilov66839a92015-04-11 13:22:31 +0300111 def on_result(self, out_err, cmd):
112 try:
113 self.parse_results(out_err)
114 except Exception as exc:
koder aka kdanilovec1b9732015-04-23 20:43:29 +0300115 msg_templ = "Error during postprocessing results: {0!s}. {1}"
116 raise RuntimeError(msg_templ.format(exc, out_err))
Yulia Portnova7ddfa732015-02-24 17:32:58 +0200117
Yulia Portnovab1a15072015-05-06 14:59:25 +0300118 def merge_results(self, results):
119 tpcm = sum([val[1] for val in results])
120 return {"res": {"TpmC": tpcm}}
121
Yulia Portnova7ddfa732015-02-24 17:32:58 +0200122
123class PgBenchTest(TwoScriptTest):
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300124 root = os.path.dirname(postgres.__file__)
Yulia Portnovab1a15072015-05-06 14:59:25 +0300125 pre_run_script = os.path.join(root, "prepare.sh")
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300126 run_script = os.path.join(root, "run.sh")
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300127
Yulia Portnova1f123962015-05-06 18:48:11 +0300128 @classmethod
129 def format_for_console(cls, data):
130 tab = texttable.Texttable(max_width=120)
131 tab.set_deco(tab.HEADER | tab.VLINES | tab.BORDER)
132 tab.header(["TpmC"])
133 tab.add_row([data['res']['TpmC']])
134 return tab.draw()
135
136
Yulia Portnovab1a15072015-05-06 14:59:25 +0300137class MysqlTest(TwoScriptTest):
138 root = os.path.dirname(mysql.__file__)
139 pre_run_script = os.path.join(root, "prepare.sh")
140 run_script = os.path.join(root, "run.sh")
141
142 @classmethod
143 def format_for_console(cls, data):
144 tab = texttable.Texttable(max_width=120)
145 tab.set_deco(tab.HEADER | tab.VLINES | tab.BORDER)
146 tab.header(["TpmC"])
147 tab.add_row([data['res']['TpmC']])
148 return tab.draw()
149
150
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800151class IOPerfTest(IPerfTest):
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300152 tcp_conn_timeout = 30
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300153 max_pig_timeout = 5
154 soft_runcycle = 5 * 60
koder aka kdanilov2c473092015-03-29 17:12:13 +0300155
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300156 def __init__(self, *dt, **mp):
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300157 IPerfTest.__init__(self, *dt, **mp)
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300158 self.config_fname = self.options['cfg']
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300159
160 if '/' not in self.config_fname and '.' not in self.config_fname:
161 cfgs_dir = os.path.dirname(io_agent.__file__)
162 self.config_fname = os.path.join(cfgs_dir,
163 self.config_fname + '.cfg')
164
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300165 self.alive_check_interval = self.options.get('alive_check_interval')
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300166
167 self.config_params = {}
168 for name, val in self.options.get('params', {}).items():
169 if isinstance(val, (list, tuple)):
170 val = "{%" + ','.join(map(str, val)) + "%}"
171 self.config_params[name] = val
172
173 self.config_params['VM_COUNT'] = self.options['testnodes_count']
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300174 self.tool = self.options.get('tool', 'fio')
koder aka kdanilovda45e882015-04-06 02:24:42 +0300175 self.raw_cfg = open(self.config_fname).read()
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300176 self.configs = list(io_agent.parse_all_in_1(self.raw_cfg,
177 self.config_params))
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800178
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300179 cmd_log = os.path.join(self.log_directory, "task_compiled.cfg")
180 raw_res = os.path.join(self.log_directory, "raw_results.txt")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300181
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300182 self.io_py_remote = self.join_remote("agent.py")
183 self.log_fl = self.join_remote("log.txt")
184 self.pid_file = self.join_remote("pid")
185 self.task_file = self.join_remote("task.cfg")
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300186 self.use_sudo = self.options.get("use_sudo", True)
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300187 self.test_logging = self.options.get("test_logging", False)
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300188
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300189 fio_command_file = open_for_append_or_create(cmd_log)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300190
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300191 if self.test_logging:
192 soft_runcycle = self.soft_runcycle
193 else:
194 soft_runcycle = None
195
196 self.fio_configs = io_agent.parse_and_slice_all_in_1(
197 self.raw_cfg,
198 self.config_params,
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300199 soft_runcycle=soft_runcycle,
200 split_on_names=self.test_logging)
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300201
202 self.fio_configs = list(self.fio_configs)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300203 splitter = "\n\n" + "-" * 60 + "\n\n"
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300204
205 cfg = splitter.join(
206 map(io_agent.fio_config_to_str,
207 self.fio_configs))
208
209 fio_command_file.write(cfg)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300210 self.fio_raw_results_file = open_for_append_or_create(raw_res)
211
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300212 def __str__(self):
213 return "{0}({1})".format(self.__class__.__name__,
214 self.node.get_conn_id())
215
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300216 def cleanup(self):
217 # delete_file(conn, self.io_py_remote)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300218 # Need to remove tempo files, used for testing
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300219 pass
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300220
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300221 def prefill_test_files(self):
222 files = {}
223
224 for section in self.configs:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300225 sz = ssize2b(section.vals['size'])
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300226 msz = sz / (1024 ** 2)
227
228 if sz % (1024 ** 2) != 0:
229 msz += 1
230
231 fname = section.vals['filename']
232
233 # if already has other test with the same file name
234 # take largest size
235 files[fname] = max(files.get(fname, 0), msz)
236
237 cmd_templ = "dd oflag=direct " + \
238 "if=/dev/zero of={0} bs={1} count={2}"
239
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300240 # cmd_templ = "fio --rw=write --bs={1} --direct=1 --size={2} "
241
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300242 if self.use_sudo:
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300243 cmd_templ = "sudo " + cmd_templ
244
245 ssize = 0
246 stime = time.time()
247
248 for fname, curr_sz in files.items():
249 cmd = cmd_templ.format(fname, 1024 ** 2, curr_sz)
250 ssize += curr_sz
251 self.run_over_ssh(cmd, timeout=curr_sz)
252
253 ddtime = time.time() - stime
254 if ddtime > 1E-3:
255 fill_bw = int(ssize / ddtime)
256 mess = "Initiall dd fill bw is {0} MiBps for this vm"
257 logger.info(mess.format(fill_bw))
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300258 self.coordinate(('init_bw', fill_bw))
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300259
260 def install_utils(self, max_retry=3, timeout=5):
261 need_install = []
262 for bin_name, package in (('fio', 'fio'), ('screen', 'screen')):
263 try:
264 self.run_over_ssh('which ' + bin_name, nolog=True)
265 except OSError:
266 need_install.append(package)
267
koder aka kdanilovafd98742015-04-24 01:27:22 +0300268 if len(need_install) == 0:
269 return
270
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300271 cmd = "sudo apt-get -y install " + " ".join(need_install)
272
273 for i in range(max_retry):
274 try:
275 self.run_over_ssh(cmd)
276 break
277 except OSError as err:
278 time.sleep(timeout)
279 else:
280 raise OSError("Can't install - " + str(err))
281
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300282 def pre_run(self):
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300283 try:
284 cmd = 'mkdir -p "{0}"'.format(self.remote_dir)
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300285 if self.use_sudo:
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300286 cmd = "sudo " + cmd
287 cmd += " ; sudo chown {0} {1}".format(self.node.get_user(),
288 self.remote_dir)
289
290 self.run_over_ssh(cmd)
291 except Exception as exc:
292 msg = "Failed to create folder {0} on remote {1}. Error: {2!s}"
293 msg = msg.format(self.remote_dir, self.node.get_conn_id(), exc)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300294 logger.exception(msg)
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300295 raise StopTestError(msg, exc)
koder aka kdanilov783b4542015-04-23 18:57:04 +0300296
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300297 self.install_utils()
koder aka kdanilovda45e882015-04-06 02:24:42 +0300298
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300299 local_fname = os.path.splitext(io_agent.__file__)[0] + ".py"
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300300 files_to_copy = {local_fname: self.io_py_remote}
301 copy_paths(self.node.connection, files_to_copy)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800302
koder aka kdanilove87ae652015-04-20 02:14:35 +0300303 if self.options.get('prefill_files', True):
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300304 self.prefill_test_files()
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300305 elif self.is_primary:
koder aka kdanilov2066daf2015-04-23 21:05:41 +0300306 logger.warning("Prefilling of test files is disabled")
koder aka kdanilov6e2ae792015-03-04 18:02:24 -0800307
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300308 def check_process_is_running(self, sftp, pid):
309 try:
310 sftp.stat("/proc/{0}".format(pid))
311 return True
koder aka kdanilova855f902015-04-26 14:31:45 +0300312 except (OSError, IOError, NameError):
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300313 return False
314
315 def kill_remote_process(self, conn, pid, soft=True):
316 try:
317 if soft:
318 cmd = "kill {0}"
319 else:
320 cmd = "kill -9 {0}"
321
322 if self.use_sudo:
323 cmd = "sudo " + cmd
324
325 self.run_over_ssh(cmd.format(pid))
326 return True
327 except OSError:
328 return False
329
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300330 def get_test_status(self, res_file=None):
331 found_res_file = False
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300332 is_connected = None
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300333 is_running = None
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300334 pid = None
335 err = None
336
337 try:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300338 # conn = connect(self.node.conn_url,
339 # conn_timeout=self.tcp_conn_timeout)
340 # with conn:
341 conn = self.node.connection
342 with conn.open_sftp() as sftp:
343 try:
344 pid = read_from_remote(sftp, self.pid_file)
345 is_running = True
346 except (NameError, IOError, OSError) as exc:
347 pid = None
348 is_running = False
349
350 if is_running:
351 if not self.check_process_is_running(sftp, pid):
352 try:
353 sftp.remove(self.pid_file)
354 except (IOError, NameError, OSError):
355 pass
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300356 is_running = False
357
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300358 if res_file is not None:
359 found_res_file = exists(sftp, res_file)
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300360
361 is_connected = True
362
koder aka kdanilova855f902015-04-26 14:31:45 +0300363 except (socket.error, SSHException, EOFError, SFTPError) as exc:
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300364 err = str(exc)
365 is_connected = False
366
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300367 return found_res_file, is_connected, is_running, pid, err
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300368
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300369 def wait_till_finished(self, soft_timeout, timeout, res_fname=None):
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300370 conn_id = self.node.get_conn_id()
371 end_of_wait_time = timeout + time.time()
koder aka kdanilova855f902015-04-26 14:31:45 +0300372 soft_end_of_wait_time = soft_timeout + time.time()
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300373
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300374 time_till_check = random.randint(5, 10)
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300375 pid = None
koder aka kdanilove2de58c2015-04-24 22:59:36 +0300376 is_running = False
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300377 pid_get_timeout = self.max_pig_timeout + time.time()
378 curr_connected = True
379
380 while end_of_wait_time > time.time():
381 time.sleep(time_till_check)
382
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300383 found_res_file, is_connected, is_running, npid, err = \
384 self.get_test_status(res_fname)
385
386 if found_res_file and not is_running:
387 return
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300388
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300389 if is_connected and not is_running:
390 if pid is None:
391 if time.time() > pid_get_timeout:
392 msg = ("On node {0} pid file doesn't " +
393 "appears in time")
394 logger.error(msg.format(conn_id))
395 raise StopTestError("Start timeout")
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300396 else:
397 # execution finished
398 break
399
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300400 if npid is not None:
401 pid = npid
402
koder aka kdanilova855f902015-04-26 14:31:45 +0300403 if is_connected and pid is not None and is_running:
404 if time.time() < soft_end_of_wait_time:
405 time.sleep(soft_end_of_wait_time - time.time())
406
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300407 if is_connected and not curr_connected:
408 msg = "Connection with {0} is restored"
409 logger.debug(msg.format(conn_id))
410 elif not is_connected and curr_connected:
411 msg = "Lost connection with " + conn_id + ". Error: " + err
412 logger.debug(msg)
413
414 curr_connected = is_connected
415
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300416 def run(self, barrier):
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300417 try:
koder aka kdanilova323b302015-04-26 00:40:22 +0300418 if len(self.fio_configs) > 1 and self.is_primary:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300419
420 exec_time = 0
421 for test in self.fio_configs:
422 exec_time += io_agent.calculate_execution_time(test)
423
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300424 # +5% - is a rough estimation for additional operations
425 # like sftp, etc
426 exec_time = int(exec_time * 1.05)
427
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300428 exec_time_s = sec_to_str(exec_time)
koder aka kdanilova855f902015-04-26 14:31:45 +0300429 now_dt = datetime.datetime.now()
430 end_dt = now_dt + datetime.timedelta(0, exec_time)
431 msg = "Entire test should takes aroud: {0} and finished at {1}"
432 logger.info(msg.format(exec_time_s,
433 end_dt.strftime("%H:%M:%S")))
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300434
435 for pos, fio_cfg_slice in enumerate(self.fio_configs):
436 names = [i.name for i in fio_cfg_slice]
437 msgs = []
438 already_processed = set()
439 for name in names:
440 if name not in already_processed:
441 already_processed.add(name)
442
443 if 1 == names.count(name):
444 msgs.append(name)
445 else:
446 frmt = "{0} * {1}"
447 msgs.append(frmt.format(name,
448 names.count(name)))
449
koder aka kdanilova323b302015-04-26 00:40:22 +0300450 if self.is_primary:
451 logger.info("Will run tests: " + ", ".join(msgs))
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300452
koder aka kdanilova323b302015-04-26 00:40:22 +0300453 nolog = (pos != 0) or not self.is_primary
454 out_err = self.do_run(barrier, fio_cfg_slice, nolog=nolog)
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300455
456 try:
457 for data in parse_output(out_err):
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300458 self.on_result_cb(data)
459 except (OSError, StopTestError):
460 raise
461 except Exception as exc:
462 msg_templ = "Error during postprocessing results: {0!s}"
463 raise RuntimeError(msg_templ.format(exc))
464
465 finally:
466 barrier.exit()
467
468 def do_run(self, barrier, cfg, nolog=False):
koder aka kdanilovabd6ead2015-04-24 02:03:07 +0300469 conn_id = self.node.get_conn_id()
470
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300471 cmd_templ = "screen -S {screen_name} -d -m " + \
472 "env python2 {0} -p {pid_file} -o {results_file} " + \
473 "--type {1} {2} --json {3}"
474
475 if self.options.get("use_sudo", True):
476 cmd_templ = "sudo " + cmd_templ
koder aka kdanilov66839a92015-04-11 13:22:31 +0300477
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300478 params = []
479 for k, v in self.config_params.items():
480 if isinstance(v, basestring) and v.startswith("{%"):
481 continue
482 params.append("{0}={1}".format(k, v))
koder aka kdanilov66839a92015-04-11 13:22:31 +0300483
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300484 if [] != params:
485 params = "--params " + " ".join(params)
koder aka kdanilov66839a92015-04-11 13:22:31 +0300486
koder aka kdanilov783b4542015-04-23 18:57:04 +0300487 with self.node.connection.open_sftp() as sftp:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300488 save_to_remote(sftp, self.task_file,
489 io_agent.fio_config_to_str(cfg))
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300490
491 screen_name = self.test_uuid
492 cmd = cmd_templ.format(self.io_py_remote,
493 self.tool,
494 params,
495 self.task_file,
496 pid_file=self.pid_file,
497 results_file=self.log_fl,
498 screen_name=screen_name)
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300499
500 exec_time = io_agent.calculate_execution_time(cfg)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300501 exec_time_str = sec_to_str(exec_time)
502
koder aka kdanilova855f902015-04-26 14:31:45 +0300503 timeout = int(exec_time + max(300, exec_time))
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300504 soft_tout = exec_time
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300505 barrier.wait()
koder aka kdanilova323b302015-04-26 00:40:22 +0300506 self.run_over_ssh(cmd, nolog=nolog)
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300507 if self.is_primary:
508 templ = "Test should takes about {0}." + \
509 " Should finish at {1}," + \
510 " will wait at most till {2}"
511 now_dt = datetime.datetime.now()
512 end_dt = now_dt + datetime.timedelta(0, exec_time)
513 wait_till = now_dt + datetime.timedelta(0, timeout)
koder aka kdanilovea22c3d2015-04-21 03:42:22 +0300514
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300515 logger.info(templ.format(exec_time_str,
516 end_dt.strftime("%H:%M:%S"),
517 wait_till.strftime("%H:%M:%S")))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300518
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300519 if not nolog:
520 msg = "Tests started in screen {1} on each testnode"
521 logger.debug(msg.format(conn_id, screen_name))
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300522
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300523 # TODO: add monitoring socket
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300524 # if not isinstance(self.node.connection, Local):
525 # self.node.connection.close()
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300526
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300527 self.wait_till_finished(soft_tout, timeout, self.log_fl)
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300528 if not nolog:
529 logger.debug("Test on node {0} is finished".format(conn_id))
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300530
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300531 # if self.node.connection is not Local:
532 # conn_timeout = self.tcp_conn_timeout * 3
533 # self.node.connection = connect(self.node.conn_url,
534 # conn_timeout=conn_timeout)
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300535
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300536 with self.node.connection.open_sftp() as sftp:
537 return read_from_remote(sftp, self.log_fl)
koder aka kdanilov66839a92015-04-11 13:22:31 +0300538
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300539 @classmethod
540 def merge_results(cls, results):
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300541 merged = results[0]
542 for block in results[1:]:
543 assert block["__meta__"] == merged["__meta__"]
544 merged['res'].extend(block['res'])
545 return merged
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300546
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300547 # @classmethod
548 # def merge_results(cls, results):
549 # if len(results) == 0:
550 # return None
koder aka kdanilov66839a92015-04-11 13:22:31 +0300551
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300552 # merged_result = results[0]
553 # merged_data = merged_result['res']
554 # mergable_fields = ['bw', 'clat', 'iops', 'lat', 'slat']
koder aka kdanilov66839a92015-04-11 13:22:31 +0300555
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300556 # for res in results[1:]:
557 # mm = merged_result['__meta__']
558 # assert mm['raw_cfg'] == res['__meta__']['raw_cfg']
559 # assert mm['params'] == res['__meta__']['params']
560 # mm['timings'].extend(res['__meta__']['timings'])
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300561
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300562 # data = res['res']
563 # for testname, test_data in data.items():
564 # if testname not in merged_data:
565 # merged_data[testname] = test_data
566 # continue
koder aka kdanilov66839a92015-04-11 13:22:31 +0300567
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300568 # res_test_data = merged_data[testname]
koder aka kdanilov66839a92015-04-11 13:22:31 +0300569
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300570 # diff = set(test_data.keys()).symmetric_difference(
571 # res_test_data.keys())
koder aka kdanilov66839a92015-04-11 13:22:31 +0300572
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300573 # msg = "Difference: {0}".format(",".join(diff))
574 # assert len(diff) == 0, msg
koder aka kdanilov66839a92015-04-11 13:22:31 +0300575
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300576 # for k, v in test_data.items():
577 # if k in mergable_fields:
578 # res_test_data[k].extend(v)
579 # else:
580 # msg = "{0!r} != {1!r}".format(res_test_data[k], v)
581 # assert res_test_data[k] == v, msg
582
583 # return merged_result
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300584
585 @classmethod
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300586 def format_for_console(cls, data, dinfo):
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300587 return io_formatter.format_results_for_console(dinfo)