aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTibor Frank <tifrank@cisco.com>2023-04-11 12:56:56 +0200
committerTibor Frank <tifrank@cisco.com>2023-04-12 05:49:41 +0000
commit5edaac881f42de498d92efca3fbbfab8e2e47e71 (patch)
tree0c9678e2362b46f0c5060ae72835a91498ea9f21
parent757fc06136fe9ea67b1a4f9b2aa43d70a50533b1 (diff)
C-Dash: Add latency hdrh for iterative data
Signed-off-by: Tibor Frank <tifrank@cisco.com> Change-Id: I4557c724d1d2b2eab355d03f0339c56660253360
-rw-r--r--csit.infra.dash/app/cdash/data/data.yaml24
-rw-r--r--csit.infra.dash/app/cdash/report/graphs.py11
-rw-r--r--csit.infra.dash/app/cdash/report/layout.py125
-rw-r--r--csit.infra.dash/app/cdash/trending/graphs.py105
-rw-r--r--csit.infra.dash/app/cdash/trending/layout.py5
-rw-r--r--csit.infra.dash/app/cdash/utils/utils.py103
6 files changed, 264 insertions, 109 deletions
diff --git a/csit.infra.dash/app/cdash/data/data.yaml b/csit.infra.dash/app/cdash/data/data.yaml
index 7a03e13d08..4e78323fe7 100644
--- a/csit.infra.dash/app/cdash/data/data.yaml
+++ b/csit.infra.dash/app/cdash/data/data.yaml
@@ -193,8 +193,16 @@
- result_pdr_lower_rate_value
- result_ndr_lower_rate_unit
- result_ndr_lower_rate_value
+ - result_latency_reverse_pdr_90_hdrh
+ - result_latency_reverse_pdr_50_hdrh
+ - result_latency_reverse_pdr_10_hdrh
+ - result_latency_reverse_pdr_0_hdrh
+ - result_latency_forward_pdr_90_hdrh
- result_latency_forward_pdr_50_avg
+ - result_latency_forward_pdr_50_hdrh
- result_latency_forward_pdr_50_unit
+ - result_latency_forward_pdr_10_hdrh
+ - result_latency_forward_pdr_0_hdrh
categories:
- job
- build
@@ -220,8 +228,16 @@
- result_pdr_lower_rate_value
- result_ndr_lower_rate_unit
- result_ndr_lower_rate_value
+ - result_latency_reverse_pdr_90_hdrh
+ - result_latency_reverse_pdr_50_hdrh
+ - result_latency_reverse_pdr_10_hdrh
+ - result_latency_reverse_pdr_0_hdrh
+ - result_latency_forward_pdr_90_hdrh
- result_latency_forward_pdr_50_avg
+ - result_latency_forward_pdr_50_hdrh
- result_latency_forward_pdr_50_unit
+ - result_latency_forward_pdr_10_hdrh
+ - result_latency_forward_pdr_0_hdrh
categories:
- job
- build
@@ -247,8 +263,16 @@
- result_pdr_lower_rate_value
- result_ndr_lower_rate_unit
- result_ndr_lower_rate_value
+ - result_latency_reverse_pdr_90_hdrh
+ - result_latency_reverse_pdr_50_hdrh
+ - result_latency_reverse_pdr_10_hdrh
+ - result_latency_reverse_pdr_0_hdrh
+ - result_latency_forward_pdr_90_hdrh
- result_latency_forward_pdr_50_avg
+ - result_latency_forward_pdr_50_hdrh
- result_latency_forward_pdr_50_unit
+ - result_latency_forward_pdr_10_hdrh
+ - result_latency_forward_pdr_0_hdrh
categories:
- job
- build
diff --git a/csit.infra.dash/app/cdash/report/graphs.py b/csit.infra.dash/app/cdash/report/graphs.py
index 544fdf0b54..f45e17ff33 100644
--- a/csit.infra.dash/app/cdash/report/graphs.py
+++ b/csit.infra.dash/app/cdash/report/graphs.py
@@ -20,7 +20,7 @@ import pandas as pd
from copy import deepcopy
from ..utils.constants import Constants as C
-from ..utils.utils import get_color
+from ..utils.utils import get_color, get_hdrh_latencies
def select_iterative_data(data: pd.DataFrame, itm:dict) -> pd.DataFrame:
@@ -145,6 +145,12 @@ def graph_iterative(data: pd.DataFrame, sel:dict, layout: dict,
show_tput = True
if ttype == "pdr":
+ customdata = list()
+ for _, row in itm_data.iterrows():
+ customdata.append(
+ get_hdrh_latencies(row, f"{row['job']}/{row['build']}")
+ )
+
y_lat_row = itm_data[C.VALUE_ITER["latency"]].to_list()
y_lat = [(y / norm_factor) for y in y_lat_row]
if y_lat:
@@ -164,7 +170,8 @@ def graph_iterative(data: pd.DataFrame, sel:dict, layout: dict,
hoverinfo="all",
boxpoints="all",
jitter=0.3,
- marker=dict(color=get_color(idx))
+ marker=dict(color=get_color(idx)),
+ customdata=customdata
)
x_lat.append(idx + 1)
lat_traces.append(go.Box(**lat_kwargs))
diff --git a/csit.infra.dash/app/cdash/report/layout.py b/csit.infra.dash/app/cdash/report/layout.py
index 2f954b611b..8dbaea3508 100644
--- a/csit.infra.dash/app/cdash/report/layout.py
+++ b/csit.infra.dash/app/cdash/report/layout.py
@@ -31,7 +31,7 @@ from ..utils.constants import Constants as C
from ..utils.control_panel import ControlPanel
from ..utils.trigger import Trigger
from ..utils.utils import show_tooltip, label, sync_checklists, gen_new_url, \
- generate_options, get_list_group_items
+ generate_options, get_list_group_items, graph_hdrh_latency
from ..utils.url_processing import url_decode
from .graphs import graph_iterative, select_iterative_data
@@ -279,6 +279,20 @@ class Layout:
self._add_ctrl_col(),
self._add_plotting_col()
]
+ ),
+ dbc.Spinner(
+ dbc.Offcanvas(
+ class_name="w-50",
+ id="offcanvas-metadata",
+ title="Throughput And Latency",
+ placement="end",
+ is_open=False,
+ children=[
+ dbc.Row(id="metadata-tput-lat"),
+ dbc.Row(id="metadata-hdrh-graph")
+ ]
+ ),
+ delay_show=C.SPINNER_DELAY
)
]
)
@@ -1320,3 +1334,112 @@ class Layout:
df = pd.concat([df, sel_data], ignore_index=True)
return dcc.send_data_frame(df.to_csv, C.REPORT_DOWNLOAD_FILE_NAME)
+
+ @app.callback(
+ Output("metadata-tput-lat", "children"),
+ Output("metadata-hdrh-graph", "children"),
+ Output("offcanvas-metadata", "is_open"),
+ Input({"type": "graph", "index": ALL}, "clickData"),
+ prevent_initial_call=True
+ )
+ def _show_metadata_from_graphs(graph_data: dict) -> tuple:
+ """Generates the data for the offcanvas displayed when a particular
+ point in a graph is clicked on.
+
+ :param graph_data: The data from the clicked point in the graph.
+ :type graph_data: dict
+ :returns: The data to be displayed on the offcanvas and the
+ information to show the offcanvas.
+ :rtype: tuple(list, list, bool)
+ """
+
+ trigger = Trigger(callback_context.triggered)
+
+ try:
+ idx = 0 if trigger.idx == "tput" else 1
+ graph_data = graph_data[idx]["points"]
+ except (IndexError, KeyError, ValueError, TypeError):
+ raise PreventUpdate
+
+ def _process_stats(data: list, param: str) -> list:
+ """Process statistical data provided by plot.ly box graph.
+
+ :param data: Statistical data provided by plot.ly box graph.
+ :param param: Parameter saying if the data come from "tput" or
+ "lat" graph.
+ :type data: list
+ :type param: str
+ :returns: Listo of tuples where the first value is the
+ statistic's name and the secont one it's value.
+ :rtype: list
+ """
+ if len(data) == 7:
+ stats = ("max", "upper fence", "q3", "median", "q1",
+ "lower fence", "min")
+ elif len(data) == 9:
+ stats = ("outlier", "max", "upper fence", "q3", "median",
+ "q1", "lower fence", "min", "outlier")
+ elif len(data) == 1:
+ if param == "lat":
+ stats = ("Average Latency at 50% PDR", )
+ else:
+ stats = ("Throughput", )
+ else:
+ return list()
+ unit = " [us]" if param == "lat" else str()
+ return [(f"{stat}{unit}", f"{value['y']:,.0f}")
+ for stat, value in zip(stats, data)]
+
+ graph = list()
+ if trigger.idx == "tput":
+ title = "Throughput"
+ elif trigger.idx == "lat":
+ title = "Latency"
+ if len(graph_data) == 1:
+ hdrh_data = graph_data[0].get("customdata", None)
+ if hdrh_data:
+ graph = [dbc.Card(
+ class_name="gy-2 p-0",
+ children=[
+ dbc.CardHeader(hdrh_data.pop("name")),
+ dbc.CardBody(children=[
+ dcc.Graph(
+ id="hdrh-latency-graph",
+ figure=graph_hdrh_latency(
+ hdrh_data, self._graph_layout
+ )
+ )
+ ])
+ ])
+ ]
+ else:
+ raise PreventUpdate
+ metadata = [
+ dbc.Card(
+ class_name="gy-2 p-0",
+ children=[
+ dbc.CardHeader(children=[
+ dcc.Clipboard(
+ target_id="tput-lat-metadata",
+ title="Copy",
+ style={"display": "inline-block"}
+ ),
+ title
+ ]),
+ dbc.CardBody(
+ id="tput-lat-metadata",
+ class_name="p-0",
+ children=[dbc.ListGroup(
+ [
+ dbc.ListGroupItem([dbc.Badge(k), v])
+ for k, v in _process_stats(
+ graph_data, trigger.idx)
+ ],
+ flush=True)
+ ]
+ )
+ ]
+ )
+ ]
+
+ return metadata, graph, True
diff --git a/csit.infra.dash/app/cdash/trending/graphs.py b/csit.infra.dash/app/cdash/trending/graphs.py
index 10ad745e1e..fc26f8bd79 100644
--- a/csit.infra.dash/app/cdash/trending/graphs.py
+++ b/csit.infra.dash/app/cdash/trending/graphs.py
@@ -17,32 +17,8 @@
import plotly.graph_objects as go
import pandas as pd
-import hdrh.histogram
-import hdrh.codec
-
from ..utils.constants import Constants as C
-from ..utils.utils import classify_anomalies, get_color
-
-
-def _get_hdrh_latencies(row: pd.Series, name: str) -> dict:
- """Get the HDRH latencies from the test data.
-
- :param row: A row fron the data frame with test data.
- :param name: The test name to be displayed as the graph title.
- :type row: pandas.Series
- :type name: str
- :returns: Dictionary with HDRH latencies.
- :rtype: dict
- """
-
- latencies = {"name": name}
- for key in C.LAT_HDRH:
- try:
- latencies[key] = row[key]
- except KeyError:
- return None
-
- return latencies
+from ..utils.utils import classify_anomalies, get_color, get_hdrh_latencies
def select_trending_data(data: pd.DataFrame, itm: dict) -> pd.DataFrame:
@@ -189,7 +165,7 @@ def graph_trending(
).replace("<stdev>", stdev).replace("<additional-info>", add_info)
hover.append(hover_itm)
if ttype == "latency":
- customdata_samples.append(_get_hdrh_latencies(row, name))
+ customdata_samples.append(get_hdrh_latencies(row, name))
customdata.append({"name": name})
else:
customdata_samples.append(
@@ -367,83 +343,6 @@ def graph_trending(
return fig_tput, fig_lat
-def graph_hdrh_latency(data: dict, layout: dict) -> go.Figure:
- """Generate HDR Latency histogram graphs.
-
- :param data: HDRH data.
- :param layout: Layout of plot.ly graph.
- :type data: dict
- :type layout: dict
- :returns: HDR latency Histogram.
- :rtype: plotly.graph_objects.Figure
- """
-
- fig = None
-
- traces = list()
- for idx, (lat_name, lat_hdrh) in enumerate(data.items()):
- try:
- decoded = hdrh.histogram.HdrHistogram.decode(lat_hdrh)
- except (hdrh.codec.HdrLengthException, TypeError):
- continue
- previous_x = 0.0
- prev_perc = 0.0
- xaxis = list()
- yaxis = list()
- hovertext = list()
- for item in decoded.get_recorded_iterator():
- # The real value is "percentile".
- # For 100%, we cut that down to "x_perc" to avoid
- # infinity.
- percentile = item.percentile_level_iterated_to
- x_perc = min(percentile, C.PERCENTILE_MAX)
- xaxis.append(previous_x)
- yaxis.append(item.value_iterated_to)
- hovertext.append(
- f"<b>{C.GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
- f"Direction: {('W-E', 'E-W')[idx % 2]}<br>"
- f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
- f"Latency: {item.value_iterated_to}uSec"
- )
- next_x = 100.0 / (100.0 - x_perc)
- xaxis.append(next_x)
- yaxis.append(item.value_iterated_to)
- hovertext.append(
- f"<b>{C.GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
- f"Direction: {('W-E', 'E-W')[idx % 2]}<br>"
- f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
- f"Latency: {item.value_iterated_to}uSec"
- )
- previous_x = next_x
- prev_perc = percentile
-
- traces.append(
- go.Scatter(
- x=xaxis,
- y=yaxis,
- name=C.GRAPH_LAT_HDRH_DESC[lat_name],
- mode="lines",
- legendgroup=C.GRAPH_LAT_HDRH_DESC[lat_name],
- showlegend=bool(idx % 2),
- line=dict(
- color=get_color(int(idx/2)),
- dash="solid",
- width=1 if idx % 2 else 2
- ),
- hovertext=hovertext,
- hoverinfo="text"
- )
- )
- if traces:
- fig = go.Figure()
- fig.add_traces(traces)
- layout_hdrh = layout.get("plot-hdrh-latency", None)
- if lat_hdrh:
- fig.update_layout(layout_hdrh)
-
- return fig
-
-
def graph_tm_trending(data: pd.DataFrame, layout: dict) -> list:
"""Generates one trending graph per test, each graph includes all selected
metrics.
diff --git a/csit.infra.dash/app/cdash/trending/layout.py b/csit.infra.dash/app/cdash/trending/layout.py
index e4b7094de3..411061470e 100644
--- a/csit.infra.dash/app/cdash/trending/layout.py
+++ b/csit.infra.dash/app/cdash/trending/layout.py
@@ -34,10 +34,9 @@ from ..utils.control_panel import ControlPanel
from ..utils.trigger import Trigger
from ..utils.telemetry_data import TelemetryData
from ..utils.utils import show_tooltip, label, sync_checklists, gen_new_url, \
- generate_options, get_list_group_items
+ generate_options, get_list_group_items, graph_hdrh_latency
from ..utils.url_processing import url_decode
-from .graphs import graph_trending, graph_hdrh_latency, select_trending_data, \
- graph_tm_trending
+from .graphs import graph_trending, select_trending_data, graph_tm_trending
# Control panel partameters and their default values.
diff --git a/csit.infra.dash/app/cdash/utils/utils.py b/csit.infra.dash/app/cdash/utils/utils.py
index 68099987d2..d9347b1c13 100644
--- a/csit.infra.dash/app/cdash/utils/utils.py
+++ b/csit.infra.dash/app/cdash/utils/utils.py
@@ -15,8 +15,12 @@
"""
import pandas as pd
+import plotly.graph_objects as go
import dash_bootstrap_components as dbc
+import hdrh.histogram
+import hdrh.codec
+
from math import sqrt
from numpy import isnan
from dash import dcc
@@ -393,6 +397,7 @@ def get_list_group_items(
return children
+
def relative_change_stdev(mean1, mean2, std1, std2):
"""Compute relative standard deviation of change of two values.
@@ -417,3 +422,101 @@ def relative_change_stdev(mean1, mean2, std1, std2):
second = std2 / mean2
std = quotient * sqrt(first * first + second * second)
return (quotient - 1) * 100, std * 100
+
+
+def get_hdrh_latencies(row: pd.Series, name: str) -> dict:
+ """Get the HDRH latencies from the test data.
+
+ :param row: A row fron the data frame with test data.
+ :param name: The test name to be displayed as the graph title.
+ :type row: pandas.Series
+ :type name: str
+ :returns: Dictionary with HDRH latencies.
+ :rtype: dict
+ """
+
+ latencies = {"name": name}
+ for key in C.LAT_HDRH:
+ try:
+ latencies[key] = row[key]
+ except KeyError:
+ return None
+
+ return latencies
+
+
+def graph_hdrh_latency(data: dict, layout: dict) -> go.Figure:
+ """Generate HDR Latency histogram graphs.
+
+ :param data: HDRH data.
+ :param layout: Layout of plot.ly graph.
+ :type data: dict
+ :type layout: dict
+ :returns: HDR latency Histogram.
+ :rtype: plotly.graph_objects.Figure
+ """
+
+ fig = None
+
+ traces = list()
+ for idx, (lat_name, lat_hdrh) in enumerate(data.items()):
+ try:
+ decoded = hdrh.histogram.HdrHistogram.decode(lat_hdrh)
+ except (hdrh.codec.HdrLengthException, TypeError):
+ continue
+ previous_x = 0.0
+ prev_perc = 0.0
+ xaxis = list()
+ yaxis = list()
+ hovertext = list()
+ for item in decoded.get_recorded_iterator():
+ # The real value is "percentile".
+ # For 100%, we cut that down to "x_perc" to avoid
+ # infinity.
+ percentile = item.percentile_level_iterated_to
+ x_perc = min(percentile, C.PERCENTILE_MAX)
+ xaxis.append(previous_x)
+ yaxis.append(item.value_iterated_to)
+ hovertext.append(
+ f"<b>{C.GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
+ f"Direction: {('W-E', 'E-W')[idx % 2]}<br>"
+ f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
+ f"Latency: {item.value_iterated_to}uSec"
+ )
+ next_x = 100.0 / (100.0 - x_perc)
+ xaxis.append(next_x)
+ yaxis.append(item.value_iterated_to)
+ hovertext.append(
+ f"<b>{C.GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
+ f"Direction: {('W-E', 'E-W')[idx % 2]}<br>"
+ f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
+ f"Latency: {item.value_iterated_to}uSec"
+ )
+ previous_x = next_x
+ prev_perc = percentile
+
+ traces.append(
+ go.Scatter(
+ x=xaxis,
+ y=yaxis,
+ name=C.GRAPH_LAT_HDRH_DESC[lat_name],
+ mode="lines",
+ legendgroup=C.GRAPH_LAT_HDRH_DESC[lat_name],
+ showlegend=bool(idx % 2),
+ line=dict(
+ color=get_color(int(idx/2)),
+ dash="solid",
+ width=1 if idx % 2 else 2
+ ),
+ hovertext=hovertext,
+ hoverinfo="text"
+ )
+ )
+ if traces:
+ fig = go.Figure()
+ fig.add_traces(traces)
+ layout_hdrh = layout.get("plot-hdrh-latency", None)
+ if lat_hdrh:
+ fig.update_layout(layout_hdrh)
+
+ return fig