blob: 1b5f38e37682459070a7a152ad6fa54a9a0d275a [file] [log] [blame]
koder aka kdanilovbc2c8982015-06-13 02:50:43 +03001import os.path
2import logging
koder aka kdanilov70227062016-11-26 23:23:21 +02003from typing import Dict, List, Union, 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 kdanilov70227062016-11-26 23:23:21 +02007from ...utils import ssize2b, StopTestError, get_os
8from ...node_interfaces import IRPCNode
9from ..itest import ThreadedTest, IterationConfig, RunTestRes
10from .fio_task_parser import execution_time, fio_cfg_compile, FioJobSection, FioParams
koder aka kdanilovf236b9c2015-06-24 18:17:22 +030011
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 kdanilov70227062016-11-26 23:23:21 +020026 self.load_profile_name = self.config.params['load'] # type: str
27 self.name = "io." + self.load_profile_name
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030028
koder aka kdanilov70227062016-11-26 23:23:21 +020029 if os.path.isfile(self.load_profile_name):
30 self.load_profile_path = os.path.join(self.configs_dir, self.load_profile_name+ '.cfg') # type: str
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030031 else:
koder aka kdanilov70227062016-11-26 23:23:21 +020032 self.load_profile_path = self.load_profile_name
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030033
koder aka kdanilov70227062016-11-26 23:23:21 +020034 self.load_profile = open(self.load_profile_path, 'rt').read() # type: str
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030035
koder aka kdanilov70227062016-11-26 23:23:21 +020036 self.use_system_fio = get('use_system_fio', False) # type: bool
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030037
38 if self.use_system_fio:
koder aka kdanilov70227062016-11-26 23:23:21 +020039 self.fio_path = "fio" # type: str
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030040 else:
koder aka kdanilov70227062016-11-26 23:23:21 +020041 self.fio_path = os.path.join(self.config.remote_dir, "fio")
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030042
koder aka kdanilov70227062016-11-26 23:23:21 +020043 self.force_prefill = get('force_prefill', False) # type: bool
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030044
koder aka kdanilov70227062016-11-26 23:23:21 +020045 if 'FILESIZE' not in self.config.params:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030046 raise NotImplementedError("File size detection is not implemented")
koder aka kdanilova94dfe12015-08-19 13:04:51 +030047
koder aka kdanilov70227062016-11-26 23:23:21 +020048 # self.max_latency = get("max_lat") # type: Optional[int]
49 # self.min_bw_per_thread = get("min_bw") # type: Optional[int]
koder aka kdanilova94dfe12015-08-19 13:04:51 +030050
koder aka kdanilov70227062016-11-26 23:23:21 +020051 self.use_sudo = get("use_sudo", True) # type: bool
52
53 self.fio_configs = list(fio_cfg_compile(self.load_profile,
54 self.load_profile_path,
55 cast(FioParams, self.config.params)))
56
57 if len(self.fio_configs) == 0:
58 logger.exception("Empty fio config provided")
59 raise StopTestError("Empty fio config provided")
60
61 self.iterations_configs = self.fio_configs # type: ignore
62 self.files_sizes = self.get_file_sizes()
63
64 self.exec_folder = self.config.remote_dir
65 self.fio_path = "" if self.use_system_fio else self.exec_folder
66
67 def get_file_sizes(self) -> Dict[str, int]:
68 files_sizes = {} # type: Dict[str, int]
69
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030070 for section in self.fio_configs:
71 sz = ssize2b(section.vals['size'])
koder aka kdanilov70227062016-11-26 23:23:21 +020072 msz = sz // (1024 ** 2) + (1 if sz % (1024 ** 2) != 0 else 0)
73 fname = section.vals['filename'] # type: str
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030074
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030075 # if already has other test with the same file name
76 # take largest size
koder aka kdanilov70227062016-11-26 23:23:21 +020077 files_sizes[fname] = max(files_sizes.get(fname, 0), msz)
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030078
koder aka kdanilov70227062016-11-26 23:23:21 +020079 return files_sizes
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 kdanilovbc2c8982015-06-13 02:50:43 +030082 try:
koder aka kdanilov70227062016-11-26 23:23:21 +020083 node.conn.rmdir(self.config.remote_dir, recursive=True, ignore_missing=True)
84 node.conn.mkdir(self.config.remote_dir)
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030085 except Exception as exc:
koder aka kdanilov39e449e2016-12-17 15:15:26 +020086 msg = "Failed to create folder {} on remote {}.".format(self.config.remote_dir, node)
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030087 logger.exception(msg)
88 raise StopTestError(msg) from exc
koder aka kdanilovbc2c8982015-06-13 02:50:43 +030089
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030090 self.install_utils(node)
koder aka kdanilov70227062016-11-26 23:23:21 +020091 logger.info("Prefilling test files with random data")
92 fill_bw = node.conn.prefill_test_files(self.files_sizes, force=self.force_prefill, fio_path=self.fio_path)
93 if fill_bw is not None:
94 logger.info("Initial fio fill bw is {} MiBps for {}".format(fill_bw, node.info.node_id()))
koder aka kdanilov8fbb27f2015-07-17 22:23:31 +030095
koder aka kdanilov70227062016-11-26 23:23:21 +020096 def install_utils(self, node: IRPCNode) -> None:
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030097 if self.use_system_fio:
koder aka kdanilov70227062016-11-26 23:23:21 +020098 node.conn.install('fio', binary='fio')
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030099
koder aka kdanilov70227062016-11-26 23:23:21 +0200100 if not self.use_system_fio:
101 os_info = get_os(node)
102 fio_dir = os.path.dirname(os.path.dirname(wally.__file__)) # type: str
103 fio_dir = os.path.join(os.getcwd(), fio_dir)
104 fio_dir = os.path.join(fio_dir, 'fio_binaries')
105 fname = 'fio_{0.release}_{0.arch}.bz2'.format(os_info)
106 fio_path = os.path.join(fio_dir, fname) # type: str
107
108 if not os.path.exists(fio_path):
109 raise RuntimeError("No prebuild fio binary available for {0}".format(os_info))
110
111 bz_dest = self.join_remote('fio.bz2') # type: str
112 node.copy_file(fio_path, bz_dest)
113 node.run("bzip2 --decompress {}" + bz_dest)
114 node.run("chmod a+x " + self.join_remote("fio"))
115
116 def get_expected_runtime(self, iteration_info: IterationConfig) -> int:
117 return execution_time(cast(FioJobSection, iteration_info))
118
119 def do_test(self, node: IRPCNode, iter_config: IterationConfig) -> RunTestRes:
120 exec_time = execution_time(cast(FioJobSection, iter_config))
121 raw_res = node.conn.fio.run_fio(self.fio_path,
122 self.exec_folder,
123 str(cast(FioJobSection, iter_config)),
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300124 exec_time + max(300, exec_time))
koder aka kdanilov70227062016-11-26 23:23:21 +0200125 # TODO(koder): fix next error
126 raise NotImplementedError("Need to extract time from test result")
127 return raw_res, (0, 0)
koder aka kdanilovbc2c8982015-06-13 02:50:43 +0300128