koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 1 | import os.path |
| 2 | import logging |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 3 | from typing import Dict, List, Union, cast |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 4 | |
koder aka kdanilov | 6ab4d43 | 2015-06-22 00:26:28 +0300 | [diff] [blame] | 5 | import wally |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame] | 6 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 7 | from ...utils import ssize2b, StopTestError, get_os |
| 8 | from ...node_interfaces import IRPCNode |
| 9 | from ..itest import ThreadedTest, IterationConfig, RunTestRes |
| 10 | from .fio_task_parser import execution_time, fio_cfg_compile, FioJobSection, FioParams |
koder aka kdanilov | f236b9c | 2015-06-24 18:17:22 +0300 | [diff] [blame] | 11 | |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 12 | |
| 13 | logger = logging.getLogger("wally") |
| 14 | |
| 15 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 16 | class IOPerfTest(ThreadedTest): |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 17 | soft_runcycle = 5 * 60 |
Michael Semenov | 8ba6e23 | 2015-08-28 10:57:18 +0000 | [diff] [blame] | 18 | retry_time = 30 |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 19 | configs_dir = os.path.dirname(__file__) # type: str |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 20 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 21 | def __init__(self, *args, **kwargs) -> None: |
| 22 | super().__init__(*args, **kwargs) |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 23 | |
| 24 | get = self.config.params.get |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 25 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 26 | self.load_profile_name = self.config.params['load'] # type: str |
| 27 | self.name = "io." + self.load_profile_name |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 28 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 29 | 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 kdanilov | 6ab4d43 | 2015-06-22 00:26:28 +0300 | [diff] [blame] | 31 | else: |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 32 | self.load_profile_path = self.load_profile_name |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 33 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 34 | self.load_profile = open(self.load_profile_path, 'rt').read() # type: str |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 35 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 36 | self.use_system_fio = get('use_system_fio', False) # type: bool |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 37 | |
| 38 | if self.use_system_fio: |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 39 | self.fio_path = "fio" # type: str |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 40 | else: |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 41 | self.fio_path = os.path.join(self.config.remote_dir, "fio") |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 42 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 43 | self.force_prefill = get('force_prefill', False) # type: bool |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 44 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 45 | if 'FILESIZE' not in self.config.params: |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame] | 46 | raise NotImplementedError("File size detection is not implemented") |
koder aka kdanilov | a94dfe1 | 2015-08-19 13:04:51 +0300 | [diff] [blame] | 47 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 48 | # self.max_latency = get("max_lat") # type: Optional[int] |
| 49 | # self.min_bw_per_thread = get("min_bw") # type: Optional[int] |
koder aka kdanilov | a94dfe1 | 2015-08-19 13:04:51 +0300 | [diff] [blame] | 50 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 51 | 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 kdanilov | 6ab4d43 | 2015-06-22 00:26:28 +0300 | [diff] [blame] | 70 | for section in self.fio_configs: |
| 71 | sz = ssize2b(section.vals['size']) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 72 | msz = sz // (1024 ** 2) + (1 if sz % (1024 ** 2) != 0 else 0) |
| 73 | fname = section.vals['filename'] # type: str |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 74 | |
koder aka kdanilov | 6ab4d43 | 2015-06-22 00:26:28 +0300 | [diff] [blame] | 75 | # if already has other test with the same file name |
| 76 | # take largest size |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 77 | files_sizes[fname] = max(files_sizes.get(fname, 0), msz) |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 78 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 79 | return files_sizes |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 80 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 81 | def config_node(self, node: IRPCNode) -> None: |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 82 | try: |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 83 | node.conn.rmdir(self.config.remote_dir, recursive=True, ignore_missing=True) |
| 84 | node.conn.mkdir(self.config.remote_dir) |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame] | 85 | except Exception as exc: |
koder aka kdanilov | 39e449e | 2016-12-17 15:15:26 +0200 | [diff] [blame^] | 86 | msg = "Failed to create folder {} on remote {}.".format(self.config.remote_dir, node) |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame] | 87 | logger.exception(msg) |
| 88 | raise StopTestError(msg) from exc |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 89 | |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame] | 90 | self.install_utils(node) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 91 | 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 kdanilov | 8fbb27f | 2015-07-17 22:23:31 +0300 | [diff] [blame] | 95 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 96 | def install_utils(self, node: IRPCNode) -> None: |
koder aka kdanilov | 6ab4d43 | 2015-06-22 00:26:28 +0300 | [diff] [blame] | 97 | if self.use_system_fio: |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 98 | node.conn.install('fio', binary='fio') |
koder aka kdanilov | 6ab4d43 | 2015-06-22 00:26:28 +0300 | [diff] [blame] | 99 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 100 | 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 kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame] | 124 | exec_time + max(300, exec_time)) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 125 | # TODO(koder): fix next error |
| 126 | raise NotImplementedError("Need to extract time from test result") |
| 127 | return raw_res, (0, 0) |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 128 | |