koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 1 | import abc |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 2 | import copy |
| 3 | from typing import Dict, List, Any, Optional, Tuple, cast, Type, Iterator, NamedTuple |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 4 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 5 | |
| 6 | import numpy |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 7 | from scipy.stats.mstats_basic import NormaltestResult |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 8 | |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 9 | from .suits.job import JobConfig |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 10 | from .node_interfaces import IRPCNode |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 11 | from .common_types import Storable |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 12 | from .utils import round_digits, Number |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 13 | |
| 14 | |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 15 | class SuiteConfig(Storable): |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 16 | """ |
| 17 | Test suite input configuration. |
| 18 | |
| 19 | test_type - test type name |
| 20 | params - parameters from yaml file for this test |
| 21 | run_uuid - UUID to be used to create file names & Co |
| 22 | nodes - nodes to run tests on |
| 23 | remote_dir - directory on nodes to be used for local files |
| 24 | """ |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 25 | __ignore_fields__ = ['nodes', 'run_uuid', 'remote_dir'] |
| 26 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 27 | def __init__(self, |
| 28 | test_type: str, |
| 29 | params: Dict[str, Any], |
| 30 | run_uuid: str, |
| 31 | nodes: List[IRPCNode], |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 32 | remote_dir: str, |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 33 | idx: int, |
| 34 | keep_raw_files: bool) -> None: |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 35 | self.test_type = test_type |
| 36 | self.params = params |
| 37 | self.run_uuid = run_uuid |
| 38 | self.nodes = nodes |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 39 | self.nodes_ids = [node.node_id for node in nodes] |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 40 | self.remote_dir = remote_dir |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 41 | self.keep_raw_files = keep_raw_files |
| 42 | |
| 43 | if 'load' in self.params: |
| 44 | self.storage_id = "{}_{}_{}".format(self.test_type, self.params['load'], idx) |
| 45 | else: |
| 46 | self.storage_id = "{}_{}".format(self.test_type, idx) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 47 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 48 | def __eq__(self, o: object) -> bool: |
| 49 | if type(o) is not self.__class__: |
| 50 | return False |
| 51 | |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 52 | other = cast(SuiteConfig, o) |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 53 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 54 | return (self.test_type == other.test_type and |
| 55 | self.params == other.params and |
| 56 | set(self.nodes_ids) == set(other.nodes_ids)) |
| 57 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 58 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 59 | class DataSource: |
| 60 | def __init__(self, |
| 61 | suite_id: str = None, |
| 62 | job_id: str = None, |
| 63 | node_id: str = None, |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 64 | sensor: str = None, |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 65 | dev: str = None, |
| 66 | metric: str = None, |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 67 | tag: str = None) -> None: |
| 68 | self.suite_id = suite_id |
| 69 | self.job_id = job_id |
| 70 | self.node_id = node_id |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 71 | self.sensor = sensor |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 72 | self.dev = dev |
| 73 | self.metric = metric |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 74 | self.tag = tag |
| 75 | |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 76 | @property |
| 77 | def metric_fqdn(self) -> str: |
| 78 | return "{0.sensor}.{0.dev}.{0.metric}".format(self) |
| 79 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 80 | def __call__(self, **kwargs) -> 'DataSource': |
| 81 | dct = self.__dict__.copy() |
| 82 | dct.update(kwargs) |
| 83 | return self.__class__(**dct) |
| 84 | |
| 85 | def __str__(self) -> str: |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 86 | return ("suite={0.suite_id},job={0.job_id},node={0.node_id}," + |
| 87 | "path={0.sensor}.{0.dev}.{0.metric},tag={0.tag}").format(self) |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 88 | |
| 89 | def __repr__(self) -> str: |
| 90 | return str(self) |
| 91 | |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 92 | @property |
| 93 | def tpl(self) -> Tuple[Optional[str], Optional[str], Optional[str], Optional[str], |
| 94 | Optional[str], Optional[str], Optional[str]]: |
| 95 | return self.suite_id, self.job_id, self.node_id, self.sensor, self.dev, self.metric, self.tag |
| 96 | |
| 97 | def __eq__(self, o: object) -> bool: |
| 98 | return self.tpl == cast(DataSource, o).tpl |
| 99 | |
| 100 | def __hash__(self) -> int: |
| 101 | return hash(self.tpl) |
| 102 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 103 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 104 | class TimeSeries: |
| 105 | """Data series from sensor - either system sensor or from load generator tool (e.g. fio)""" |
| 106 | |
| 107 | def __init__(self, |
| 108 | name: str, |
| 109 | raw: Optional[bytes], |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 110 | data: numpy.ndarray, |
| 111 | times: numpy.ndarray, |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 112 | units: str, |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 113 | source: DataSource, |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 114 | time_units: str = 'us', |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 115 | raw_tag: str = 'txt', |
| 116 | histo_bins: numpy.ndarray = None) -> None: |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 117 | |
| 118 | # Sensor name. Typically DEV_NAME.METRIC |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 119 | self.name = name |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 120 | |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 121 | # units for data |
| 122 | self.units = units |
| 123 | |
| 124 | # units for time |
| 125 | self.time_units = time_units |
| 126 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 127 | # Time series times and values. Time in ms from Unix epoch. |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 128 | self.times = times |
| 129 | self.data = data |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 130 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 131 | # Raw sensor data (is provided). Like log file for fio iops/bw/lat. |
koder aka kdanilov | 23e6bdf | 2016-12-24 02:18:54 +0200 | [diff] [blame] | 132 | self.raw = raw |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 133 | self.raw_tag = raw_tag |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 134 | self.source = source |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 135 | self.histo_bins = histo_bins |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 136 | |
| 137 | def __str__(self) -> str: |
| 138 | res = "TS({}):\n".format(self.name) |
| 139 | res += " source={}\n".format(self.source) |
| 140 | res += " times_size={}\n".format(len(self.times)) |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 141 | res += " data_shape={}\n".format(*self.data.shape) |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 142 | return res |
| 143 | |
| 144 | def __repr__(self) -> str: |
| 145 | return str(self) |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 146 | |
kdanylov aka koder | 736e5c1 | 2017-05-07 17:27:14 +0300 | [diff] [blame^] | 147 | def copy(self, no_data: bool = False) -> 'TimeSeries': |
kdanylov aka koder | 4518318 | 2017-04-30 23:55:40 +0300 | [diff] [blame] | 148 | cp = copy.copy(self) |
kdanylov aka koder | 736e5c1 | 2017-05-07 17:27:14 +0300 | [diff] [blame^] | 149 | |
| 150 | if not no_data: |
| 151 | cp.times = self.times.copy() |
| 152 | cp.data = self.data.copy() |
| 153 | |
kdanylov aka koder | 4518318 | 2017-04-30 23:55:40 +0300 | [diff] [blame] | 154 | cp.source = self.source() |
| 155 | return cp |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 156 | |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 157 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 158 | # (node_name, source_dev, metric_name) => metric_results |
| 159 | JobMetrics = Dict[Tuple[str, str, str], TimeSeries] |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 160 | |
| 161 | |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 162 | class StatProps(Storable): |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 163 | "Statistic properties for timeseries with unknown data distribution" |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 164 | |
| 165 | __ignore_fields__ = ['data'] |
| 166 | |
kdanylov aka koder | 4518318 | 2017-04-30 23:55:40 +0300 | [diff] [blame] | 167 | def __init__(self, data: numpy.array, units: str) -> None: |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 168 | self.perc_99 = None # type: float |
| 169 | self.perc_95 = None # type: float |
| 170 | self.perc_90 = None # type: float |
| 171 | self.perc_50 = None # type: float |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 172 | self.perc_10 = None # type: float |
| 173 | self.perc_5 = None # type: float |
| 174 | self.perc_1 = None # type: float |
koder aka kdanilov | 7022706 | 2016-11-26 23:23:21 +0200 | [diff] [blame] | 175 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 176 | self.min = None # type: Number |
| 177 | self.max = None # type: Number |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 178 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 179 | # bin_center: bin_count |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 180 | self.log_bins = False |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 181 | self.bins_populations = None # type: numpy.array |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 182 | |
| 183 | # bin edges, one more element that in bins_populations |
| 184 | self.bins_edges = None # type: numpy.array |
| 185 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 186 | self.data = data |
kdanylov aka koder | 4518318 | 2017-04-30 23:55:40 +0300 | [diff] [blame] | 187 | self.units = units |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 188 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 189 | def __str__(self) -> str: |
| 190 | res = ["{}(size = {}):".format(self.__class__.__name__, len(self.data))] |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 191 | for name in ["perc_1", "perc_5", "perc_10", "perc_50", "perc_90", "perc_95", "perc_99"]: |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 192 | res.append(" {} = {}".format(name, round_digits(getattr(self, name)))) |
| 193 | res.append(" range {} {}".format(round_digits(self.min), round_digits(self.max))) |
| 194 | return "\n".join(res) |
| 195 | |
| 196 | def __repr__(self) -> str: |
| 197 | return str(self) |
| 198 | |
| 199 | def raw(self) -> Dict[str, Any]: |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 200 | data = super().raw() |
| 201 | data['bins_mids'] = list(data['bins_mids']) |
| 202 | data['bins_populations'] = list(data['bins_populations']) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 203 | return data |
| 204 | |
| 205 | @classmethod |
| 206 | def fromraw(cls, data: Dict[str, Any]) -> 'StatProps': |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 207 | data['bins_mids'] = numpy.array(data['bins_mids']) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 208 | data['bins_populations'] = numpy.array(data['bins_populations']) |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 209 | return cast(StatProps, super().fromraw(data)) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 210 | |
| 211 | |
| 212 | class HistoStatProps(StatProps): |
| 213 | """Statistic properties for 2D timeseries with unknown data distribution and histogram as input value. |
| 214 | Used for latency""" |
kdanylov aka koder | 4518318 | 2017-04-30 23:55:40 +0300 | [diff] [blame] | 215 | def __init__(self, data: numpy.array, units: str) -> None: |
| 216 | StatProps.__init__(self, data, units) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 217 | |
| 218 | |
| 219 | class NormStatProps(StatProps): |
| 220 | "Statistic properties for timeseries with normal data distribution. Used for iops/bw" |
kdanylov aka koder | 4518318 | 2017-04-30 23:55:40 +0300 | [diff] [blame] | 221 | def __init__(self, data: numpy.array, units: str) -> None: |
| 222 | StatProps.__init__(self, data, units) |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 223 | |
| 224 | self.average = None # type: float |
| 225 | self.deviation = None # type: float |
| 226 | self.confidence = None # type: float |
| 227 | self.confidence_level = None # type: float |
| 228 | self.normtest = None # type: NormaltestResult |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 229 | self.skew = None # type: float |
| 230 | self.kurt = None # type: float |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 231 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 232 | def __str__(self) -> str: |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 233 | res = ["NormStatProps(size = {}):".format(len(self.data)), |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 234 | " distr = {} ~ {}".format(round_digits(self.average), round_digits(self.deviation)), |
| 235 | " confidence({0.confidence_level}) = {1}".format(self, round_digits(self.confidence)), |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 236 | " perc_1 = {}".format(round_digits(self.perc_1)), |
| 237 | " perc_5 = {}".format(round_digits(self.perc_5)), |
| 238 | " perc_10 = {}".format(round_digits(self.perc_10)), |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 239 | " perc_50 = {}".format(round_digits(self.perc_50)), |
| 240 | " perc_90 = {}".format(round_digits(self.perc_90)), |
| 241 | " perc_95 = {}".format(round_digits(self.perc_95)), |
| 242 | " perc_99 = {}".format(round_digits(self.perc_99)), |
| 243 | " range {} {}".format(round_digits(self.min), round_digits(self.max)), |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 244 | " normtest = {0.normtest}".format(self), |
| 245 | " skew ~ kurt = {0.skew} ~ {0.kurt}".format(self)] |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 246 | return "\n".join(res) |
| 247 | |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 248 | def raw(self) -> Dict[str, Any]: |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 249 | data = super().raw() |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 250 | data['normtest'] = (data['nortest'].statistic, data['nortest'].pvalue) |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 251 | return data |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 252 | |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 253 | @classmethod |
| 254 | def fromraw(cls, data: Dict[str, Any]) -> 'NormStatProps': |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 255 | data['normtest'] = NormaltestResult(*data['normtest']) |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 256 | return cast(NormStatProps, super().fromraw(data)) |
koder aka kdanilov | 7f59d56 | 2016-12-26 01:34:23 +0200 | [diff] [blame] | 257 | |
| 258 | |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 259 | JobStatMetrics = Dict[Tuple[str, str, str], StatProps] |
| 260 | |
| 261 | |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 262 | class JobResult: |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 263 | """Contains done test job information""" |
| 264 | |
| 265 | def __init__(self, |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 266 | info: JobConfig, |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 267 | begin_time: int, |
| 268 | end_time: int, |
| 269 | raw: JobMetrics) -> None: |
koder aka kdanilov | ffaf48d | 2016-12-27 02:25:29 +0200 | [diff] [blame] | 270 | self.info = info |
koder aka kdanilov | f286517 | 2016-12-30 03:35:11 +0200 | [diff] [blame] | 271 | self.run_interval = (begin_time, end_time) |
| 272 | self.raw = raw # type: JobMetrics |
| 273 | self.processed = None # type: JobStatMetrics |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 274 | |
| 275 | |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 276 | ArrayData = NamedTuple("ArrayData", |
| 277 | [('header', List[str]), |
| 278 | ('histo_bins', Optional[numpy.ndarray]), |
| 279 | ('data', Optional[numpy.ndarray])]) |
| 280 | |
| 281 | |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 282 | class IResultStorage(metaclass=abc.ABCMeta): |
| 283 | |
| 284 | @abc.abstractmethod |
| 285 | def sync(self) -> None: |
| 286 | pass |
| 287 | |
| 288 | @abc.abstractmethod |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 289 | def append_sensor(self, data: numpy.array, ds: DataSource, units: str, histo_bins: numpy.ndarray = None) -> None: |
| 290 | pass |
| 291 | |
| 292 | @abc.abstractmethod |
koder aka kdanilov | a732a60 | 2017-02-01 20:29:56 +0200 | [diff] [blame] | 293 | def load_sensor(self, ds: DataSource) -> TimeSeries: |
| 294 | pass |
| 295 | |
| 296 | @abc.abstractmethod |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 297 | def iter_sensors(self, ds: DataSource) -> Iterator[TimeSeries]: |
| 298 | pass |
| 299 | |
| 300 | @abc.abstractmethod |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 301 | def put_or_check_suite(self, suite: SuiteConfig) -> None: |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 302 | pass |
| 303 | |
| 304 | @abc.abstractmethod |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 305 | def put_job(self, suite: SuiteConfig, job: JobConfig) -> None: |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 306 | pass |
| 307 | |
| 308 | @abc.abstractmethod |
| 309 | def put_ts(self, ts: TimeSeries) -> None: |
| 310 | pass |
| 311 | |
| 312 | @abc.abstractmethod |
| 313 | def put_extra(self, data: bytes, source: DataSource) -> None: |
| 314 | pass |
| 315 | |
| 316 | @abc.abstractmethod |
| 317 | def put_stat(self, data: StatProps, source: DataSource) -> None: |
| 318 | pass |
| 319 | |
| 320 | @abc.abstractmethod |
| 321 | def get_stat(self, stat_cls: Type[StatProps], source: DataSource) -> StatProps: |
| 322 | pass |
| 323 | |
| 324 | @abc.abstractmethod |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 325 | def iter_suite(self, suite_type: str = None) -> Iterator[SuiteConfig]: |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 326 | pass |
| 327 | |
| 328 | @abc.abstractmethod |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 329 | def iter_job(self, suite: SuiteConfig) -> Iterator[JobConfig]: |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 330 | pass |
| 331 | |
| 332 | @abc.abstractmethod |
koder aka kdanilov | f90de85 | 2017-01-20 18:12:27 +0200 | [diff] [blame] | 333 | def iter_ts(self, suite: SuiteConfig, job: JobConfig) -> Iterator[TimeSeries]: |
koder aka kdanilov | 108ac36 | 2017-01-19 20:17:16 +0200 | [diff] [blame] | 334 | pass |
| 335 | |
| 336 | # return path to file to be inserted into report |
| 337 | @abc.abstractmethod |
| 338 | def put_plot_file(self, data: bytes, source: DataSource) -> str: |
| 339 | pass |