blob: e055d98bd5dad6e46b82a332f84796ee5936c4db [file] [log] [blame]
koder aka kdanilovbc2c8982015-06-13 02:50:43 +03001import os.path
2import logging
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +02003from typing import cast
koder aka kdanilovbc2c8982015-06-13 02:50:43 +03004
koder aka kdanilov6ab4d432015-06-22 00:26:28 +03005import wally
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +03006
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +02007from ...utils import StopTestError, get_os, ssize2b
koder aka kdanilov70227062016-11-26 23:23:21 +02008from ...node_interfaces import IRPCNode
9from ..itest import ThreadedTest, IterationConfig, RunTestRes
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020010from .fio_task_parser import execution_time, fio_cfg_compile, FioJobSection, FioParams, get_log_files
11from . import rpc_plugin
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030012
13logger = logging.getLogger("wally")
14
15
koder aka kdanilov70227062016-11-26 23:23:21 +020016class IOPerfTest(ThreadedTest):
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030017 soft_runcycle = 5 * 60
Michael Semenov8ba6e232015-08-28 10:57:18 +000018 retry_time = 30
koder aka kdanilov70227062016-11-26 23:23:21 +020019 configs_dir = os.path.dirname(__file__) # type: str
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030020
koder aka kdanilov70227062016-11-26 23:23:21 +020021 def __init__(self, *args, **kwargs) -> None:
22 super().__init__(*args, **kwargs)
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030023
24 get = self.config.params.get
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030025
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020026 self.remote_task_file = self.join_remote("task.fio")
27 self.remote_output_file = self.join_remote("fio_result.json")
28 self.use_system_fio = get('use_system_fio', False) # type: bool
29 self.use_sudo = get("use_sudo", True) # type: bool
30 self.force_prefill = get('force_prefill', False) # type: bool
31
koder aka kdanilov70227062016-11-26 23:23:21 +020032 self.load_profile_name = self.config.params['load'] # type: str
33 self.name = "io." + self.load_profile_name
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030034
koder aka kdanilov70227062016-11-26 23:23:21 +020035 if os.path.isfile(self.load_profile_name):
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020036 self.load_profile_path = self.load_profile_name # type: str
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030037 else:
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020038 self.load_profile_path = os.path.join(self.configs_dir, self.load_profile_name+ '.cfg')
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030039
koder aka kdanilov70227062016-11-26 23:23:21 +020040 self.load_profile = open(self.load_profile_path, 'rt').read() # type: str
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030041
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030042 if self.use_system_fio:
koder aka kdanilov70227062016-11-26 23:23:21 +020043 self.fio_path = "fio" # type: str
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030044 else:
koder aka kdanilov70227062016-11-26 23:23:21 +020045 self.fio_path = os.path.join(self.config.remote_dir, "fio")
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030046
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020047 self.load_params = self.config.params['params']
48 self.file_name = self.load_params['FILENAME']
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030049
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020050 if 'FILESIZE' not in self.load_params:
51 logger.debug("Getting test file sizes on all nodes")
52 try:
53 sizes = {node.conn.fs.file_stat(self.file_name)['size']
54 for node in self.config.nodes}
55 except Exception:
56 logger.exception("FILESIZE is not set in config file and fail to detect it." +
57 "Set FILESIZE or fix error and rerun test")
58 raise StopTestError()
koder aka kdanilova94dfe12015-08-19 13:04:51 +030059
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020060 if len(sizes) != 1:
61 logger.error("IO target file %r has different sizes on test nodes - %r",
62 self.file_name, sizes)
63 raise StopTestError()
koder aka kdanilova94dfe12015-08-19 13:04:51 +030064
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020065 self.file_size = list(sizes)[0]
66 logger.info("Detected test file size is %s", self.file_size)
67 self.load_params['FILESIZE'] = self.file_size
68 else:
69 self.file_size = ssize2b(self.load_params['FILESIZE'])
koder aka kdanilov70227062016-11-26 23:23:21 +020070
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020071 self.fio_configs = list(fio_cfg_compile(self.load_profile, self.load_profile_path,
72 cast(FioParams, self.load_params)))
koder aka kdanilov70227062016-11-26 23:23:21 +020073
74 if len(self.fio_configs) == 0:
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020075 logger.error("Empty fio config provided")
76 raise StopTestError()
koder aka kdanilov70227062016-11-26 23:23:21 +020077
78 self.iterations_configs = self.fio_configs # type: ignore
koder aka kdanilov70227062016-11-26 23:23:21 +020079 self.exec_folder = self.config.remote_dir
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030080
koder aka kdanilov70227062016-11-26 23:23:21 +020081 def config_node(self, node: IRPCNode) -> None:
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020082 plugin_code = open(rpc_plugin.__file__.rsplit(".", 1)[0] + ".py", "rb").read()
83 node.upload_plugin(code=plugin_code, name="fio")
84
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030085 try:
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020086 node.conn.fs.rmtree(self.config.remote_dir)
87 except Exception:
88 pass
89
90 try:
91 node.conn.fs.makedirs(self.config.remote_dir)
92 except Exception:
93 msg = "Failed to recreate folder {} on remote {}.".format(self.config.remote_dir, node)
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030094 logger.exception(msg)
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020095 raise StopTestError()
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030096
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030097 self.install_utils(node)
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020098
99 mb = int(self.file_size / 1024 ** 2)
100 logger.info("Filling test file %s with %sMiB of random data", self.file_name, mb)
101 fill_bw = node.conn.fio.fill_file(self.file_name, mb, force=self.force_prefill, fio_path=self.fio_path)
koder aka kdanilov70227062016-11-26 23:23:21 +0200102 if fill_bw is not None:
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +0200103 logger.info("Initial fio fill bw is {} MiBps for {}".format(fill_bw, node))
104
105 fio_config = "\n".join(map(str, self.iterations_configs))
106 node.put_to_file(self.remote_task_file, fio_config.encode("utf8"))
koder aka kdanilov8fbb27f2015-07-17 22:23:31 +0300107
koder aka kdanilov70227062016-11-26 23:23:21 +0200108 def install_utils(self, node: IRPCNode) -> None:
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +0200109 os_info = get_os(node)
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300110 if self.use_system_fio:
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +0200111 if os_info.distro != 'ubuntu':
112 logger.error("Only ubuntu supported on test VM")
113 raise StopTestError()
114 node.conn.fio.install('fio', binary='fio')
115 else:
116 node.conn.fio.install('bzip2', binary='bzip2')
koder aka kdanilov70227062016-11-26 23:23:21 +0200117 fio_dir = os.path.dirname(os.path.dirname(wally.__file__)) # type: str
118 fio_dir = os.path.join(os.getcwd(), fio_dir)
119 fio_dir = os.path.join(fio_dir, 'fio_binaries')
120 fname = 'fio_{0.release}_{0.arch}.bz2'.format(os_info)
121 fio_path = os.path.join(fio_dir, fname) # type: str
122
123 if not os.path.exists(fio_path):
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +0200124 logger.error("No prebuild fio binary available for {0}".format(os_info))
125 raise StopTestError()
koder aka kdanilov70227062016-11-26 23:23:21 +0200126
127 bz_dest = self.join_remote('fio.bz2') # type: str
128 node.copy_file(fio_path, bz_dest)
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +0200129 node.run("bzip2 --decompress {} ; chmod a+x {}".format(bz_dest, self.join_remote("fio")))
koder aka kdanilov70227062016-11-26 23:23:21 +0200130
131 def get_expected_runtime(self, iteration_info: IterationConfig) -> int:
132 return execution_time(cast(FioJobSection, iteration_info))
133
134 def do_test(self, node: IRPCNode, iter_config: IterationConfig) -> RunTestRes:
135 exec_time = execution_time(cast(FioJobSection, iter_config))
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +0200136 fio_cmd_templ = "cd {exec_folder}; " + \
137 "{fio_path} --output-format=json --output={out_file} --alloc-size=262144 {job_file}"
138
139 bw_log, iops_log, lat_hist_log = get_log_files(iter_config)
140
141 cmd = fio_cmd_templ.format(exec_folder=self.exec_folder,
142 fio_path=self.fio_path,
143 out_file=self.remote_output_file,
144 job_file=self.remote_task_file)
145 raw_res = node.run(cmd, timeout=exec_time + max(300, exec_time))
146
147 return
148
koder aka kdanilov70227062016-11-26 23:23:21 +0200149 # TODO(koder): fix next error
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +0200150 # raise NotImplementedError("Need to extract time from test result")
151 # return raw_res, (0, 0)
koder aka kdanilovbc2c8982015-06-13 02:50:43 +0300152