kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 1 | import logging |
kdanylov aka koder | 84de1e4 | 2017-05-22 14:00:07 +0300 | [diff] [blame] | 2 | from typing import Tuple, Iterator, List, Iterable, Dict, Union, Callable, Set |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 3 | |
| 4 | import numpy |
| 5 | |
kdanylov aka koder | b083333 | 2017-05-13 20:39:17 +0300 | [diff] [blame] | 6 | from cephlib.numeric_types import DataSource, TimeSeries |
| 7 | from cephlib.storage_selectors import c_interpolate_ts_on_seconds_border |
kdanylov aka koder | 84de1e4 | 2017-05-22 14:00:07 +0300 | [diff] [blame] | 8 | from cephlib.node import NodeInfo |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 9 | |
kdanylov aka koder | 026e5f2 | 2017-05-15 01:04:39 +0300 | [diff] [blame] | 10 | from .result_classes import IWallyStorage |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 11 | |
| 12 | |
| 13 | logger = logging.getLogger("wally") |
| 14 | |
| 15 | # Separately for each test heatmaps & agg acroos whole time histos: |
| 16 | # * fio latency heatmap for all instances |
| 17 | # * data dev iops across all osd |
| 18 | # * data dev bw across all osd |
| 19 | # * date dev qd across all osd |
| 20 | # * journal dev iops across all osd |
| 21 | # * journal dev bw across all osd |
| 22 | # * journal dev qd across all osd |
| 23 | # * net dev pps across all hosts |
| 24 | # * net dev bps across all hosts |
| 25 | |
| 26 | # Main API's |
| 27 | # get sensors by pattern |
| 28 | # allign values to seconds |
| 29 | # cut ranges for particular test |
| 30 | # transform into 2d histos (either make histos or rebin them) and clip outliers same time |
| 31 | |
| 32 | |
| 33 | AGG_TAG = 'ALL' |
| 34 | |
| 35 | |
kdanylov aka koder | 026e5f2 | 2017-05-15 01:04:39 +0300 | [diff] [blame] | 36 | def find_all_series(rstorage: IWallyStorage, suite_id: str, job_id: str, metric: str) -> Iterator[TimeSeries]: |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 37 | "Iterated over selected metric for all nodes for given Suite/job" |
kdanylov aka koder | b083333 | 2017-05-13 20:39:17 +0300 | [diff] [blame] | 38 | return (rstorage.get_ts(ds) for ds in rstorage.iter_ts(suite_id=suite_id, job_id=job_id, metric=metric)) |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 39 | |
| 40 | |
kdanylov aka koder | 026e5f2 | 2017-05-15 01:04:39 +0300 | [diff] [blame] | 41 | def get_aggregated(rstorage: IWallyStorage, suite_id: str, job_id: str, metric: str, |
kdanylov aka koder | b083333 | 2017-05-13 20:39:17 +0300 | [diff] [blame] | 42 | trange: Tuple[int, int]) -> TimeSeries: |
kdanylov aka koder | 84de1e4 | 2017-05-22 14:00:07 +0300 | [diff] [blame] | 43 | "Sum selected fio metric for all nodes for given Suite/job" |
| 44 | |
| 45 | key = (id(rstorage), suite_id, job_id, metric, trange) |
| 46 | aggregated_cache = rstorage.storage.other_caches['aggregated'] |
| 47 | if key in aggregated_cache: |
| 48 | return aggregated_cache[key].copy() |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 49 | |
kdanylov aka koder | b083333 | 2017-05-13 20:39:17 +0300 | [diff] [blame] | 50 | tss = list(find_all_series(rstorage, suite_id, job_id, metric)) |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 51 | |
| 52 | if len(tss) == 0: |
kdanylov aka koder | 938f75f | 2018-06-27 01:52:44 +0300 | [diff] [blame] | 53 | raise NameError(f"Can't found any TS for {suite_id},{job_id},{metric}") |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 54 | |
kdanylov aka koder | 84de1e4 | 2017-05-22 14:00:07 +0300 | [diff] [blame] | 55 | c_intp = c_interpolate_ts_on_seconds_border |
| 56 | tss_inp = [c_intp(ts.select(trange), tp='fio', allow_broken_step=(metric == 'lat')) for ts in tss] |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 57 | |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 58 | res = None |
kdanylov aka koder | 84de1e4 | 2017-05-22 14:00:07 +0300 | [diff] [blame] | 59 | res_times = None |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 60 | |
kdanylov aka koder | 2e5fce1 | 2017-05-23 01:47:36 +0300 | [diff] [blame] | 61 | for ts, ts_orig in zip(tss_inp, tss): |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 62 | if ts.time_units != 's': |
| 63 | msg = "time_units must be 's' for fio sensor" |
| 64 | logger.error(msg) |
| 65 | raise ValueError(msg) |
| 66 | |
kdanylov aka koder | 13e5845 | 2018-07-15 02:51:51 +0300 | [diff] [blame] | 67 | # if metric == 'lat' and (len(ts.data.shape) != 2 or ts.data.shape[1] != expected_lat_bins): |
| 68 | # msg = f"Sensor {ts.source.dev}.{ts.source.sensor} on node {ts.source.node_id} " + \ |
| 69 | # f"has shape={ts.data.shape}. Can only process sensors with shape=[X, {expected_lat_bins}]." |
| 70 | # logger.error(msg) |
| 71 | # raise ValueError(msg) |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 72 | |
| 73 | if metric != 'lat' and len(ts.data.shape) != 1: |
kdanylov aka koder | 938f75f | 2018-06-27 01:52:44 +0300 | [diff] [blame] | 74 | msg = f"Sensor {ts.source.dev}.{ts.source.sensor} on node {ts.source.node_id} " + \ |
| 75 | f"has shape={ts.data.shape}. Can only process 1D sensors." |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 76 | logger.error(msg) |
| 77 | raise ValueError(msg) |
| 78 | |
kdanylov aka koder | 938f75f | 2018-06-27 01:52:44 +0300 | [diff] [blame] | 79 | assert trange[0] >= ts.times[0] and trange[1] <= ts.times[-1], \ |
| 80 | f"[{ts.times[0]}, {ts.times[-1]}] not in [{trange[0]}, {trange[-1]}]" |
| 81 | |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 82 | idx1, idx2 = numpy.searchsorted(ts.times, trange) |
| 83 | idx2 += 1 |
| 84 | |
| 85 | assert (idx2 - idx1) == (trange[1] - trange[0] + 1), \ |
| 86 | "Broken time array at {} for {}".format(trange, ts.source) |
| 87 | |
| 88 | dt = ts.data[idx1: idx2] |
| 89 | if res is None: |
kdanylov aka koder | 84de1e4 | 2017-05-22 14:00:07 +0300 | [diff] [blame] | 90 | res = dt.copy() |
| 91 | res_times = ts.times[idx1: idx2].copy() |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 92 | else: |
kdanylov aka koder | 938f75f | 2018-06-27 01:52:44 +0300 | [diff] [blame] | 93 | assert res.shape == dt.shape, f"res.shape(={res.shape}) != dt.shape(={dt.shape})" |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 94 | res += dt |
| 95 | |
kdanylov aka koder | 84de1e4 | 2017-05-22 14:00:07 +0300 | [diff] [blame] | 96 | ds = DataSource(suite_id=suite_id, job_id=job_id, node_id=AGG_TAG, sensor='fio', |
| 97 | dev=AGG_TAG, metric=metric, tag='csv') |
kdanylov aka koder | b083333 | 2017-05-13 20:39:17 +0300 | [diff] [blame] | 98 | agg_ts = TimeSeries(res, source=ds, |
kdanylov aka koder | 84de1e4 | 2017-05-22 14:00:07 +0300 | [diff] [blame] | 99 | times=res_times, |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 100 | units=tss_inp[0].units, |
| 101 | histo_bins=tss_inp[0].histo_bins, |
| 102 | time_units=tss_inp[0].time_units) |
kdanylov aka koder | 84de1e4 | 2017-05-22 14:00:07 +0300 | [diff] [blame] | 103 | aggregated_cache[key] = agg_ts |
| 104 | return agg_ts.copy() |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 105 | |
kdanylov aka koder | 84de1e4 | 2017-05-22 14:00:07 +0300 | [diff] [blame] | 106 | |
| 107 | def get_nodes(storage: IWallyStorage, roles: Iterable[str]) -> List[NodeInfo]: |
| 108 | return [node for node in storage.load_nodes() if node.roles.intersection(roles)] |
kdanylov aka koder | cdfcdaf | 2017-04-29 10:03:39 +0300 | [diff] [blame] | 109 | |