koder aka kdanilov | 4643fd6 | 2015-02-10 16:20:13 -0800 | [diff] [blame] | 1 | import abc |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 2 | import time |
| 3 | import logging |
koder aka kdanilov | 4643fd6 | 2015-02-10 16:20:13 -0800 | [diff] [blame] | 4 | import os.path |
kdanylov aka koder | 026e5f2 | 2017-05-15 01:04:39 +0300 | [diff] [blame] | 5 | from typing import Any, List, Optional, Callable, Iterable, cast, Tuple |
koder aka kdanilov | 652cd80 | 2015-04-13 12:21:07 +0300 | [diff] [blame] | 6 | |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 7 | from concurrent.futures import ThreadPoolExecutor, wait |
koder aka kdanilov | 4643fd6 | 2015-02-10 16:20:13 -0800 | [diff] [blame] | 8 | |
kdanylov aka koder | 026e5f2 | 2017-05-15 01:04:39 +0300 | [diff] [blame] | 9 | from cephlib.node import IRPCNode |
| 10 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 11 | from ..utils import StopTestError, get_time_interval_printable_info |
kdanylov aka koder | 026e5f2 | 2017-05-15 01:04:39 +0300 | [diff] [blame] | 12 | from ..result_classes import SuiteConfig, JobConfig, TimeSeries, IWallyStorage |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 13 | |
koder aka kdanilov | 4af1c1d | 2015-05-18 15:48:58 +0300 | [diff] [blame] | 14 | |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 15 | logger = logging.getLogger("wally") |
koder aka kdanilov | 88407ff | 2015-05-26 15:35:57 +0300 | [diff] [blame] | 16 | |
| 17 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 18 | __doc__ = "Contains base classes for performance tests" |
| 19 | |
| 20 | |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 21 | class PerfTest(metaclass=abc.ABCMeta): |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 22 | """Base class for all tests""" |
| 23 | name = None # type: str |
| 24 | max_retry = 3 |
| 25 | retry_time = 30 |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 26 | job_config_cls = None # type: type |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 27 | |
kdanylov aka koder | 026e5f2 | 2017-05-15 01:04:39 +0300 | [diff] [blame] | 28 | def __init__(self, storage: IWallyStorage, suite: SuiteConfig, on_idle: Callable[[], None] = None) -> None: |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 29 | self.suite = suite |
koder aka kdanilov | e2de58c | 2015-04-24 22:59:36 +0300 | [diff] [blame] | 30 | self.stop_requested = False |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 31 | self.sorted_nodes_ids = sorted(node.node_id for node in self.suite.nodes) |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 32 | self.on_idle = on_idle |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 33 | self.storage = storage |
koder aka kdanilov | e2de58c | 2015-04-24 22:59:36 +0300 | [diff] [blame] | 34 | |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame] | 35 | def request_stop(self) -> None: |
koder aka kdanilov | e2de58c | 2015-04-24 22:59:36 +0300 | [diff] [blame] | 36 | self.stop_requested = True |
koder aka kdanilov | 2066daf | 2015-04-23 21:05:41 +0300 | [diff] [blame] | 37 | |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame] | 38 | def join_remote(self, path: str) -> str: |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 39 | return os.path.join(self.suite.remote_dir, path) |
koder aka kdanilov | 4500a5f | 2015-04-17 16:55:17 +0300 | [diff] [blame] | 40 | |
koder aka kdanilov | 4af1c1d | 2015-05-18 15:48:58 +0300 | [diff] [blame] | 41 | @abc.abstractmethod |
koder aka kdanilov | bbbe1dc | 2016-12-20 01:19:56 +0200 | [diff] [blame] | 42 | def run(self) -> None: |
koder aka kdanilov | 4643fd6 | 2015-02-10 16:20:13 -0800 | [diff] [blame] | 43 | pass |
| 44 | |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 45 | @abc.abstractmethod |
koder aka kdanilov | 39e449e | 2016-12-17 15:15:26 +0200 | [diff] [blame] | 46 | def format_for_console(self, data: Any) -> str: |
koder aka kdanilov | ec1b973 | 2015-04-23 20:43:29 +0300 | [diff] [blame] | 47 | pass |
| 48 | |
koder aka kdanilov | 4643fd6 | 2015-02-10 16:20:13 -0800 | [diff] [blame] | 49 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 50 | class ThreadedTest(PerfTest, metaclass=abc.ABCMeta): |
| 51 | """Base class for tests, which spawn separated thread for each node""" |
| 52 | |
| 53 | # max allowed time difference between starts and stops of run of the same test on different test nodes |
| 54 | # used_max_diff = max((min_run_time * max_rel_time_diff), max_time_diff) |
| 55 | max_time_diff = 5 |
| 56 | max_rel_time_diff = 0.05 |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 57 | load_profile_name = None # type: str |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 58 | |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 59 | def __init__(self, *args, **kwargs) -> None: |
| 60 | PerfTest.__init__(self, *args, **kwargs) |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 61 | self.job_configs = None # type: List[JobConfig] |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 62 | |
| 63 | @abc.abstractmethod |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 64 | def get_expected_runtime(self, iter_cfg: JobConfig) -> Optional[int]: |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 65 | pass |
| 66 | |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 67 | def get_not_done_jobs(self) -> Iterable[JobConfig]: |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 68 | jobs_map = {job.storage_id: job for job in self.job_configs} |
| 69 | already_in_storage = set() |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 70 | for db_config in cast(List[JobConfig], self.storage.iter_job(self.suite)): |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 71 | if db_config.storage_id in jobs_map: |
| 72 | job = jobs_map[db_config.storage_id] |
| 73 | if job != db_config: |
| 74 | logger.error("Test info at '%s.%s' is not equal to expected config for iteration %s.%s." + |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 75 | " Maybe configuration was changed before test was restarted. " + |
| 76 | "DB cfg is:\n %s\nExpected cfg is:\n %s\nFix DB or rerun test from beginning", |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 77 | self.suite.storage_id, job.storage_id, self.name, job.summary, |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 78 | str(db_config).replace("\n", "\n "), |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 79 | str(job).replace("\n", "\n ")) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 80 | raise StopTestError() |
koder aka kdanilov | bbbe1dc | 2016-12-20 01:19:56 +0200 | [diff] [blame] | 81 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 82 | logger.info("Test iteration %s.%s found in storage and will be skipped", self.name, job.summary) |
| 83 | already_in_storage.add(db_config.storage_id) |
| 84 | |
| 85 | return [job for job in self.job_configs if job.storage_id not in already_in_storage] |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 86 | |
koder aka kdanilov | bbbe1dc | 2016-12-20 01:19:56 +0200 | [diff] [blame] | 87 | def run(self) -> None: |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 88 | self.storage.put_or_check_suite(self.suite) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 89 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 90 | not_in_storage = list(self.get_not_done_jobs()) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 91 | if not not_in_storage: |
| 92 | logger.info("All test iteration in storage already. Skip test") |
| 93 | return |
| 94 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 95 | logger.debug("Run test %s with profile %r on nodes %s.", self.name, |
| 96 | self.load_profile_name, |
| 97 | ",".join(self.sorted_nodes_ids)) |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 98 | logger.debug("Prepare nodes") |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 99 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 100 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 101 | with ThreadPoolExecutor(len(self.suite.nodes)) as pool: |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 102 | # config nodes |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 103 | list(pool.map(self.config_node, self.suite.nodes)) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 104 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 105 | run_times = list(map(self.get_expected_runtime, not_in_storage)) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 106 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 107 | if None not in run_times: |
kdanylov aka koder | 84de1e4 | 2017-05-22 14:00:07 +0300 | [diff] [blame] | 108 | # +10s - is a rough estimation for additional operations per iteration |
| 109 | expected_run_time = int(sum(run_times) + 10 * len(not_in_storage)) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 110 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 111 | exec_time_s, end_dt_s = get_time_interval_printable_info(expected_run_time) |
kdanylov aka koder | 736e5c1 | 2017-05-07 17:27:14 +0300 | [diff] [blame] | 112 | logger.info("Entire test should takes around %s and finish at %s", exec_time_s, end_dt_s) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 113 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 114 | for job in not_in_storage: |
| 115 | results = [] # type: List[TimeSeries] |
| 116 | for retry_idx in range(self.max_retry): |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 117 | logger.info("Preparing job %s", job.params.summary) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 118 | |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 119 | # prepare nodes for new iterations |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 120 | wait([pool.submit(self.prepare_iteration, node, job) for node in self.suite.nodes]) |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 121 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 122 | expected_job_time = self.get_expected_runtime(job) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 123 | exec_time_s, end_dt_s = get_time_interval_printable_info(expected_job_time) |
kdanylov aka koder | 736e5c1 | 2017-05-07 17:27:14 +0300 | [diff] [blame] | 124 | logger.info("Job should takes around %s and finish at %s", exec_time_s, end_dt_s) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 125 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 126 | jfutures = [pool.submit(self.run_iteration, node, job) for node in self.suite.nodes] |
| 127 | failed = False |
| 128 | for future in jfutures: |
| 129 | try: |
| 130 | results.extend(future.result()) |
| 131 | except EnvironmentError: |
| 132 | failed = True |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 133 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 134 | if not failed: |
| 135 | break |
| 136 | |
| 137 | if self.max_retry - 1 == retry_idx: |
| 138 | logger.exception("Fio failed") |
| 139 | raise StopTestError() |
| 140 | |
| 141 | logger.exception("During fio run") |
| 142 | logger.info("Sleeping %ss and retrying job", self.retry_time) |
| 143 | time.sleep(self.retry_time) |
| 144 | results = [] |
| 145 | |
| 146 | # per node jobs start and stop times |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 147 | start_times = [] # type: List[int] |
| 148 | stop_times = [] # type: List[int] |
| 149 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 150 | for ts in results: |
| 151 | self.storage.put_ts(ts) |
| 152 | if len(ts.times) >= 2: # type: ignore |
| 153 | start_times.append(ts.times[0]) |
| 154 | stop_times.append(ts.times[-1]) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 155 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 156 | if len(start_times) > 0: |
| 157 | min_start_time = min(start_times) |
| 158 | max_start_time = max(start_times) |
| 159 | min_stop_time = min(stop_times) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 160 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 161 | max_allowed_time_diff = int((min_stop_time - max_start_time) * self.max_rel_time_diff) |
| 162 | max_allowed_time_diff = max(max_allowed_time_diff, self.max_time_diff) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 163 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 164 | if min_start_time + self.max_time_diff < max_allowed_time_diff: |
| 165 | logger.warning("Too large difference in %s:%s start time - %s. " + |
| 166 | "Max recommended difference is %s", |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 167 | self.name, job.summary, |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 168 | max_start_time - min_start_time, self.max_time_diff) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 169 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 170 | if min_stop_time + self.max_time_diff < max_allowed_time_diff: |
| 171 | logger.warning("Too large difference in %s:%s stop time - %s. " + |
| 172 | "Max recommended difference is %s", |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 173 | self.name, job.summary, |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 174 | max_start_time - min_start_time, self.max_time_diff) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 175 | |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 176 | job.reliable_info_range = (int(max_start_time), int(min_stop_time)) |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 177 | |
| 178 | self.storage.put_job(self.suite, job) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 179 | self.storage.sync() |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 180 | |
| 181 | if self.on_idle is not None: |
| 182 | self.on_idle() |
| 183 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 184 | @abc.abstractmethod |
| 185 | def config_node(self, node: IRPCNode) -> None: |
| 186 | pass |
| 187 | |
| 188 | @abc.abstractmethod |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 189 | def prepare_iteration(self, node: IRPCNode, job: JobConfig) -> None: |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 190 | pass |
| 191 | |
| 192 | @abc.abstractmethod |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 193 | def run_iteration(self, node: IRPCNode, job: JobConfig) -> List[TimeSeries]: |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 194 | pass |
| 195 | |
| 196 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 197 | class TwoScriptTest(ThreadedTest, metaclass=abc.ABCMeta): |
| 198 | def __init__(self, *dt, **mp) -> None: |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 199 | ThreadedTest.__init__(self, *dt, **mp) |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 200 | self.prerun_script = self.suite.params['prerun_script'] |
| 201 | self.run_script = self.suite.params['run_script'] |
| 202 | self.prerun_tout = self.suite.params.get('prerun_tout', 3600) |
| 203 | self.run_tout = self.suite.params.get('run_tout', 3600) |
| 204 | # TODO: fix job_configs field |
| 205 | raise NotImplementedError("Fix job configs") |
Yulia Portnova | 7ddfa73 | 2015-02-24 17:32:58 +0200 | [diff] [blame] | 206 | |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 207 | def get_expected_runtime(self, job: JobConfig) -> Optional[int]: |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 208 | return None |
Yulia Portnova | 7ddfa73 | 2015-02-24 17:32:58 +0200 | [diff] [blame] | 209 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 210 | def config_node(self, node: IRPCNode) -> None: |
| 211 | node.copy_file(self.run_script, self.join_remote(self.run_script)) |
| 212 | node.copy_file(self.prerun_script, self.join_remote(self.prerun_script)) |
koder aka kdanilov | bc2c898 | 2015-06-13 02:50:43 +0300 | [diff] [blame] | 213 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 214 | cmd = self.join_remote(self.prerun_script) |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 215 | cmd += ' ' + self.suite.params.get('prerun_opts', '') |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame] | 216 | node.run(cmd, timeout=self.prerun_tout) |
Yulia Portnova | 7ddfa73 | 2015-02-24 17:32:58 +0200 | [diff] [blame] | 217 | |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 218 | def prepare_iteration(self, node: IRPCNode, job: JobConfig) -> None: |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 219 | pass |
| 220 | |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 221 | def run_iteration(self, node: IRPCNode, job: JobConfig) -> List[TimeSeries]: |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 222 | # TODO: have to store logs |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 223 | cmd = self.join_remote(self.run_script) |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 224 | cmd += ' ' + self.suite.params.get('run_opts', '') |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 225 | return self.parse_results(node.run(cmd, timeout=self.run_tout)) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 226 | |
| 227 | @abc.abstractmethod |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 228 | def parse_results(self, data: str) -> List[TimeSeries]: |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 229 | pass |
| 230 | |