diff options
Diffstat (limited to 'resources/tools/dash/app')
19 files changed, 493 insertions, 530 deletions
diff --git a/resources/tools/dash/app/pal/__init__.py b/resources/tools/dash/app/pal/__init__.py index 9f80c5fcaa..0eb2a4e79e 100644 --- a/resources/tools/dash/app/pal/__init__.py +++ b/resources/tools/dash/app/pal/__init__.py @@ -19,21 +19,8 @@ import logging from flask import Flask from flask_assets import Environment +from .utils.constants import Constants as C -# Maximal value of TIME_PERIOD for Trending in days. -# Do not change without a good reason. -MAX_TIME_PERIOD = 180 - -# It defines the time period for Trending in days from now back to the past from -# which data is read to dataframes. -# TIME_PERIOD = None means all data (max MAX_TIME_PERIOD days) is read. -# TIME_PERIOD = MAX_TIME_PERIOD is the default value -TIME_PERIOD = MAX_TIME_PERIOD # [days] - -# List of releases used for iterative data processing. -# The releases MUST be in the order from the current (newest) to the last -# (oldest). -RELEASES=["csit2206", "csit2202", ] def init_app(): """Construct core Flask application with embedded Dash app. @@ -58,10 +45,10 @@ def init_app(): assets.init_app(app) # Set the time period for Trending - if TIME_PERIOD is None or TIME_PERIOD > MAX_TIME_PERIOD: - time_period = MAX_TIME_PERIOD + if C.TIME_PERIOD is None or C.TIME_PERIOD > C.MAX_TIME_PERIOD: + time_period = C.MAX_TIME_PERIOD else: - time_period = TIME_PERIOD + time_period = C.TIME_PERIOD # Import Dash applications. from .news.news import init_news @@ -74,7 +61,7 @@ def init_app(): app = init_trending(app, time_period=time_period) from .report.report import init_report - app = init_report(app, releases=RELEASES) + app = init_report(app, releases=C.RELEASES) return app diff --git a/resources/tools/dash/app/pal/data/data.py b/resources/tools/dash/app/pal/data/data.py index 296db024c0..0956333e34 100644 --- a/resources/tools/dash/app/pal/data/data.py +++ b/resources/tools/dash/app/pal/data/data.py @@ -15,15 +15,13 @@ """ import logging +import awswrangler as wr from yaml import load, FullLoader, YAMLError from datetime import datetime, timedelta from time import time from pytz import UTC from pandas import DataFrame - -import awswrangler as wr - from awswrangler.exceptions import EmptyDataFrame, NoFilesFound diff --git a/resources/tools/dash/app/pal/news/layout.py b/resources/tools/dash/app/pal/news/layout.py index 2f66ce5c81..c9f2808a14 100644 --- a/resources/tools/dash/app/pal/news/layout.py +++ b/resources/tools/dash/app/pal/news/layout.py @@ -27,7 +27,9 @@ from yaml import load, FullLoader, YAMLError from copy import deepcopy from ..data.data import Data -from ..data.utils import classify_anomalies +from ..utils.constants import Constants as C +from ..utils.utils import classify_anomalies +from ..data.data import Data from .tables import table_news @@ -35,12 +37,6 @@ class Layout: """The layout of the dash app and the callbacks. """ - # The default job displayed when the page is loaded first time. - DEFAULT_JOB = "csit-vpp-perf-mrr-daily-master-2n-icx" - - # Time period for regressions and progressions. - TIME_PERIOD = 21 # [days] - def __init__(self, app: Flask, html_layout_file: str, data_spec_file: str, tooltip_file: str) -> None: """Initialization: @@ -73,7 +69,7 @@ class Layout: data_stats, data_mrr, data_ndrpdr = Data( data_spec_file=self._data_spec_file, debug=True - ).read_stats(days=self.TIME_PERIOD) + ).read_stats(days=C.NEWS_TIME_PERIOD) df_tst_info = pd.concat([data_mrr, data_ndrpdr], ignore_index=True) @@ -95,7 +91,7 @@ class Layout: job_info["tbed"].append("-".join(lst_job[-2:])) self.df_job_info = pd.DataFrame.from_dict(job_info) - self._default = self._set_job_params(self.DEFAULT_JOB) + self._default = self._set_job_params(C.NEWS_DEFAULT_JOB) # Pre-process the data: diff --git a/resources/tools/dash/app/pal/news/news.py b/resources/tools/dash/app/pal/news/news.py index deb00c1810..5e65b62281 100644 --- a/resources/tools/dash/app/pal/news/news.py +++ b/resources/tools/dash/app/pal/news/news.py @@ -14,8 +14,8 @@ """Instantiate the Statistics Dash applocation. """ import dash -import dash_bootstrap_components as dbc +from ..utils.constants import Constants as C from .layout import Layout @@ -30,15 +30,15 @@ def init_news(server): dash_app = dash.Dash( server=server, - routes_pathname_prefix=u"/news/", - external_stylesheets=[dbc.themes.LUX], + routes_pathname_prefix=C.NEWS_ROUTES_PATHNAME_PREFIX, + external_stylesheets=C.EXTERNAL_STYLESHEETS ) layout = Layout( app=dash_app, - html_layout_file="pal/templates/news_layout.jinja2", - data_spec_file="pal/data/data.yaml", - tooltip_file="pal/data/tooltips.yaml", + html_layout_file=C.NEWS_HTML_LAYOUT_FILE, + data_spec_file=C.DATA_SPEC_FILE, + tooltip_file=C.TOOLTIP_FILE, ) dash_app.index_string = layout.html_layout dash_app.layout = layout.add_content() diff --git a/resources/tools/dash/app/pal/news/tables.py b/resources/tools/dash/app/pal/news/tables.py index 53b24608d5..6272814165 100644 --- a/resources/tools/dash/app/pal/news/tables.py +++ b/resources/tools/dash/app/pal/news/tables.py @@ -17,9 +17,7 @@ import pandas as pd import dash_bootstrap_components as dbc - -# Time period for regressions and progressions. -TIME_PERIOD = 21 # [days] +from ..utils.constants import Constants as C def table_news(data: pd.DataFrame, job: str) -> list: @@ -57,7 +55,7 @@ def table_news(data: pd.DataFrame, job: str) -> list: class_name="p-0", size="lg", children=( - f"Regressions during the last {TIME_PERIOD} days " + f"Regressions during the last {C.NEWS_TIME_PERIOD} days " f"({len(regressions['Test Name'])})" ) ), @@ -68,7 +66,7 @@ def table_news(data: pd.DataFrame, job: str) -> list: class_name="p-0", size="lg", children=( - f"Progressions during the last {TIME_PERIOD} days " + f"Progressions during the last {C.NEWS_TIME_PERIOD} days " f"({len(progressions['Test Name'])})" ) ), diff --git a/resources/tools/dash/app/pal/report/graphs.py b/resources/tools/dash/app/pal/report/graphs.py index 76aa8b7793..c5d8f8f2d7 100644 --- a/resources/tools/dash/app/pal/report/graphs.py +++ b/resources/tools/dash/app/pal/report/graphs.py @@ -20,75 +20,13 @@ import pandas as pd from copy import deepcopy -import hdrh.histogram -import hdrh.codec - - -_NORM_FREQUENCY = 2.0 # [GHz] -_FREQURENCY = { # [GHz] - "2n-aws": 1.000, - "2n-dnv": 2.000, - "2n-clx": 2.300, - "2n-icx": 2.600, - "2n-skx": 2.500, - "2n-tx2": 2.500, - "2n-zn2": 2.900, - "3n-alt": 3.000, - "3n-aws": 1.000, - "3n-dnv": 2.000, - "3n-icx": 2.600, - "3n-skx": 2.500, - "3n-tsh": 2.200 -} - -_VALUE = { - "mrr": "result_receive_rate_rate_values", - "ndr": "result_ndr_lower_rate_value", - "pdr": "result_pdr_lower_rate_value", - "pdr-lat": "result_latency_forward_pdr_50_avg" -} -_UNIT = { - "mrr": "result_receive_rate_rate_unit", - "ndr": "result_ndr_lower_rate_unit", - "pdr": "result_pdr_lower_rate_unit", - "pdr-lat": "result_latency_forward_pdr_50_unit" -} -_LAT_HDRH = ( # Do not change the order - "result_latency_forward_pdr_0_hdrh", - "result_latency_reverse_pdr_0_hdrh", - "result_latency_forward_pdr_10_hdrh", - "result_latency_reverse_pdr_10_hdrh", - "result_latency_forward_pdr_50_hdrh", - "result_latency_reverse_pdr_50_hdrh", - "result_latency_forward_pdr_90_hdrh", - "result_latency_reverse_pdr_90_hdrh", -) -# This value depends on latency stream rate (9001 pps) and duration (5s). -# Keep it slightly higher to ensure rounding errors to not remove tick mark. -PERCENTILE_MAX = 99.999501 - -_GRAPH_LAT_HDRH_DESC = { - "result_latency_forward_pdr_0_hdrh": "No-load.", - "result_latency_reverse_pdr_0_hdrh": "No-load.", - "result_latency_forward_pdr_10_hdrh": "Low-load, 10% PDR.", - "result_latency_reverse_pdr_10_hdrh": "Low-load, 10% PDR.", - "result_latency_forward_pdr_50_hdrh": "Mid-load, 50% PDR.", - "result_latency_reverse_pdr_50_hdrh": "Mid-load, 50% PDR.", - "result_latency_forward_pdr_90_hdrh": "High-load, 90% PDR.", - "result_latency_reverse_pdr_90_hdrh": "High-load, 90% PDR." -} +from ..utils.constants import Constants as C def _get_color(idx: int) -> str: """ """ - _COLORS = ( - "#1A1110", "#DA2647", "#214FC6", "#01786F", "#BD8260", "#FFD12A", - "#A6E7FF", "#738276", "#C95A49", "#FC5A8D", "#CEC8EF", "#391285", - "#6F2DA8", "#FF878D", "#45A27D", "#FFD0B9", "#FD5240", "#DB91EF", - "#44D7A8", "#4F86F7", "#84DE02", "#FFCFF1", "#614051" - ) - return _COLORS[idx % len(_COLORS)] + return C.PLOT_COLORS[idx % len(C.PLOT_COLORS)] def get_short_version(version: str, dut_type: str="vpp") -> str: @@ -182,16 +120,16 @@ def graph_iterative(data: pd.DataFrame, sel:dict, layout: dict, continue phy = itm["phy"].split("-") topo_arch = f"{phy[0]}-{phy[1]}" if len(phy) == 4 else str() - norm_factor = (_NORM_FREQUENCY / _FREQURENCY[topo_arch]) \ + norm_factor = (C.NORM_FREQUENCY / C.FREQUENCY[topo_arch]) \ if normalize else 1.0 if itm["testtype"] == "mrr": - y_data_raw = itm_data[_VALUE[itm["testtype"]]].to_list()[0] + y_data_raw = itm_data[C.VALUE_ITER[itm["testtype"]]].to_list()[0] y_data = [(y * norm_factor) for y in y_data_raw] if len(y_data) > 0: y_tput_max = \ max(y_data) if max(y_data) > y_tput_max else y_tput_max else: - y_data_raw = itm_data[_VALUE[itm["testtype"]]].to_list() + y_data_raw = itm_data[C.VALUE_ITER[itm["testtype"]]].to_list() y_data = [(y * norm_factor) for y in y_data_raw] if y_data: y_tput_max = \ @@ -214,7 +152,7 @@ def graph_iterative(data: pd.DataFrame, sel:dict, layout: dict, show_tput = True if itm["testtype"] == "pdr": - y_lat_row = itm_data[_VALUE["pdr-lat"]].to_list() + y_lat_row = itm_data[C.VALUE_ITER["pdr-lat"]].to_list() y_lat = [(y / norm_factor) for y in y_lat_row] if y_lat: y_lat_max = max(y_lat) if max(y_lat) > y_lat_max else y_lat_max @@ -302,73 +240,3 @@ def table_comparison(data: pd.DataFrame, sel:dict, ) return pd.DataFrame() #table - - -def graph_hdrh_latency(data: dict, layout: dict) -> go.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) as err: - 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, PERCENTILE_MAX) - xaxis.append(previous_x) - yaxis.append(item.value_iterated_to) - hovertext.append( - f"<b>{_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>{_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=_GRAPH_LAT_HDRH_DESC[lat_name], - mode="lines", - legendgroup=_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 diff --git a/resources/tools/dash/app/pal/report/layout.py b/resources/tools/dash/app/pal/report/layout.py index e7c8db43ae..164f2d48d9 100644 --- a/resources/tools/dash/app/pal/report/layout.py +++ b/resources/tools/dash/app/pal/report/layout.py @@ -27,8 +27,9 @@ from yaml import load, FullLoader, YAMLError from copy import deepcopy from ast import literal_eval +from ..utils.constants import Constants as C +from ..utils.url_processing import url_decode, url_encode from ..data.data import Data -from ..data.url_processing import url_decode, url_encode from .graphs import graph_iterative, table_comparison, get_short_version @@ -36,51 +37,6 @@ class Layout: """ """ - # If True, clear all inputs in control panel when button "ADD SELECTED" is - # pressed. - CLEAR_ALL_INPUTS = False - - STYLE_DISABLED = {"display": "none"} - STYLE_ENABLED = {"display": "inherit"} - - CL_ALL_DISABLED = [{ - "label": "All", - "value": "all", - "disabled": True - }] - CL_ALL_ENABLED = [{ - "label": "All", - "value": "all", - "disabled": False - }] - - PLACEHOLDER = html.Nobr("") - - DRIVERS = ("avf", "af-xdp", "rdma", "dpdk") - - LABELS = { - "dpdk": "DPDK", - "container_memif": "LXC/DRC Container Memif", - "crypto": "IPSec IPv4 Routing", - "ip4": "IPv4 Routing", - "ip6": "IPv6 Routing", - "ip4_tunnels": "IPv4 Tunnels", - "l2": "L2 Ethernet Switching", - "srv6": "SRv6 Routing", - "vm_vhost": "VMs vhost-user", - "nfv_density-dcr_memif-chain_ipsec": "CNF Service Chains Routing IPSec", - "nfv_density-vm_vhost-chain_dot1qip4vxlan":"VNF Service Chains Tunnels", - "nfv_density-vm_vhost-chain": "VNF Service Chains Routing", - "nfv_density-dcr_memif-pipeline": "CNF Service Pipelines Routing", - "nfv_density-dcr_memif-chain": "CNF Service Chains Routing", - } - - URL_STYLE = { - "background-color": "#d2ebf5", - "border-color": "#bce1f1", - "color": "#135d7c" - } - def __init__(self, app: Flask, releases: list, html_layout_file: str, graph_layout_file: str, data_spec_file: str, tooltip_file: str) -> None: """ @@ -125,7 +81,7 @@ class Layout: replace("2n-", "") test = lst_test_id[-1] nic = suite.split("-")[0] - for drv in self.DRIVERS: + for drv in C.DRIVERS: if drv in test: driver = drv.replace("-", "_") test = test.replace(f"{drv}-", "") @@ -234,7 +190,7 @@ class Layout: return self._graph_layout def label(self, key: str) -> str: - return self.LABELS.get(key, key) + return C.LABELS.get(key, key) def _show_tooltip(self, id: str, title: str, clipboard_id: str=None) -> list: @@ -359,9 +315,7 @@ class Layout: dbc.Row( # Throughput id="row-graph-tput", class_name="g-0 p-2", - children=[ - self.PLACEHOLDER - ] + children=[C.PLACEHOLDER, ] ), width=6 ), @@ -369,9 +323,7 @@ class Layout: dbc.Row( # Latency id="row-graph-lat", class_name="g-0 p-2", - children=[ - self.PLACEHOLDER - ] + children=[C.PLACEHOLDER, ] ), width=6 ) @@ -380,16 +332,12 @@ class Layout: dbc.Row( # Tables id="row-table", class_name="g-0 p-2", - children=[ - self.PLACEHOLDER - ] + children=[C.PLACEHOLDER, ] ), dbc.Row( # Download id="row-btn-download", class_name="g-0 p-2", - children=[ - self.PLACEHOLDER - ] + children=[C.PLACEHOLDER, ] ) ] ) @@ -548,7 +496,7 @@ class Layout: children=[ dbc.Checklist( id="cl-ctrl-framesize-all", - options=self.CL_ALL_DISABLED, + options=C.CL_ALL_DISABLED, inline=True, switch=False ), @@ -579,7 +527,7 @@ class Layout: children=[ dbc.Checklist( id="cl-ctrl-core-all", - options=self.CL_ALL_DISABLED, + options=C.CL_ALL_DISABLED, inline=False, switch=False ) @@ -610,7 +558,7 @@ class Layout: children=[ dbc.Checklist( id="cl-ctrl-testtype-all", - options=self.CL_ALL_DISABLED, + options=C.CL_ALL_DISABLED, inline=True, switch=False ), @@ -675,7 +623,7 @@ class Layout: dbc.Row( id="row-card-sel-tests", class_name="gy-1", - style=self.STYLE_DISABLED, + style=C.STYLE_DISABLED, children=[ dbc.Label( "Selected tests", @@ -692,7 +640,7 @@ class Layout: ), dbc.Row( id="row-btns-sel-tests", - style=self.STYLE_DISABLED, + style=C.STYLE_DISABLED, children=[ dbc.ButtonGroup( class_name="gy-2", @@ -722,12 +670,6 @@ class Layout: class ControlPanel: def __init__(self, panel: dict) -> None: - CL_ALL_DISABLED = [{ - "label": "All", - "value": "all", - "disabled": True - }] - # Defines also the order of keys self._defaults = { "dd-rls-value": str(), @@ -749,15 +691,15 @@ class Layout: "cl-core-options": list(), "cl-core-value": list(), "cl-core-all-value": list(), - "cl-core-all-options": CL_ALL_DISABLED, + "cl-core-all-options": C.CL_ALL_DISABLED, "cl-framesize-options": list(), "cl-framesize-value": list(), "cl-framesize-all-value": list(), - "cl-framesize-all-options": CL_ALL_DISABLED, + "cl-framesize-all-options": C.CL_ALL_DISABLED, "cl-testtype-options": list(), "cl-testtype-value": list(), "cl-testtype-all-value": list(), - "cl-testtype-all-options": CL_ALL_DISABLED, + "cl-testtype-all-options": C.CL_ALL_DISABLED, "btn-add-disabled": True, "cl-normalize-value": list(), "cl-selected-options": list() @@ -818,10 +760,10 @@ class Layout: (fig_tput, fig_lat) = figs - row_fig_tput = self.PLACEHOLDER - row_fig_lat = self.PLACEHOLDER - row_table = self.PLACEHOLDER - row_btn_dwnld = self.PLACEHOLDER + row_fig_tput = C.PLACEHOLDER + row_fig_lat = C.PLACEHOLDER + row_table = C.PLACEHOLDER + row_btn_dwnld = C.PLACEHOLDER if fig_tput: row_fig_tput = [ @@ -853,7 +795,7 @@ class Layout: class_name="me-1", children=[ dbc.InputGroupText( - style=self.URL_STYLE, + style=C.URL_STYLE, children=self._show_tooltip( "help-url", "URL", "input-url") ), @@ -861,7 +803,7 @@ class Layout: id="input-url", readonly=True, type="url", - style=self.URL_STYLE, + style=C.URL_STYLE, value=url ) ] @@ -1019,15 +961,15 @@ class Layout: "cl-core-options": list(), "cl-core-value": list(), "cl-core-all-value": list(), - "cl-core-all-options": self.CL_ALL_DISABLED, + "cl-core-all-options": C.CL_ALL_DISABLED, "cl-framesize-options": list(), "cl-framesize-value": list(), "cl-framesize-all-value": list(), - "cl-framesize-all-options": self.CL_ALL_DISABLED, + "cl-framesize-all-options": C.CL_ALL_DISABLED, "cl-testtype-options": list(), "cl-testtype-value": list(), "cl-testtype-all-value": list(), - "cl-testtype-all-options": self.CL_ALL_DISABLED + "cl-testtype-all-options": C.CL_ALL_DISABLED }) elif trigger_id == "dd-ctrl-dut": try: @@ -1058,15 +1000,15 @@ class Layout: "cl-core-options": list(), "cl-core-value": list(), "cl-core-all-value": list(), - "cl-core-all-options": self.CL_ALL_DISABLED, + "cl-core-all-options": C.CL_ALL_DISABLED, "cl-framesize-options": list(), "cl-framesize-value": list(), "cl-framesize-all-value": list(), - "cl-framesize-all-options": self.CL_ALL_DISABLED, + "cl-framesize-all-options": C.CL_ALL_DISABLED, "cl-testtype-options": list(), "cl-testtype-value": list(), "cl-testtype-all-value": list(), - "cl-testtype-all-options": self.CL_ALL_DISABLED + "cl-testtype-all-options": C.CL_ALL_DISABLED }) elif trigger_id == "dd-ctrl-dutver": try: @@ -1095,15 +1037,15 @@ class Layout: "cl-core-options": list(), "cl-core-value": list(), "cl-core-all-value": list(), - "cl-core-all-options": self.CL_ALL_DISABLED, + "cl-core-all-options": C.CL_ALL_DISABLED, "cl-framesize-options": list(), "cl-framesize-value": list(), "cl-framesize-all-value": list(), - "cl-framesize-all-options": self.CL_ALL_DISABLED, + "cl-framesize-all-options": C.CL_ALL_DISABLED, "cl-testtype-options": list(), "cl-testtype-value": list(), "cl-testtype-all-value": list(), - "cl-testtype-all-options": self.CL_ALL_DISABLED + "cl-testtype-all-options": C.CL_ALL_DISABLED }) elif trigger_id == "dd-ctrl-phy": try: @@ -1131,15 +1073,15 @@ class Layout: "cl-core-options": list(), "cl-core-value": list(), "cl-core-all-value": list(), - "cl-core-all-options": self.CL_ALL_DISABLED, + "cl-core-all-options": C.CL_ALL_DISABLED, "cl-framesize-options": list(), "cl-framesize-value": list(), "cl-framesize-all-value": list(), - "cl-framesize-all-options": self.CL_ALL_DISABLED, + "cl-framesize-all-options": C.CL_ALL_DISABLED, "cl-testtype-options": list(), "cl-testtype-value": list(), "cl-testtype-all-value": list(), - "cl-testtype-all-options": self.CL_ALL_DISABLED + "cl-testtype-all-options": C.CL_ALL_DISABLED }) elif trigger_id == "dd-ctrl-area": try: @@ -1164,15 +1106,15 @@ class Layout: "cl-core-options": list(), "cl-core-value": list(), "cl-core-all-value": list(), - "cl-core-all-options": self.CL_ALL_DISABLED, + "cl-core-all-options": C.CL_ALL_DISABLED, "cl-framesize-options": list(), "cl-framesize-value": list(), "cl-framesize-all-value": list(), - "cl-framesize-all-options": self.CL_ALL_DISABLED, + "cl-framesize-all-options": C.CL_ALL_DISABLED, "cl-testtype-options": list(), "cl-testtype-value": list(), "cl-testtype-all-value": list(), - "cl-testtype-all-options": self.CL_ALL_DISABLED + "cl-testtype-all-options": C.CL_ALL_DISABLED }) elif trigger_id == "dd-ctrl-test": rls = ctrl_panel.get("dd-rls-value") @@ -1188,17 +1130,17 @@ class Layout: for v in sorted(test["core"])], "cl-core-value": list(), "cl-core-all-value": list(), - "cl-core-all-options": self.CL_ALL_ENABLED, + "cl-core-all-options": C.CL_ALL_ENABLED, "cl-framesize-options": [{"label": v, "value": v} for v in sorted(test["frame-size"])], "cl-framesize-value": list(), "cl-framesize-all-value": list(), - "cl-framesize-all-options": self.CL_ALL_ENABLED, + "cl-framesize-all-options": C.CL_ALL_ENABLED, "cl-testtype-options": [{"label": v, "value": v} for v in sorted(test["test-type"])], "cl-testtype-value": list(), "cl-testtype-all-value": list(), - "cl-testtype-all-options": self.CL_ALL_ENABLED, + "cl-testtype-all-options": C.CL_ALL_ENABLED, }) elif trigger_id == "cl-ctrl-core": val_sel, val_all = self._sync_checklists( @@ -1305,21 +1247,21 @@ class Layout: "testtype": ttype.lower() }) store_sel = sorted(store_sel, key=lambda d: d["id"]) - row_card_sel_tests = self.STYLE_ENABLED - row_btns_sel_tests = self.STYLE_ENABLED - if self.CLEAR_ALL_INPUTS: + row_card_sel_tests = C.STYLE_ENABLED + row_btns_sel_tests = C.STYLE_ENABLED + if C.CLEAR_ALL_INPUTS: ctrl_panel.set(ctrl_panel.defaults) ctrl_panel.set({ "cl-selected-options": self._list_tests(store_sel) }) elif trigger_id == "btn-sel-remove-all": _ = btn_remove_all - row_fig_tput = self.PLACEHOLDER - row_fig_lat = self.PLACEHOLDER - row_table = self.PLACEHOLDER - row_btn_dwnld = self.PLACEHOLDER - row_card_sel_tests = self.STYLE_DISABLED - row_btns_sel_tests = self.STYLE_DISABLED + row_fig_tput = C.PLACEHOLDER + row_fig_lat = C.PLACEHOLDER + row_table = C.PLACEHOLDER + row_btn_dwnld = C.PLACEHOLDER + row_card_sel_tests = C.STYLE_DISABLED + row_btns_sel_tests = C.STYLE_DISABLED store_sel = list() ctrl_panel.set({"cl-selected-options": list()}) elif trigger_id == "btn-sel-remove": @@ -1337,8 +1279,8 @@ class Layout: store_sel = literal_eval( url_params.get("store_sel", list())[0]) if store_sel: - row_card_sel_tests = self.STYLE_ENABLED - row_btns_sel_tests = self.STYLE_ENABLED + row_card_sel_tests = C.STYLE_ENABLED + row_btns_sel_tests = C.STYLE_ENABLED if trigger_id in ("btn-ctrl-add", "url", "btn-sel-remove", "cl-ctrl-normalize"): @@ -1358,12 +1300,12 @@ class Layout: "cl-selected-options": self._list_tests(store_sel) }) else: - row_fig_tput = self.PLACEHOLDER - row_fig_lat = self.PLACEHOLDER - row_table = self.PLACEHOLDER - row_btn_dwnld = self.PLACEHOLDER - row_card_sel_tests = self.STYLE_DISABLED - row_btns_sel_tests = self.STYLE_DISABLED + row_fig_tput = C.PLACEHOLDER + row_fig_lat = C.PLACEHOLDER + row_table = C.PLACEHOLDER + row_btn_dwnld = C.PLACEHOLDER + row_card_sel_tests = C.STYLE_DISABLED + row_btns_sel_tests = C.STYLE_DISABLED store_sel = list() ctrl_panel.set({"cl-selected-options": list()}) diff --git a/resources/tools/dash/app/pal/report/report.py b/resources/tools/dash/app/pal/report/report.py index c02b409973..c6008ca595 100644 --- a/resources/tools/dash/app/pal/report/report.py +++ b/resources/tools/dash/app/pal/report/report.py @@ -14,8 +14,8 @@ """Instantiate the Report Dash applocation. """ import dash -import dash_bootstrap_components as dbc +from ..utils.constants import Constants as C from .layout import Layout @@ -30,17 +30,17 @@ def init_report(server, releases): dash_app = dash.Dash( server=server, - routes_pathname_prefix=u"/report/", - external_stylesheets=[dbc.themes.LUX], + routes_pathname_prefix=C.REPORT_ROUTES_PATHNAME_PREFIX, + external_stylesheets=C.EXTERNAL_STYLESHEETS ) layout = Layout( app=dash_app, releases=releases, - html_layout_file="pal/templates/report_layout.jinja2", - graph_layout_file="pal/report/layout.yaml", - data_spec_file="pal/data/data.yaml", - tooltip_file="pal/data/tooltips.yaml" + html_layout_file=C.REPORT_HTML_LAYOUT_FILE, + graph_layout_file=C.REPORT_GRAPH_LAYOUT_FILE, + data_spec_file=C.DATA_SPEC_FILE, + tooltip_file=C.TOOLTIP_FILE, ) dash_app.index_string = layout.html_layout dash_app.layout = layout.add_content() diff --git a/resources/tools/dash/app/pal/routes.py b/resources/tools/dash/app/pal/routes.py index d4cd88ffce..59af748168 100644 --- a/resources/tools/dash/app/pal/routes.py +++ b/resources/tools/dash/app/pal/routes.py @@ -17,14 +17,16 @@ from flask import current_app as app from flask import render_template +from .utils.constants import Constants as C -@app.route("/") + +@app.route(C.APPLICATIN_ROOT) def home(): """Landing page. """ return render_template( - "index_layout.jinja2", - title="FD.io CSIT", - description="Performance Dashboard", - template="d-flex h-100 text-center text-white bg-dark" + C.MAIN_HTML_LAYOUT_FILE, + title=C.TITLE, + description=C.DESCRIPTION, + template=C.TEMPLATE ) diff --git a/resources/tools/dash/app/pal/stats/layout.py b/resources/tools/dash/app/pal/stats/layout.py index 5c3758ba76..03707c0394 100644 --- a/resources/tools/dash/app/pal/stats/layout.py +++ b/resources/tools/dash/app/pal/stats/layout.py @@ -28,8 +28,9 @@ from yaml import load, FullLoader, YAMLError from datetime import datetime, timedelta from copy import deepcopy +from ..utils.constants import Constants as C +from ..utils.url_processing import url_decode, url_encode from ..data.data import Data -from ..data.url_processing import url_decode, url_encode from .graphs import graph_statistics, select_data @@ -37,14 +38,6 @@ class Layout: """ """ - DEFAULT_JOB = "csit-vpp-perf-mrr-daily-master-2n-icx" - - URL_STYLE = { - "background-color": "#d2ebf5", - "border-color": "#bce1f1", - "color": "#135d7c" - } - def __init__(self, app: Flask, html_layout_file: str, graph_layout_file: str, data_spec_file: str, tooltip_file: str, time_period: int=None) -> None: @@ -95,7 +88,7 @@ class Layout: job_info["tbed"].append("-".join(lst_job[-2:])) self.df_job_info = pd.DataFrame.from_dict(job_info) - self._default = self._set_job_params(self.DEFAULT_JOB) + self._default = self._set_job_params(C.STATS_DEFAULT_JOB) tst_info = { "job": list(), @@ -431,7 +424,7 @@ class Layout: class_name="me-1", children=[ dbc.InputGroupText( - style=self.URL_STYLE, + style=C.URL_STYLE, children=self._show_tooltip( "help-url", "URL", "input-url") ), @@ -439,7 +432,7 @@ class Layout: id="input-url", readonly=True, type="url", - style=self.URL_STYLE, + style=C.URL_STYLE, value="" ) ] diff --git a/resources/tools/dash/app/pal/stats/stats.py b/resources/tools/dash/app/pal/stats/stats.py index 3da742d61e..560ec53f14 100644 --- a/resources/tools/dash/app/pal/stats/stats.py +++ b/resources/tools/dash/app/pal/stats/stats.py @@ -14,8 +14,8 @@ """Instantiate the Statistics Dash applocation. """ import dash -import dash_bootstrap_components as dbc +from ..utils.constants import Constants as C from .layout import Layout @@ -30,16 +30,16 @@ def init_stats(server, time_period=None): dash_app = dash.Dash( server=server, - routes_pathname_prefix=u"/stats/", - external_stylesheets=[dbc.themes.LUX], + routes_pathname_prefix=C.STATS_ROUTES_PATHNAME_PREFIX, + external_stylesheets=C.EXTERNAL_STYLESHEETS ) layout = Layout( app=dash_app, - html_layout_file="pal/templates/stats_layout.jinja2", - graph_layout_file="pal/stats/layout.yaml", - data_spec_file="pal/data/data.yaml", - tooltip_file="pal/data/tooltips.yaml", + html_layout_file=C.STATS_HTML_LAYOUT_FILE, + graph_layout_file=C.STATS_GRAPH_LAYOUT_FILE, + data_spec_file=C.DATA_SPEC_FILE, + tooltip_file=C.TOOLTIP_FILE, time_period=time_period ) dash_app.index_string = layout.html_layout diff --git a/resources/tools/dash/app/pal/trending/graphs.py b/resources/tools/dash/app/pal/trending/graphs.py index a63bebb818..0b4968082f 100644 --- a/resources/tools/dash/app/pal/trending/graphs.py +++ b/resources/tools/dash/app/pal/trending/graphs.py @@ -22,96 +22,14 @@ import hdrh.codec from datetime import datetime -from ..data.utils import classify_anomalies - -_NORM_FREQUENCY = 2.0 # [GHz] -_FREQURENCY = { # [GHz] - "2n-aws": 1.000, - "2n-dnv": 2.000, - "2n-clx": 2.300, - "2n-icx": 2.600, - "2n-skx": 2.500, - "2n-tx2": 2.500, - "2n-zn2": 2.900, - "3n-alt": 3.000, - "3n-aws": 1.000, - "3n-dnv": 2.000, - "3n-icx": 2.600, - "3n-skx": 2.500, - "3n-tsh": 2.200 -} - -_ANOMALY_COLOR = { - "regression": 0.0, - "normal": 0.5, - "progression": 1.0 -} -_COLORSCALE_TPUT = [ - [0.00, "red"], - [0.33, "red"], - [0.33, "white"], - [0.66, "white"], - [0.66, "green"], - [1.00, "green"] -] -_TICK_TEXT_TPUT = ["Regression", "Normal", "Progression"] -_COLORSCALE_LAT = [ - [0.00, "green"], - [0.33, "green"], - [0.33, "white"], - [0.66, "white"], - [0.66, "red"], - [1.00, "red"] -] -_TICK_TEXT_LAT = ["Progression", "Normal", "Regression"] -_VALUE = { - "mrr": "result_receive_rate_rate_avg", - "ndr": "result_ndr_lower_rate_value", - "pdr": "result_pdr_lower_rate_value", - "pdr-lat": "result_latency_forward_pdr_50_avg" -} -_UNIT = { - "mrr": "result_receive_rate_rate_unit", - "ndr": "result_ndr_lower_rate_unit", - "pdr": "result_pdr_lower_rate_unit", - "pdr-lat": "result_latency_forward_pdr_50_unit" -} -_LAT_HDRH = ( # Do not change the order - "result_latency_forward_pdr_0_hdrh", - "result_latency_reverse_pdr_0_hdrh", - "result_latency_forward_pdr_10_hdrh", - "result_latency_reverse_pdr_10_hdrh", - "result_latency_forward_pdr_50_hdrh", - "result_latency_reverse_pdr_50_hdrh", - "result_latency_forward_pdr_90_hdrh", - "result_latency_reverse_pdr_90_hdrh", -) -# This value depends on latency stream rate (9001 pps) and duration (5s). -# Keep it slightly higher to ensure rounding errors to not remove tick mark. -PERCENTILE_MAX = 99.999501 - -_GRAPH_LAT_HDRH_DESC = { - "result_latency_forward_pdr_0_hdrh": "No-load.", - "result_latency_reverse_pdr_0_hdrh": "No-load.", - "result_latency_forward_pdr_10_hdrh": "Low-load, 10% PDR.", - "result_latency_reverse_pdr_10_hdrh": "Low-load, 10% PDR.", - "result_latency_forward_pdr_50_hdrh": "Mid-load, 50% PDR.", - "result_latency_reverse_pdr_50_hdrh": "Mid-load, 50% PDR.", - "result_latency_forward_pdr_90_hdrh": "High-load, 90% PDR.", - "result_latency_reverse_pdr_90_hdrh": "High-load, 90% PDR." -} +from ..utils.constants import Constants as C +from ..utils.utils import classify_anomalies def _get_color(idx: int) -> str: """ """ - _COLORS = ( - "#1A1110", "#DA2647", "#214FC6", "#01786F", "#BD8260", "#FFD12A", - "#A6E7FF", "#738276", "#C95A49", "#FC5A8D", "#CEC8EF", "#391285", - "#6F2DA8", "#FF878D", "#45A27D", "#FFD0B9", "#FD5240", "#DB91EF", - "#44D7A8", "#4F86F7", "#84DE02", "#FFCFF1", "#614051" - ) - return _COLORS[idx % len(_COLORS)] + return C.PLOT_COLORS[idx % len(C.PLOT_COLORS)] def _get_hdrh_latencies(row: pd.Series, name: str) -> dict: @@ -119,7 +37,7 @@ def _get_hdrh_latencies(row: pd.Series, name: str) -> dict: """ latencies = {"name": name} - for key in _LAT_HDRH: + for key in C.LAT_HDRH: try: latencies[key] = row[key] except KeyError: @@ -176,7 +94,7 @@ def _generate_trending_traces(ttype: str, name: str, df: pd.DataFrame, """ """ - df = df.dropna(subset=[_VALUE[ttype], ]) + df = df.dropna(subset=[C.VALUE[ttype], ]) if df.empty: return list() df = df.loc[((df["start_time"] >= start) & (df["start_time"] <= end))] @@ -185,9 +103,9 @@ def _generate_trending_traces(ttype: str, name: str, df: pd.DataFrame, x_axis = df["start_time"].tolist() if ttype == "pdr-lat": - y_data = [(itm / norm_factor) for itm in df[_VALUE[ttype]].tolist()] + y_data = [(itm / norm_factor) for itm in df[C.VALUE[ttype]].tolist()] else: - y_data = [(itm * norm_factor) for itm in df[_VALUE[ttype]].tolist()] + y_data = [(itm * norm_factor) for itm in df[C.VALUE[ttype]].tolist()] anomalies, trend_avg, trend_stdev = classify_anomalies( {k: v for k, v in zip(x_axis, y_data)} @@ -199,7 +117,7 @@ def _generate_trending_traces(ttype: str, name: str, df: pd.DataFrame, d_type = "trex" if row["dut_type"] == "none" else row["dut_type"] hover_itm = ( f"date: {row['start_time'].strftime('%Y-%m-%d %H:%M:%S')}<br>" - f"<prop> [{row[_UNIT[ttype]]}]: {y_data[idx]:,.0f}<br>" + f"<prop> [{row[C.UNIT[ttype]]}]: {y_data[idx]:,.0f}<br>" f"<stdev>" f"{d_type}-ref: {row['dut_version']}<br>" f"csit-ref: {row['job']}/{row['build']}<br>" @@ -277,7 +195,7 @@ def _generate_trending_traces(ttype: str, name: str, df: pd.DataFrame, if anomaly in ("regression", "progression"): anomaly_x.append(x_axis[idx]) anomaly_y.append(trend_avg[idx]) - anomaly_color.append(_ANOMALY_COLOR[anomaly]) + anomaly_color.append(C.ANOMALY_COLOR[anomaly]) hover_itm = ( f"date: {x_axis[idx].strftime('%Y-%m-%d %H:%M:%S')}<br>" f"trend [pps]: {trend_avg[idx]:,.0f}<br>" @@ -301,8 +219,8 @@ def _generate_trending_traces(ttype: str, name: str, df: pd.DataFrame, "size": 15, "symbol": "circle-open", "color": anomaly_color, - "colorscale": _COLORSCALE_LAT \ - if ttype == "pdr-lat" else _COLORSCALE_TPUT, + "colorscale": C.COLORSCALE_LAT \ + if ttype == "pdr-lat" else C.COLORSCALE_TPUT, "showscale": True, "line": { "width": 2 @@ -314,8 +232,8 @@ def _generate_trending_traces(ttype: str, name: str, df: pd.DataFrame, "titleside": "right", "tickmode": "array", "tickvals": [0.167, 0.500, 0.833], - "ticktext": _TICK_TEXT_LAT \ - if ttype == "pdr-lat" else _TICK_TEXT_TPUT, + "ticktext": C.TICK_TEXT_LAT \ + if ttype == "pdr-lat" else C.TICK_TEXT_TPUT, "ticks": "", "ticklen": 0, "tickangle": -90, @@ -349,7 +267,7 @@ def graph_trending(data: pd.DataFrame, sel:dict, layout: dict, if normalize: phy = itm["phy"].split("-") topo_arch = f"{phy[0]}-{phy[1]}" if len(phy) == 4 else str() - norm_factor = (_NORM_FREQUENCY / _FREQURENCY[topo_arch]) \ + norm_factor = (C.NORM_FREQUENCY / C.FREQUENCY[topo_arch]) \ if topo_arch else 1.0 else: norm_factor = 1.0 @@ -400,11 +318,11 @@ def graph_hdrh_latency(data: dict, layout: dict) -> go.Figure: # For 100%, we cut that down to "x_perc" to avoid # infinity. percentile = item.percentile_level_iterated_to - x_perc = min(percentile, PERCENTILE_MAX) + x_perc = min(percentile, C.PERCENTILE_MAX) xaxis.append(previous_x) yaxis.append(item.value_iterated_to) hovertext.append( - f"<b>{_GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>" + 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" @@ -413,7 +331,7 @@ def graph_hdrh_latency(data: dict, layout: dict) -> go.Figure: xaxis.append(next_x) yaxis.append(item.value_iterated_to) hovertext.append( - f"<b>{_GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>" + 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" @@ -425,9 +343,9 @@ def graph_hdrh_latency(data: dict, layout: dict) -> go.Figure: go.Scatter( x=xaxis, y=yaxis, - name=_GRAPH_LAT_HDRH_DESC[lat_name], + name=C.GRAPH_LAT_HDRH_DESC[lat_name], mode="lines", - legendgroup=_GRAPH_LAT_HDRH_DESC[lat_name], + legendgroup=C.GRAPH_LAT_HDRH_DESC[lat_name], showlegend=bool(idx % 2), line=dict( color=_get_color(int(idx/2)), diff --git a/resources/tools/dash/app/pal/trending/layout.py b/resources/tools/dash/app/pal/trending/layout.py index d632820b99..48a8e5481e 100644 --- a/resources/tools/dash/app/pal/trending/layout.py +++ b/resources/tools/dash/app/pal/trending/layout.py @@ -30,8 +30,9 @@ from copy import deepcopy from json import loads, JSONDecodeError from ast import literal_eval +from ..utils.constants import Constants as C +from ..utils.url_processing import url_decode, url_encode from ..data.data import Data -from ..data.url_processing import url_decode, url_encode from .graphs import graph_trending, graph_hdrh_latency, \ select_trending_data @@ -40,51 +41,6 @@ class Layout: """ """ - # If True, clear all inputs in control panel when button "ADD SELECTED" is - # pressed. - CLEAR_ALL_INPUTS = False - - STYLE_DISABLED = {"display": "none"} - STYLE_ENABLED = {"display": "inherit"} - - CL_ALL_DISABLED = [{ - "label": "All", - "value": "all", - "disabled": True - }] - CL_ALL_ENABLED = [{ - "label": "All", - "value": "all", - "disabled": False - }] - - PLACEHOLDER = html.Nobr("") - - DRIVERS = ("avf", "af-xdp", "rdma", "dpdk") - - LABELS = { - "dpdk": "DPDK", - "container_memif": "LXC/DRC Container Memif", - "crypto": "IPSec IPv4 Routing", - "ip4": "IPv4 Routing", - "ip6": "IPv6 Routing", - "ip4_tunnels": "IPv4 Tunnels", - "l2": "L2 Ethernet Switching", - "srv6": "SRv6 Routing", - "vm_vhost": "VMs vhost-user", - "nfv_density-dcr_memif-chain_ipsec": "CNF Service Chains Routing IPSec", - "nfv_density-vm_vhost-chain_dot1qip4vxlan":"VNF Service Chains Tunnels", - "nfv_density-vm_vhost-chain": "VNF Service Chains Routing", - "nfv_density-dcr_memif-pipeline": "CNF Service Pipelines Routing", - "nfv_density-dcr_memif-chain": "CNF Service Chains Routing", - } - - URL_STYLE = { - "background-color": "#d2ebf5", - "border-color": "#bce1f1", - "color": "#135d7c" - } - def __init__(self, app: Flask, html_layout_file: str, graph_layout_file: str, data_spec_file: str, tooltip_file: str, time_period: str=None) -> None: @@ -135,7 +91,7 @@ class Layout: replace("2n-", "") test = lst_test[-1] nic = suite.split("-")[0] - for drv in self.DRIVERS: + for drv in C.DRIVERS: if drv in test: if drv == "af-xdp": driver = "af_xdp" @@ -242,7 +198,7 @@ class Layout: return self._time_period def label(self, key: str) -> str: - return self.LABELS.get(key, key) + return C.LABELS.get(key, key) def _show_tooltip(self, id: str, title: str, clipboard_id: str=None) -> list: @@ -364,21 +320,21 @@ class Layout: id="row-graph-tput", class_name="g-0 p-2", children=[ - self.PLACEHOLDER + C.PLACEHOLDER ] ), dbc.Row( # Latency id="row-graph-lat", class_name="g-0 p-2", children=[ - self.PLACEHOLDER + C.PLACEHOLDER ] ), dbc.Row( # Download id="row-btn-download", class_name="g-0 p-2", children=[ - self.PLACEHOLDER + C.PLACEHOLDER ] ) ] @@ -497,7 +453,7 @@ class Layout: children=[ dbc.Checklist( id="cl-ctrl-framesize-all", - options=self.CL_ALL_DISABLED, + options=C.CL_ALL_DISABLED, inline=True, switch=False ), @@ -528,7 +484,7 @@ class Layout: children=[ dbc.Checklist( id="cl-ctrl-core-all", - options=self.CL_ALL_DISABLED, + options=C.CL_ALL_DISABLED, inline=False, switch=False ) @@ -559,7 +515,7 @@ class Layout: children=[ dbc.Checklist( id="cl-ctrl-testtype-all", - options=self.CL_ALL_DISABLED, + options=C.CL_ALL_DISABLED, inline=True, switch=False ), @@ -648,7 +604,7 @@ class Layout: dbc.Row( id="row-card-sel-tests", class_name="gy-1", - style=self.STYLE_DISABLED, + style=C.STYLE_DISABLED, children=[ dbc.Label( "Selected tests", @@ -665,7 +621,7 @@ class Layout: ), dbc.Row( id="row-btns-sel-tests", - style=self.STYLE_DISABLED, + style=C.STYLE_DISABLED, children=[ dbc.ButtonGroup( class_name="gy-2", @@ -695,12 +651,6 @@ class Layout: class ControlPanel: def __init__(self, panel: dict) -> None: - CL_ALL_DISABLED = [{ - "label": "All", - "value": "all", - "disabled": True - }] - # Defines also the order of keys self._defaults = { "dd-ctrl-dut-value": str(), @@ -716,15 +666,15 @@ class Layout: "cl-ctrl-core-options": list(), "cl-ctrl-core-value": list(), "cl-ctrl-core-all-value": list(), - "cl-ctrl-core-all-options": CL_ALL_DISABLED, + "cl-ctrl-core-all-options": C.CL_ALL_DISABLED, "cl-ctrl-framesize-options": list(), "cl-ctrl-framesize-value": list(), "cl-ctrl-framesize-all-value": list(), - "cl-ctrl-framesize-all-options": CL_ALL_DISABLED, + "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED, "cl-ctrl-testtype-options": list(), "cl-ctrl-testtype-value": list(), "cl-ctrl-testtype-all-value": list(), - "cl-ctrl-testtype-all-options": CL_ALL_DISABLED, + "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED, "btn-ctrl-add-disabled": True, "cl-normalize-value": list(), "cl-selected-options": list(), @@ -788,9 +738,9 @@ class Layout: (fig_tput, fig_lat) = figs - row_fig_tput = self.PLACEHOLDER - row_fig_lat = self.PLACEHOLDER - row_btn_dwnld = self.PLACEHOLDER + row_fig_tput = C.PLACEHOLDER + row_fig_lat = C.PLACEHOLDER + row_btn_dwnld = C.PLACEHOLDER if fig_tput: row_fig_tput = [ @@ -822,7 +772,7 @@ class Layout: class_name="me-1", children=[ dbc.InputGroupText( - style=self.URL_STYLE, + style=C.URL_STYLE, children=self._show_tooltip( "help-url", "URL", "input-url") ), @@ -830,7 +780,7 @@ class Layout: id="input-url", readonly=True, type="url", - style=self.URL_STYLE, + style=C.URL_STYLE, value=url ) ] @@ -971,15 +921,15 @@ class Layout: "cl-ctrl-core-options": list(), "cl-ctrl-core-value": list(), "cl-ctrl-core-all-value": list(), - "cl-ctrl-core-all-options": self.CL_ALL_DISABLED, + "cl-ctrl-core-all-options": C.CL_ALL_DISABLED, "cl-ctrl-framesize-options": list(), "cl-ctrl-framesize-value": list(), "cl-ctrl-framesize-all-value": list(), - "cl-ctrl-framesize-all-options": self.CL_ALL_DISABLED, + "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED, "cl-ctrl-testtype-options": list(), "cl-ctrl-testtype-value": list(), "cl-ctrl-testtype-all-value": list(), - "cl-ctrl-testtype-all-options": self.CL_ALL_DISABLED, + "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED, }) elif trigger_id == "dd-ctrl-phy": try: @@ -1005,15 +955,15 @@ class Layout: "cl-ctrl-core-options": list(), "cl-ctrl-core-value": list(), "cl-ctrl-core-all-value": list(), - "cl-ctrl-core-all-options": self.CL_ALL_DISABLED, + "cl-ctrl-core-all-options": C.CL_ALL_DISABLED, "cl-ctrl-framesize-options": list(), "cl-ctrl-framesize-value": list(), "cl-ctrl-framesize-all-value": list(), - "cl-ctrl-framesize-all-options": self.CL_ALL_DISABLED, + "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED, "cl-ctrl-testtype-options": list(), "cl-ctrl-testtype-value": list(), "cl-ctrl-testtype-all-value": list(), - "cl-ctrl-testtype-all-options": self.CL_ALL_DISABLED, + "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED, }) elif trigger_id == "dd-ctrl-area": try: @@ -1036,15 +986,15 @@ class Layout: "cl-ctrl-core-options": list(), "cl-ctrl-core-value": list(), "cl-ctrl-core-all-value": list(), - "cl-ctrl-core-all-options": self.CL_ALL_DISABLED, + "cl-ctrl-core-all-options": C.CL_ALL_DISABLED, "cl-ctrl-framesize-options": list(), "cl-ctrl-framesize-value": list(), "cl-ctrl-framesize-all-value": list(), - "cl-ctrl-framesize-all-options": self.CL_ALL_DISABLED, + "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED, "cl-ctrl-testtype-options": list(), "cl-ctrl-testtype-value": list(), "cl-ctrl-testtype-all-value": list(), - "cl-ctrl-testtype-all-options": self.CL_ALL_DISABLED, + "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED, }) elif trigger_id == "dd-ctrl-test": core_opts = list() @@ -1069,15 +1019,15 @@ class Layout: "cl-ctrl-core-options": core_opts, "cl-ctrl-core-value": list(), "cl-ctrl-core-all-value": list(), - "cl-ctrl-core-all-options": self.CL_ALL_ENABLED, + "cl-ctrl-core-all-options": C.CL_ALL_ENABLED, "cl-ctrl-framesize-options": framesize_opts, "cl-ctrl-framesize-value": list(), "cl-ctrl-framesize-all-value": list(), - "cl-ctrl-framesize-all-options": self.CL_ALL_ENABLED, + "cl-ctrl-framesize-all-options": C.CL_ALL_ENABLED, "cl-ctrl-testtype-options": testtype_opts, "cl-ctrl-testtype-value": list(), "cl-ctrl-testtype-all-value": list(), - "cl-ctrl-testtype-all-options": self.CL_ALL_ENABLED, + "cl-ctrl-testtype-all-options": C.CL_ALL_ENABLED, }) elif trigger_id == "cl-ctrl-core": val_sel, val_all = self._sync_checklists( @@ -1180,17 +1130,17 @@ class Layout: "testtype": ttype.lower() }) store_sel = sorted(store_sel, key=lambda d: d["id"]) - row_card_sel_tests = self.STYLE_ENABLED - row_btns_sel_tests = self.STYLE_ENABLED - if self.CLEAR_ALL_INPUTS: + row_card_sel_tests = C.STYLE_ENABLED + row_btns_sel_tests = C.STYLE_ENABLED + if C.CLEAR_ALL_INPUTS: ctrl_panel.set(ctrl_panel.defaults) elif trigger_id == "btn-sel-remove-all": _ = btn_remove_all - row_fig_tput = self.PLACEHOLDER - row_fig_lat = self.PLACEHOLDER - row_btn_dwnld = self.PLACEHOLDER - row_card_sel_tests = self.STYLE_DISABLED - row_btns_sel_tests = self.STYLE_DISABLED + row_fig_tput = C.PLACEHOLDER + row_fig_lat = C.PLACEHOLDER + row_btn_dwnld = C.PLACEHOLDER + row_card_sel_tests = C.STYLE_DISABLED + row_btns_sel_tests = C.STYLE_DISABLED store_sel = list() ctrl_panel.set({"cl-selected-options": list()}) elif trigger_id == "btn-sel-remove": @@ -1210,8 +1160,8 @@ class Layout: d_start = self._get_date(url_params.get("start", list())[0]) d_end = self._get_date(url_params.get("end", list())[0]) if store_sel: - row_card_sel_tests = self.STYLE_ENABLED - row_btns_sel_tests = self.STYLE_ENABLED + row_card_sel_tests = C.STYLE_ENABLED + row_btns_sel_tests = C.STYLE_ENABLED if trigger_id in ("btn-ctrl-add", "url", "dpr-period", "btn-sel-remove", "cl-ctrl-normalize"): @@ -1226,11 +1176,11 @@ class Layout: "cl-selected-options": self._list_tests(store_sel) }) else: - row_fig_tput = self.PLACEHOLDER - row_fig_lat = self.PLACEHOLDER - row_btn_dwnld = self.PLACEHOLDER - row_card_sel_tests = self.STYLE_DISABLED - row_btns_sel_tests = self.STYLE_DISABLED + row_fig_tput = C.PLACEHOLDER + row_fig_lat = C.PLACEHOLDER + row_btn_dwnld = C.PLACEHOLDER + row_card_sel_tests = C.STYLE_DISABLED + row_btns_sel_tests = C.STYLE_DISABLED store_sel = list() ctrl_panel.set({"cl-selected-options": list()}) diff --git a/resources/tools/dash/app/pal/trending/trending.py b/resources/tools/dash/app/pal/trending/trending.py index 1c64677eea..3697f7150f 100644 --- a/resources/tools/dash/app/pal/trending/trending.py +++ b/resources/tools/dash/app/pal/trending/trending.py @@ -14,8 +14,8 @@ """Instantiate the Trending Dash applocation. """ import dash -import dash_bootstrap_components as dbc +from ..utils.constants import Constants as C from .layout import Layout @@ -30,16 +30,16 @@ def init_trending(server, time_period=None): dash_app = dash.Dash( server=server, - routes_pathname_prefix=u"/trending/", - external_stylesheets=[dbc.themes.LUX], + routes_pathname_prefix=C.TREND_ROUTES_PATHNAME_PREFIX, + external_stylesheets=C.EXTERNAL_STYLESHEETS ) layout = Layout( app=dash_app, - html_layout_file="pal/templates/trending_layout.jinja2", - graph_layout_file="pal/trending/layout.yaml", - data_spec_file="pal/data/data.yaml", - tooltip_file="pal/data/tooltips.yaml", + html_layout_file=C.TREND_HTML_LAYOUT_FILE, + graph_layout_file=C.TREND_GRAPH_LAYOUT_FILE, + data_spec_file=C.DATA_SPEC_FILE, + tooltip_file=C.TOOLTIP_FILE, time_period=time_period ) dash_app.index_string = layout.html_layout diff --git a/resources/tools/dash/app/pal/utils/__init__.py b/resources/tools/dash/app/pal/utils/__init__.py new file mode 100644 index 0000000000..5692432123 --- /dev/null +++ b/resources/tools/dash/app/pal/utils/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) 2022 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/resources/tools/dash/app/pal/utils/constants.py b/resources/tools/dash/app/pal/utils/constants.py new file mode 100644 index 0000000000..1c84ba14a6 --- /dev/null +++ b/resources/tools/dash/app/pal/utils/constants.py @@ -0,0 +1,299 @@ +# Copyright (c) 2022 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Constants used in Dash PAL. + +"Constant" means a value that keeps its value since initialization. The value +does not need to be hard coded here, but can be read from environment variables. +""" + + +import dash_bootstrap_components as dbc + +from dash import html + + +class Constants: + """Constants used in Dash PAL. + """ + + ############################################################################ + # General, application wide constants. + + # The application title. + TITLE = "FD.io CSIT" + + # The application description. + DESCRIPTION = "Performance Dashboard" + + # External stylesheets. + EXTERNAL_STYLESHEETS = [dbc.themes.LUX, ] + + # Top level template for all pages. + TEMPLATE = "d-flex h-100 text-center text-white bg-dark" + + # Path and name of the file specifying the HTML layout of the dash + # application. + MAIN_HTML_LAYOUT_FILE = "index_layout.jinja2" + + # Application root. + APPLICATIN_ROOT = "/" + + # Data to be downloaded from the parquets specification file. + DATA_SPEC_FILE = "pal/data/data.yaml" + + # The file with tooltips. + TOOLTIP_FILE = "pal/utils/tooltips.yaml" + + # Maximal value of TIME_PERIOD for data read from the parquets in days. + # Do not change without a good reason. + MAX_TIME_PERIOD = 180 + + # It defines the time period for data read from the parquets in days from + # now back to the past. + # TIME_PERIOD = None - means all data (max MAX_TIME_PERIOD days) is read. + # TIME_PERIOD = MAX_TIME_PERIOD - is the default value + TIME_PERIOD = MAX_TIME_PERIOD # [days] + + # List of releases used for iterative data processing. + # The releases MUST be in the order from the current (newest) to the last + # (oldest). + RELEASES = ["csit2206", "csit2202", ] + + ############################################################################ + # General, application wide, layout affecting constants. + + # If True, clear all inputs in control panel when button "ADD SELECTED" is + # pressed. + CLEAR_ALL_INPUTS = False + + # The element is disabled. + STYLE_DISABLED = {"display": "none"} + + # The element is enabled and visible. + STYLE_ENABLED = {"display": "inherit"} + + # Checklist "All" is disabled. + CL_ALL_DISABLED = [ + { + "label": "All", + "value": "all", + "disabled": True + } + ] + + # Checklist "All" is enable, visible and unchecked. + CL_ALL_ENABLED = [ + { + "label": "All", + "value": "all", + "disabled": False + } + ] + + # Placeholder for any element in the layout. + PLACEHOLDER = html.Nobr("") + + # List of drivers used in CSIT. + DRIVERS = ("avf", "af-xdp", "rdma", "dpdk") + + # Labels for input elements (dropdowns, ...). + LABELS = { + "dpdk": "DPDK", + "container_memif": "LXC/DRC Container Memif", + "crypto": "IPSec IPv4 Routing", + "ip4": "IPv4 Routing", + "ip6": "IPv6 Routing", + "ip4_tunnels": "IPv4 Tunnels", + "l2": "L2 Ethernet Switching", + "srv6": "SRv6 Routing", + "vm_vhost": "VMs vhost-user", + "nfv_density-dcr_memif-chain_ipsec": "CNF Service Chains Routing IPSec", + "nfv_density-vm_vhost-chain_dot1qip4vxlan":"VNF Service Chains Tunnels", + "nfv_density-vm_vhost-chain": "VNF Service Chains Routing", + "nfv_density-dcr_memif-pipeline": "CNF Service Pipelines Routing", + "nfv_density-dcr_memif-chain": "CNF Service Chains Routing", + } + + # URL style. + URL_STYLE = { + "background-color": "#d2ebf5", + "border-color": "#bce1f1", + "color": "#135d7c" + } + + ############################################################################ + # General, normalization constants. + + NORM_FREQUENCY = 2.0 # [GHz] + FREQUENCY = { # [GHz] + "2n-aws": 1.000, + "2n-dnv": 2.000, + "2n-clx": 2.300, + "2n-icx": 2.600, + "2n-skx": 2.500, + "2n-tx2": 2.500, + "2n-zn2": 2.900, + "3n-alt": 3.000, + "3n-aws": 1.000, + "3n-dnv": 2.000, + "3n-icx": 2.600, + "3n-skx": 2.500, + "3n-tsh": 2.200 + } + + ############################################################################ + # General, plots constants. + + PLOT_COLORS = ( + "#1A1110", "#DA2647", "#214FC6", "#01786F", "#BD8260", "#FFD12A", + "#A6E7FF", "#738276", "#C95A49", "#FC5A8D", "#CEC8EF", "#391285", + "#6F2DA8", "#FF878D", "#45A27D", "#FFD0B9", "#FD5240", "#DB91EF", + "#44D7A8", "#4F86F7", "#84DE02", "#FFCFF1", "#614051" + ) + + # Trending, anomalies. + ANOMALY_COLOR = { + "regression": 0.0, + "normal": 0.5, + "progression": 1.0 + } + + COLORSCALE_TPUT = [ + [0.00, "red"], + [0.33, "red"], + [0.33, "white"], + [0.66, "white"], + [0.66, "green"], + [1.00, "green"] + ] + + TICK_TEXT_TPUT = ["Regression", "Normal", "Progression"] + + COLORSCALE_LAT = [ + [0.00, "green"], + [0.33, "green"], + [0.33, "white"], + [0.66, "white"], + [0.66, "red"], + [1.00, "red"] + ] + + TICK_TEXT_LAT = ["Progression", "Normal", "Regression"] + + # Access to the results. + VALUE = { + "mrr": "result_receive_rate_rate_avg", + "ndr": "result_ndr_lower_rate_value", + "pdr": "result_pdr_lower_rate_value", + "pdr-lat": "result_latency_forward_pdr_50_avg" + } + + VALUE_ITER = { + "mrr": "result_receive_rate_rate_values", + "ndr": "result_ndr_lower_rate_value", + "pdr": "result_pdr_lower_rate_value", + "pdr-lat": "result_latency_forward_pdr_50_avg" + } + + UNIT = { + "mrr": "result_receive_rate_rate_unit", + "ndr": "result_ndr_lower_rate_unit", + "pdr": "result_pdr_lower_rate_unit", + "pdr-lat": "result_latency_forward_pdr_50_unit" + } + + # Latencies. + LAT_HDRH = ( # Do not change the order + "result_latency_forward_pdr_0_hdrh", + "result_latency_reverse_pdr_0_hdrh", + "result_latency_forward_pdr_10_hdrh", + "result_latency_reverse_pdr_10_hdrh", + "result_latency_forward_pdr_50_hdrh", + "result_latency_reverse_pdr_50_hdrh", + "result_latency_forward_pdr_90_hdrh", + "result_latency_reverse_pdr_90_hdrh", + ) + + # This value depends on latency stream rate (9001 pps) and duration (5s). + # Keep it slightly higher to ensure rounding errors to not remove tick mark. + PERCENTILE_MAX = 99.999501 + + GRAPH_LAT_HDRH_DESC = { + "result_latency_forward_pdr_0_hdrh": "No-load.", + "result_latency_reverse_pdr_0_hdrh": "No-load.", + "result_latency_forward_pdr_10_hdrh": "Low-load, 10% PDR.", + "result_latency_reverse_pdr_10_hdrh": "Low-load, 10% PDR.", + "result_latency_forward_pdr_50_hdrh": "Mid-load, 50% PDR.", + "result_latency_reverse_pdr_50_hdrh": "Mid-load, 50% PDR.", + "result_latency_forward_pdr_90_hdrh": "High-load, 90% PDR.", + "result_latency_reverse_pdr_90_hdrh": "High-load, 90% PDR." + } + + ############################################################################ + # News. + + # The pathname prefix for the application. + NEWS_ROUTES_PATHNAME_PREFIX = "/news/" + + # Path and name of the file specifying the HTML layout of the dash + # application. + NEWS_HTML_LAYOUT_FILE = "pal/templates/news_layout.jinja2" + + # The default job displayed when the page is loaded first time. + NEWS_DEFAULT_JOB = "csit-vpp-perf-mrr-daily-master-2n-icx" + + # Time period for regressions and progressions. + NEWS_TIME_PERIOD = 21 # [days] + + ############################################################################ + # Report. + + # The pathname prefix for the application. + REPORT_ROUTES_PATHNAME_PREFIX = "/report/" + + # Path and name of the file specifying the HTML layout of the dash + # application. + REPORT_HTML_LAYOUT_FILE = "pal/templates/report_layout.jinja2" + + # Layout of plot.ly graphs. + REPORT_GRAPH_LAYOUT_FILE = "pal/report/layout.yaml" + + ############################################################################ + # Statistics. + + # The pathname prefix for the application. + STATS_ROUTES_PATHNAME_PREFIX = "/stats/" + + # Path and name of the file specifying the HTML layout of the dash + # application. + STATS_HTML_LAYOUT_FILE = "pal/templates/stats_layout.jinja2" + + # Layout of plot.ly graphs. + STATS_GRAPH_LAYOUT_FILE = "pal/stats/layout.yaml" + + # The default job displayed when the page is loaded first time. + STATS_DEFAULT_JOB = "csit-vpp-perf-mrr-daily-master-2n-icx" + + ############################################################################ + # Trending. + + # The pathname prefix for the application. + TREND_ROUTES_PATHNAME_PREFIX = "/trending/" + + # Path and name of the file specifying the HTML layout of the dash + # application. + TREND_HTML_LAYOUT_FILE = "pal/templates/trending_layout.jinja2" + + # Layout of plot.ly graphs. + TREND_GRAPH_LAYOUT_FILE = "pal/trending/layout.yaml" diff --git a/resources/tools/dash/app/pal/data/tooltips.yaml b/resources/tools/dash/app/pal/utils/tooltips.yaml index 2086b575a9..2086b575a9 100644 --- a/resources/tools/dash/app/pal/data/tooltips.yaml +++ b/resources/tools/dash/app/pal/utils/tooltips.yaml diff --git a/resources/tools/dash/app/pal/data/url_processing.py b/resources/tools/dash/app/pal/utils/url_processing.py index 9307015d0d..9307015d0d 100644 --- a/resources/tools/dash/app/pal/data/url_processing.py +++ b/resources/tools/dash/app/pal/utils/url_processing.py diff --git a/resources/tools/dash/app/pal/data/utils.py b/resources/tools/dash/app/pal/utils/utils.py index 63c9c1aaa4..63c9c1aaa4 100644 --- a/resources/tools/dash/app/pal/data/utils.py +++ b/resources/tools/dash/app/pal/utils/utils.py |