| import logging |
| from typing import cast, List, Union |
| |
| import numpy |
| |
| from cephlib.common import float2str |
| from cephlib.texttable import Texttable |
| from cephlib.statistic import calc_norm_stat_props, calc_histo_stat_props |
| |
| from .stage import Stage, StepOrder |
| from .test_run_class import TestRun |
| from .result_classes import SuiteConfig |
| from .suits.io.fio import FioTest |
| from .suits.io.fio_job import FioJobParams |
| from .suits.io.fio_hist import get_lat_vals |
| from .data_selectors import get_aggregated |
| from .result_storage import IWallyStorage |
| |
| |
| logger = logging.getLogger("wally") |
| |
| |
| console_report_headers = ["Description", "IOPS ~ Dev", "BW, MiBps", 'Skew/Kurt', 'lat med, ms', 'lat 95, ms'] |
| console_report_align = ['l', 'r', 'r', 'r', 'r', 'r'] |
| |
| def get_console_report_table(suite: SuiteConfig, rstorage: IWallyStorage) -> List[Union[List[str], Texttable.HLINE]]: |
| table: List[Union[List[str], Texttable.HLINE]] = [] |
| prev_params = None |
| for job in sorted(rstorage.iter_job(suite), key=lambda job: job.params): |
| fparams = cast(FioJobParams, job.params) |
| fparams['qd'] = None |
| |
| if prev_params is not None and fparams.char_tpl != prev_params: |
| table.append(Texttable.HLINE) |
| |
| prev_params = fparams.char_tpl |
| |
| bw_ts = get_aggregated(rstorage, suite.storage_id, job.storage_id, metric='bw', |
| trange=job.reliable_info_range_s) |
| props = calc_norm_stat_props(bw_ts) |
| avg_iops = props.average // job.params.params['bsize'] |
| iops_dev = props.deviation // job.params.params['bsize'] |
| |
| lat_ts = get_aggregated(rstorage, suite.storage_id, job.storage_id, metric='lat', |
| trange=job.reliable_info_range_s) |
| bins_edges = numpy.array(get_lat_vals(lat_ts.data.shape[1]), dtype='float32') / 1000 # convert us to ms |
| lat_props = calc_histo_stat_props(lat_ts, bins_edges) |
| table.append([job.params.summary, |
| f"{float2str(avg_iops):>6s} ~ {float2str(iops_dev):>6s}", |
| float2str(props.average / 1024), # Ki -> Mi |
| f"{props.skew:>5.1f}/{props.kurt:>5.1f}", |
| float2str(lat_props.perc_50), float2str(lat_props.perc_95)]) |
| return table |
| |
| |
| class ConsoleReportStage(Stage): |
| |
| priority = StepOrder.REPORT |
| |
| def run(self, ctx: TestRun) -> None: |
| for suite in ctx.rstorage.iter_suite(FioTest.name): |
| table = Texttable(max_width=200) |
| table.set_deco(Texttable.VLINES | Texttable.BORDER | Texttable.HEADER) |
| tbl = ctx.rstorage.get_txt_report(suite) |
| if tbl is None: |
| table.header(console_report_headers) |
| table.set_cols_align(console_report_align) |
| for line in get_console_report_table(suite, ctx.rstorage): |
| table.add_row(line) |
| tbl = table.draw() |
| ctx.rstorage.put_txt_report(suite, tbl) |
| print(tbl) |