aboutsummaryrefslogtreecommitdiffstats
path: root/resources/tools/dash/app
diff options
context:
space:
mode:
authorTibor Frank <tifrank@cisco.com>2022-04-25 16:55:01 +0200
committerTibor Frank <tifrank@cisco.com>2022-04-26 08:49:11 +0000
commitc01befc28450d5c2003d25876dda0201eb827735 (patch)
tree4bd0eacbe76c605b8950e7b001ef296bbeca277a /resources/tools/dash/app
parent76def9e0a3404b38c9e7fbd58260700207b17639 (diff)
feat(uti): Add statistics
Change-Id: I14046fd1050f130d201bbe81a72e48ad4fd10057 Signed-off-by: Tibor Frank <tifrank@cisco.com>
Diffstat (limited to 'resources/tools/dash/app')
-rw-r--r--resources/tools/dash/app/pal/__init__.py7
-rw-r--r--resources/tools/dash/app/pal/data/data.py30
-rw-r--r--resources/tools/dash/app/pal/data/data.yaml14
-rw-r--r--resources/tools/dash/app/pal/stats/graphs.py109
-rw-r--r--resources/tools/dash/app/pal/stats/layout.py331
-rw-r--r--resources/tools/dash/app/pal/stats/layout.yaml117
-rw-r--r--resources/tools/dash/app/pal/stats/stats.py48
-rw-r--r--resources/tools/dash/app/pal/templates/index_layout.jinja23
-rw-r--r--resources/tools/dash/app/pal/templates/stats_layout.jinja217
-rw-r--r--resources/tools/dash/app/pal/trending/graphs.py2
-rw-r--r--resources/tools/dash/app/pal/trending/spec_test_selection.yaml6
11 files changed, 669 insertions, 15 deletions
diff --git a/resources/tools/dash/app/pal/__init__.py b/resources/tools/dash/app/pal/__init__.py
index ff56ab522e..ba68c017a1 100644
--- a/resources/tools/dash/app/pal/__init__.py
+++ b/resources/tools/dash/app/pal/__init__.py
@@ -43,13 +43,12 @@ def init_app():
assets.init_app(app)
# Import Dash applications.
+ from .stats.stats import init_stats
+ app = init_stats(app)
+
from .trending.trending import init_trending
app = init_trending(app)
- # Temporarily switched off
- # from .report.report import init_report
- # app = init_report(app)
-
return app
app = init_app()
diff --git a/resources/tools/dash/app/pal/data/data.py b/resources/tools/dash/app/pal/data/data.py
index a3b6c2a478..3d9b8b1664 100644
--- a/resources/tools/dash/app/pal/data/data.py
+++ b/resources/tools/dash/app/pal/data/data.py
@@ -151,13 +151,29 @@ class Data:
def read_stats(self, days=None):
"""Read Suite Result Analysis data partition from parquet.
"""
- lambda_f = lambda part: True if part["stats_type"] == "sra" else False
-
- return self._create_dataframe_from_parquet(
- path=self._get_path("statistics"),
- partition_filter=lambda_f,
- columns=None, # Get all columns.
- days=days
+ l_stats = lambda part: True if part["stats_type"] == "sra" else False
+ l_mrr = lambda part: True if part["test_type"] == "mrr" else False
+ l_ndrpdr = lambda part: True if part["test_type"] == "ndrpdr" else False
+
+ return (
+ self._create_dataframe_from_parquet(
+ path=self._get_path("statistics"),
+ partition_filter=l_stats,
+ columns=self._get_columns("statistics"),
+ days=days
+ ),
+ self._create_dataframe_from_parquet(
+ path=self._get_path("statistics-trending"),
+ partition_filter=l_mrr,
+ columns=self._get_columns("statistics-trending"),
+ days=days
+ ),
+ self._create_dataframe_from_parquet(
+ path=self._get_path("statistics-trending"),
+ partition_filter=l_ndrpdr,
+ columns=self._get_columns("statistics-trending"),
+ days=days
+ )
)
def read_trending_mrr(self, days=None):
diff --git a/resources/tools/dash/app/pal/data/data.yaml b/resources/tools/dash/app/pal/data/data.yaml
index f639873fa8..7490b43b2a 100644
--- a/resources/tools/dash/app/pal/data/data.yaml
+++ b/resources/tools/dash/app/pal/data/data.yaml
@@ -1,5 +1,19 @@
statistics:
path: s3://fdio-docs-s3-cloudfront-index/csit/parquet/stats
+ columns:
+ - job
+ - build
+ - start_time
+ - duration
+statistics-trending:
+ path: s3://fdio-docs-s3-cloudfront-index/csit/parquet/trending
+ columns:
+ - job
+ - build
+ - dut_type
+ - dut_version
+ - hosts
+ - passed
trending-mrr:
path: s3://fdio-docs-s3-cloudfront-index/csit/parquet/trending
columns:
diff --git a/resources/tools/dash/app/pal/stats/graphs.py b/resources/tools/dash/app/pal/stats/graphs.py
new file mode 100644
index 0000000000..2fabf8e6ae
--- /dev/null
+++ b/resources/tools/dash/app/pal/stats/graphs.py
@@ -0,0 +1,109 @@
+# 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.
+
+"""
+"""
+
+import plotly.graph_objects as go
+import pandas as pd
+
+from datetime import datetime, timedelta
+
+def select_data(data: pd.DataFrame, itm:str) -> pd.DataFrame:
+ """
+ """
+
+ df = data.loc[(data["job"] == itm)].sort_values(
+ by="start_time", ignore_index=True)
+
+ return df
+
+
+def graph_statistics(df: pd.DataFrame, job:str, layout: dict,
+ start: datetime=datetime.utcnow()-timedelta(days=180),
+ end: datetime=datetime.utcnow()) -> tuple:
+ """
+ """
+
+ data = select_data(df, job)
+ data = data.dropna(subset=["duration", ])
+ if data.empty:
+ return None, None
+
+ x_axis = [d for d in data["start_time"] if d >= start and d <= end]
+ if not x_axis:
+ return None, None
+
+ hover = list()
+ for _, row in data.iterrows():
+ hover_itm = (
+ f"date: {row['start_time'].strftime('%d-%m-%Y %H:%M:%S')}<br>"
+ f"duration: "
+ f"{(int(row['duration']) // 3600):02d}:"
+ f"{((int(row['duration']) % 3600) // 60):02d}<br>"
+ f"passed: {row['passed']}<br>"
+ f"failed: {row['failed']}<br>"
+ f"{row['dut_type']}-ref: {row['dut_version']}<br>"
+ f"csit-ref: {row['job']}/{row['build']}<br>"
+ f"hosts: {', '.join(row['hosts'])}"
+ )
+ hover.append(hover_itm)
+
+ # Job durations:
+ fig_duration = go.Figure(
+ data=go.Scatter(
+ x=x_axis,
+ y=data["duration"],
+ name=u"Duration",
+ text=hover,
+ hoverinfo=u"text"
+ )
+ )
+
+ tickvals = [0, ]
+ step = max(data["duration"]) / 5
+ for i in range(5):
+ tickvals.append(int(step * (i + 1)))
+ layout_duration = layout.get("plot-stats-duration", dict())
+ if layout_duration:
+ layout_duration["yaxis"]["tickvals"] = tickvals
+ layout_duration["yaxis"]["ticktext"] = [
+ f"{(val // 3600):02d}:{((val % 3600) // 60):02d}" \
+ for val in tickvals
+ ]
+ fig_duration.update_layout(layout_duration)
+
+ # Passed / failed:
+ fig_passed = go.Figure(
+ data=[
+ go.Bar(
+ x=x_axis,
+ y=data["passed"],
+ name=u"Passed",
+ hovertext=hover,
+ hoverinfo=u"text"
+ ),
+ go.Bar(
+ x=x_axis,
+ y=data["failed"],
+ name=u"Failed",
+ hovertext=hover,
+ hoverinfo=u"text"
+ )
+ ]
+ )
+ layout_pf = layout.get("plot-stats-passed", dict())
+ if layout_pf:
+ fig_passed.update_layout(layout_pf)
+
+ return fig_passed, fig_duration
diff --git a/resources/tools/dash/app/pal/stats/layout.py b/resources/tools/dash/app/pal/stats/layout.py
new file mode 100644
index 0000000000..18f7b69612
--- /dev/null
+++ b/resources/tools/dash/app/pal/stats/layout.py
@@ -0,0 +1,331 @@
+# 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.
+
+"""Plotly Dash HTML layout override.
+"""
+
+import pandas as pd
+import dash_bootstrap_components as dbc
+
+from dash import dcc
+from dash import html
+from dash import Input, Output
+from dash.exceptions import PreventUpdate
+from yaml import load, FullLoader, YAMLError
+from datetime import datetime, timedelta
+
+from ..data.data import Data
+from .graphs import graph_statistics
+
+
+class Layout:
+ """
+ """
+
+ def __init__(self, app, html_layout_file, spec_file, graph_layout_file,
+ data_spec_file):
+ """
+ """
+
+ # Inputs
+ self._app = app
+ self._html_layout_file = html_layout_file
+ self._spec_file = spec_file
+ self._graph_layout_file = graph_layout_file
+ self._data_spec_file = data_spec_file
+
+ # Read the data:
+ data_stats, data_mrr, data_ndrpdr = Data(
+ data_spec_file=self._data_spec_file,
+ debug=True
+ ).read_stats(days=180)
+
+ df_tst_info = pd.concat([data_mrr, data_ndrpdr], ignore_index=True)
+
+ # Pre-process the data:
+ data_stats = data_stats[~data_stats.job.str.contains("-verify-")]
+ data_stats = data_stats[~data_stats.job.str.contains("-coverage-")]
+ data_stats = data_stats[~data_stats.job.str.contains("-iterative-")]
+ data_stats = data_stats[["job", "build", "start_time", "duration"]]
+
+ self._jobs = sorted(list(data_stats["job"].unique()))
+
+ tst_info = {
+ "job": list(),
+ "build": list(),
+ "dut_type": list(),
+ "dut_version": list(),
+ "hosts": list(),
+ "passed": list(),
+ "failed": list()
+ }
+ for job in self._jobs:
+ df_job = df_tst_info.loc[(df_tst_info["job"] == job)]
+ builds = df_job["build"].unique()
+ for build in builds:
+ df_build = df_job.loc[(df_job["build"] == build)]
+ tst_info["job"].append(job)
+ tst_info["build"].append(build)
+ tst_info["dut_type"].append(df_build["dut_type"].iloc[-1])
+ tst_info["dut_version"].append(df_build["dut_version"].iloc[-1])
+ tst_info["hosts"].append(df_build["hosts"].iloc[-1])
+ try:
+ passed = df_build.value_counts(subset='passed')[True]
+ except KeyError:
+ passed = 0
+ try:
+ failed = df_build.value_counts(subset='passed')[False]
+ except KeyError:
+ failed = 0
+ tst_info["passed"].append(passed)
+ tst_info["failed"].append(failed)
+
+ self._data = data_stats.merge(pd.DataFrame.from_dict(tst_info))
+
+ # Read from files:
+ self._html_layout = ""
+ self._graph_layout = None
+
+ try:
+ with open(self._html_layout_file, "r") as file_read:
+ self._html_layout = file_read.read()
+ except IOError as err:
+ raise RuntimeError(
+ f"Not possible to open the file {self._html_layout_file}\n{err}"
+ )
+
+ try:
+ with open(self._graph_layout_file, "r") as file_read:
+ self._graph_layout = load(file_read, Loader=FullLoader)
+ except IOError as err:
+ raise RuntimeError(
+ f"Not possible to open the file {self._graph_layout_file}\n"
+ f"{err}"
+ )
+ except YAMLError as err:
+ raise RuntimeError(
+ f"An error occurred while parsing the specification file "
+ f"{self._graph_layout_file}\n"
+ f"{err}"
+ )
+
+ self._default_fig_passed, self._default_fig_duration = graph_statistics(
+ self.data, self.jobs[0], self.layout
+ )
+
+ # Callbacks:
+ if self._app is not None and hasattr(self, 'callbacks'):
+ self.callbacks(self._app)
+
+ @property
+ def html_layout(self) -> dict:
+ return self._html_layout
+
+ @property
+ def data(self) -> pd.DataFrame:
+ return self._data
+
+ @property
+ def layout(self) -> dict:
+ return self._graph_layout
+
+ @property
+ def jobs(self) -> list:
+ return self._jobs
+
+ def add_content(self):
+ """
+ """
+ if self.html_layout:
+ return html.Div(
+ id="div-main",
+ children=[
+ dbc.Row(
+ id="row-navbar",
+ class_name="g-0",
+ children=[
+ self._add_navbar(),
+ ]
+ ),
+ dbc.Row(
+ id="row-main",
+ class_name="g-0",
+ children=[
+ dcc.Store(
+ id="selected-tests"
+ ),
+ dcc.Store(
+ id="control-panel"
+ ),
+ self._add_ctrl_col(),
+ self._add_plotting_col(),
+ ]
+ )
+ ]
+ )
+ else:
+ return html.Div(
+ id="div-main-error",
+ children=[
+ dbc.Alert(
+ [
+ "An Error Occured",
+ ],
+ color="danger",
+ ),
+ ]
+ )
+
+ def _add_navbar(self):
+ """Add nav element with navigation panel. It is placed on the top.
+ """
+ return dbc.NavbarSimple(
+ id="navbarsimple-main",
+ children=[
+ dbc.NavItem(
+ dbc.NavLink(
+ "Continuous Performance Statistics",
+ disabled=True,
+ external_link=True,
+ href="#"
+ )
+ )
+ ],
+ brand="Dashboard",
+ brand_href="/",
+ brand_external_link=True,
+ class_name="p-2",
+ fluid=True,
+ )
+
+ def _add_ctrl_col(self) -> dbc.Col:
+ """Add column with controls. It is placed on the left side.
+ """
+ return dbc.Col(
+ id="col-controls",
+ children=[
+ self._add_ctrl_panel(),
+ ],
+ )
+
+ def _add_plotting_col(self) -> dbc.Col:
+ """Add column with plots and tables. It is placed on the right side.
+ """
+ return dbc.Col(
+ id="col-plotting-area",
+ children=[
+ dbc.Row( # Passed / failed tests
+ id="row-graph-passed",
+ class_name="g-0 p-2",
+ children=[
+ dcc.Loading(children=[
+ dcc.Graph(
+ id="graph-passed",
+ figure=self._default_fig_passed
+ )
+ ])
+ ]
+ ),
+ dbc.Row( # Duration
+ id="row-graph-duration",
+ class_name="g-0 p-2",
+ children=[
+ dcc.Loading(children=[
+ dcc.Graph(
+ id="graph-duration",
+ figure=self._default_fig_duration
+ )
+ ])
+ ]
+ ),
+ dbc.Row( # Download
+ id="row-btn-download",
+ class_name="g-0 p-2",
+ children=[
+ dcc.Loading(children=[
+ dbc.Button(
+ id="btn-download-data",
+ children=["Download Data"]
+ ),
+ dcc.Download(id="download-data")
+ ])
+ ]
+ )
+ ],
+ width=9,
+ )
+
+ def _add_ctrl_panel(self) -> dbc.Row:
+ """
+ """
+ return dbc.Row(
+ id="row-ctrl-panel",
+ class_name="g-0 p-2",
+ children=[
+ dbc.Label("Choose the Trending Job"),
+ dbc.RadioItems(
+ id="ri_job",
+ value=self.jobs[0],
+ options=[{"label": i, "value": i} for i in self.jobs]
+ ),
+ dbc.Label("Choose the Time Period"),
+ dcc.DatePickerRange(
+ id="dpr-period",
+ className="d-flex justify-content-center",
+ min_date_allowed=\
+ datetime.utcnow()-timedelta(days=180),
+ max_date_allowed=datetime.utcnow(),
+ initial_visible_month=datetime.utcnow(),
+ start_date=datetime.utcnow() - timedelta(days=180),
+ end_date=datetime.utcnow(),
+ display_format="D MMMM YY"
+ )
+ ]
+ )
+
+ def callbacks(self, app):
+
+ @app.callback(
+ Output("graph-passed", "figure"),
+ Output("graph-duration", "figure"),
+ Input("ri_job", "value"),
+ Input("dpr-period", "start_date"),
+ Input("dpr-period", "end_date"),
+ prevent_initial_call=True
+ )
+ def _update_ctrl_panel(job:str, d_start: str, d_end: str) -> tuple:
+ """
+ """
+
+ d_start = datetime(int(d_start[0:4]), int(d_start[5:7]),
+ int(d_start[8:10]))
+ d_end = datetime(int(d_end[0:4]), int(d_end[5:7]), int(d_end[8:10]))
+
+ fig_passed, fig_duration = graph_statistics(
+ self.data, job, self.layout, d_start, d_end
+ )
+
+ return fig_passed, fig_duration
+
+ @app.callback(
+ Output("download-data", "data"),
+ Input("btn-download-data", "n_clicks"),
+ prevent_initial_call=True
+ )
+ def _download_data(n_clicks):
+ """
+ """
+ if not n_clicks:
+ raise PreventUpdate
+
+ return dcc.send_data_frame(self.data.to_csv, "statistics.csv")
diff --git a/resources/tools/dash/app/pal/stats/layout.yaml b/resources/tools/dash/app/pal/stats/layout.yaml
new file mode 100644
index 0000000000..0a102e4d0a
--- /dev/null
+++ b/resources/tools/dash/app/pal/stats/layout.yaml
@@ -0,0 +1,117 @@
+plot-stats-passed:
+ autosize: True
+ showlegend: False
+ yaxis:
+ showticklabels: True
+ title: "Number of Passed / Failed Tests"
+ gridcolor: "rgb(238, 238, 238)"
+ linecolor: "rgb(238, 238, 238)"
+ showline: True
+ zeroline: False
+ tickcolor: "rgb(238, 238, 238)"
+ linewidth: 1
+ showgrid: True
+ rangemode: "tozero"
+ xaxis:
+ title: 'Date [MMDD]'
+ type: "date"
+ autorange: True
+ fixedrange: False
+ showgrid: True
+ gridcolor: "rgb(238, 238, 238)"
+ showline: True
+ linecolor: "rgb(238, 238, 238)"
+ zeroline: False
+ linewidth: 1
+ showticklabels: True
+ tickcolor: "rgb(238, 238, 238)"
+ tickmode: "auto"
+ tickformat: "%m%d"
+ rangeselector:
+ buttons:
+ - count: 14
+ label: "2w"
+ step: "day"
+ stepmode: "backward"
+ - count: 1
+ label: "1m"
+ step: "month"
+ stepmode: "backward"
+ - count: 2
+ label: "2m"
+ step: "month"
+ stepmode: "backward"
+ - count: 3
+ label: "3m"
+ step: "month"
+ stepmode: "backward"
+ - step: "all"
+ margin:
+ r: 20
+ b: 5
+ t: 5
+ l: 70
+ paper_bgcolor: "#fff"
+ plot_bgcolor: "#fff"
+ barmode: "stack"
+ hoverlabel:
+ namelength: -1
+
+plot-stats-duration:
+ autosize: True
+ showlegend: False
+ yaxis:
+ showticklabels: True
+ title: "Duration [hh:mm]"
+ gridcolor: "rgb(238, 238, 238)"
+ linecolor: "rgb(238, 238, 238)"
+ showline: True
+ zeroline: False
+ tickmode: "array"
+ tickcolor: "rgb(238, 238, 238)"
+ linewidth: 1
+ showgrid: True
+ rangemode: "tozero"
+ xaxis:
+ title: 'Date [MMDD]'
+ type: "date"
+ autorange: True
+ fixedrange: False
+ showgrid: True
+ gridcolor: "rgb(238, 238, 238)"
+ showline: True
+ linecolor: "rgb(238, 238, 238)"
+ zeroline: False
+ linewidth: 1
+ showticklabels: True
+ tickcolor: "rgb(238, 238, 238)"
+ tickmode: "auto"
+ tickformat: "%m%d"
+ rangeselector:
+ buttons:
+ - count: 14
+ label: "2w"
+ step: "day"
+ stepmode: "backward"
+ - count: 1
+ label: "1m"
+ step: "month"
+ stepmode: "backward"
+ - count: 2
+ label: "2m"
+ step: "month"
+ stepmode: "backward"
+ - count: 3
+ label: "3m"
+ step: "month"
+ stepmode: "backward"
+ - step: "all"
+ margin:
+ r: 20
+ b: 5
+ t: 5
+ l: 70
+ paper_bgcolor: "#fff"
+ plot_bgcolor: "#fff"
+ hoverlabel:
+ namelength: -1
diff --git a/resources/tools/dash/app/pal/stats/stats.py b/resources/tools/dash/app/pal/stats/stats.py
new file mode 100644
index 0000000000..78bb6e6f88
--- /dev/null
+++ b/resources/tools/dash/app/pal/stats/stats.py
@@ -0,0 +1,48 @@
+# 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.
+
+"""Instantiate the Statistics Dash applocation.
+"""
+import dash
+import dash_bootstrap_components as dbc
+
+from .layout import Layout
+
+
+def init_stats(server):
+ """Create a Plotly Dash dashboard.
+
+ :param server: Flask server.
+ :type server: Flask
+ :returns: Dash app server.
+ :rtype: Dash
+ """
+
+ dash_app = dash.Dash(
+ server=server,
+ routes_pathname_prefix=u"/stats/",
+ external_stylesheets=[dbc.themes.LUX],
+ )
+
+ # Custom HTML layout
+ layout = Layout(
+ app=dash_app,
+ html_layout_file="pal/templates/stats_layout.jinja2",
+ spec_file="pal/stats/spec_job_selection.yaml",
+ graph_layout_file="pal/stats/layout.yaml",
+ data_spec_file="pal/data/data.yaml"
+ )
+ dash_app.index_string = layout.html_layout
+ dash_app.layout = layout.add_content()
+
+ return dash_app.server
diff --git a/resources/tools/dash/app/pal/templates/index_layout.jinja2 b/resources/tools/dash/app/pal/templates/index_layout.jinja2
index b9fba66206..5fd3d40b25 100644
--- a/resources/tools/dash/app/pal/templates/index_layout.jinja2
+++ b/resources/tools/dash/app/pal/templates/index_layout.jinja2
@@ -16,6 +16,9 @@
<p class="lead">
<a href="/trending/" class="btn btn-primary fw-bold">Trending</a>
</p>
+ <p class="lead">
+ <a href="/stats/" class="btn btn-primary fw-bold">Statistics</a>
+ </p>
</main>
<footer class="mt-auto text-white-50">
diff --git a/resources/tools/dash/app/pal/templates/stats_layout.jinja2 b/resources/tools/dash/app/pal/templates/stats_layout.jinja2
new file mode 100644
index 0000000000..dae6f00c19
--- /dev/null
+++ b/resources/tools/dash/app/pal/templates/stats_layout.jinja2
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Continuous Performance Statistics</title>
+ {%metas%}
+ {%favicon%}
+ {%css%}
+</head>
+<body>
+ {%app_entry%}
+ <footer>
+ {%config%}
+ {%scripts%}
+ {%renderer%}
+ </footer>
+</body>
+</html> \ No newline at end of file
diff --git a/resources/tools/dash/app/pal/trending/graphs.py b/resources/tools/dash/app/pal/trending/graphs.py
index 7089e2c080..16cb5a2cb3 100644
--- a/resources/tools/dash/app/pal/trending/graphs.py
+++ b/resources/tools/dash/app/pal/trending/graphs.py
@@ -165,7 +165,7 @@ def select_trending_data(data: pd.DataFrame, itm:dict) -> pd.DataFrame:
phy = itm["phy"].split("-")
if len(phy) == 4:
topo, arch, nic, drv = phy
- if drv in ("dpdk", "ixgbe"):
+ if drv == "dpdk":
drv = ""
else:
drv += "-"
diff --git a/resources/tools/dash/app/pal/trending/spec_test_selection.yaml b/resources/tools/dash/app/pal/trending/spec_test_selection.yaml
index 4d177c0737..ad4589fee5 100644
--- a/resources/tools/dash/app/pal/trending/spec_test_selection.yaml
+++ b/resources/tools/dash/app/pal/trending/spec_test_selection.yaml
@@ -395,7 +395,7 @@
core: [1C, 2C, 4C]
frame-size: [64B, ]
test-type: [MRR, NDR, PDR]
-2n-dnv-10ge2p1x553-ixgbe:
+2n-dnv-10ge2p1x553-dpdk:
l2-base:
label: L2 Ethernet Switching Base
test:
@@ -1474,7 +1474,7 @@
core: [1C, 2C]
frame-size: [IMIX, 1518B]
test-type: [MRR, ]
-3n-dnv-10ge2p1x553-ixgbe:
+3n-dnv-10ge2p1x553-dpdk:
l2-base:
label: L2 Ethernet Switching Base
test:
@@ -1811,7 +1811,7 @@
core: [2C, 3C, 4C]
frame-size: [1518B, IMIX]
test-type: [MRR, NDR, PDR]
-3n-tsh-10ge2p1x520-ixgbe:
+3n-tsh-10ge2p1x520-dpdk:
l2-base:
label: L2 Ethernet Switching Base
test: