diff --git a/frontend/README.md b/frontend/README.md
index 8032d5a0..0b01bbaf 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -1,7 +1,4 @@
-Currently, two official plugins are available:
-
-
# Openshift Performance Dashbaord
## Dashboard directory structure
diff --git a/frontend/src/assets/constants/grafanaConstants.js b/frontend/src/assets/constants/grafanaConstants.js
new file mode 100644
index 00000000..6458c52c
--- /dev/null
+++ b/frontend/src/assets/constants/grafanaConstants.js
@@ -0,0 +1,48 @@
+export const GRAFANA_BASE_URL =
+ "https://grafana.rdu2.scalelab.redhat.com:3000/d/";
+export const DASHBOARD_KUBE_BURNER =
+ "D5E8c5XVz/kube-burner-report-mode?orgId=1";
+export const DASHBOARD_INGRESS =
+ "d6105ff8-bc26-4d64-951e-56da771b703d/ingress-perf?orgId=1&var-termination=edge&" +
+ "var-termination=http&var-termination=passthrough&" +
+ "var-termination=reencrypt&var-latency_metric=p99_lat_us" +
+ "&var-compare_by=uuid.keyword";
+export const DASHBOARD_NET_PERF =
+ "wINGhybVz/k8s-netperf?orgId=1&var-samples=3&var-service=All&" +
+ "var-parallelism=All&var-profile=All&var-messageSize=All&" +
+ "var-driver=netperf&var-hostNetwork=true&var-hostNetwork=false";
+export const DASHBOARD_QUAY =
+ "7nkJIoXVk/quay-dashboard-v2?orgId=1&var-api_endpoints_datasource=Quay%20QE%20-%20quay-vegeta&" +
+ "var-quay_push_pull_datasource=Quay%20QE%20-%20quay-push-pull";
+
+export const PROW_DATASOURCE_NETPERF = "&var-datasource=QE+K8s+netperf";
+export const PROW_DATASOURCE_INGRESS = "&var-datasource=QE+Ingress-perf";
+export const JENKINS_DATASOURCE_NETPERF = "&var-datasource=k8s-netperf";
+export const JENKINS_DATASOURCE_INGRESS = "&var-datasource=QE+Ingress-perf";
+export const PROW_DATASOURCE = "&var-Datasource=QE+kube-burner";
+export const DEFAULT_DATASOURCE =
+ "&var-Datasource=AWS+Pro+-+ripsaw-kube-burner";
+export const QUAY_LOAD_TEST = "quay-load-test";
+
+export const ciSystemMap = {
+ prow: {
+ "k8s-netperf": {
+ dataSource: PROW_DATASOURCE_NETPERF,
+ dashboardURL: DASHBOARD_NET_PERF,
+ },
+ "ingress-perf": {
+ dataSource: PROW_DATASOURCE_INGRESS,
+ dashboardURL: DASHBOARD_INGRESS,
+ },
+ },
+ jenkins: {
+ "k8s-netperf": {
+ dataSource: JENKINS_DATASOURCE_NETPERF,
+ dashboardURL: DASHBOARD_NET_PERF,
+ },
+ "ingress-perf": {
+ dataSource: JENKINS_DATASOURCE_INGRESS,
+ dashboardURL: DASHBOARD_INGRESS,
+ },
+ },
+};
diff --git a/frontend/src/commons/DisplayGrafana.js b/frontend/src/commons/DisplayGrafana.js
deleted file mode 100644
index d200527a..00000000
--- a/frontend/src/commons/DisplayGrafana.js
+++ /dev/null
@@ -1,110 +0,0 @@
-import React from 'react';
-import { FaCheck, FaExclamationCircle, FaExclamationTriangle } from "react-icons/fa";
-import {formatTime} from "../../helpers/Formatters";
-import {SplitView} from "../PatternflyComponents/Split/SplitView";
-import CardView from "../PatternflyComponents/Card/CardView";
-import ListView from "../PatternflyComponents/List/ListView";
-import {Text6} from "../PatternflyComponents/Text/Text";
-import { getBuildLink } from './commons';
-
-
-export function DisplayGrafana({benchmarkConfigs}) {
-
-
-
- const icons = {
- "failed": ,
- "failure": ,
- "success": ,
- "upstream_failed": ,
-
- }
-
- const { getGrafanaLink, getTimeFormat, status } = getGrafanaData(benchmarkConfigs)
-
- return (
- ]
- }
- /> || }
- />
- )
-
-}
-
-const getGrafanaData = (benchmarkConfigs) => {
- const grafanaURL = "https://grafana.rdu2.scalelab.redhat.com:3000/d/";
- const dashboardKubeBurner = "D5E8c5XVz/kube-burner-report-mode?orgId=1"
- const dashboardIngress = "d6105ff8-bc26-4d64-951e-56da771b703d/ingress-perf?orgId=1&var-termination=edge&" +
- "var-termination=http&var-termination=passthrough&" +
- "var-termination=reencrypt&var-latency_metric=p99_lat_us" +
- "&var-compare_by=uuid.keyword"
- const dashboardNetPerf = "wINGhybVz/k8s-netperf?orgId=1&var-samples=3&var-service=All&" +
- "var-parallelism=All&var-profile=All&var-messageSize=All&" +
- "var-driver=netperf&var-hostNetwork=true&var-hostNetwork=false"
- const quayDashboard = "7nkJIoXVk/quay-dashboard-v2?orgId=1&var-api_endpoints_datasource=Quay%20QE%20-%20quay-vegeta&" +
- "var-quay_push_pull_datasource=Quay%20QE%20-%20quay-push-pull"
- let status = null;
- let getTimeFormat = null;
- let getGrafanaLink = null;
- let getGrafanaUrl = null;
- if(benchmarkConfigs){
- const startDate = new Date((benchmarkConfigs.start_date && benchmarkConfigs.start_date) || benchmarkConfigs.startDate).valueOf()
- const endDate = new Date((benchmarkConfigs.end_date && benchmarkConfigs.end_date) || benchmarkConfigs.endDate).valueOf()
- status = benchmarkConfigs.job_status && benchmarkConfigs.job_status || benchmarkConfigs.jobStatus
-
- let dashboardURL = dashboardKubeBurner
- let dataSource = "&var-Datasource=AWS+Pro+-+ripsaw-kube-burner"
- if (benchmarkConfigs.ciSystem === "PROW"){
- dataSource = "&var-Datasource=QE+kube-burner"
- if (benchmarkConfigs.benchmark === "k8s-netperf") {
- dataSource = "&var-datasource=QE+K8s+netperf"
- dashboardURL = dashboardNetPerf
- }
- if (benchmarkConfigs.benchmark === "ingress-perf") {
- dataSource = "&var-datasource=QE+Ingress-perf"
- dashboardURL = dashboardIngress
- }
- } else if(benchmarkConfigs.ciSystem === "JENKINS") {
- if (benchmarkConfigs.benchmark === "k8s-netperf") {
- dataSource = "&var-datasource=k8s-netperf"
- dashboardURL = dashboardNetPerf
- }
- if (benchmarkConfigs.benchmark === "ingress-perf") {
- dataSource = "&var-datasource=QE+Ingress-perf"
- dashboardURL = dashboardIngress
- }
- }
- if (benchmarkConfigs.benchmark === "quay-load-test") {
- getGrafanaUrl = grafanaURL+quayDashboard+
- "&from="+startDate+"&to="+endDate+
- "&var-uuid="+benchmarkConfigs.uuid
-
- } else {
- getGrafanaUrl = grafanaURL+dashboardURL+
- "&from="+startDate+"&to="+endDate+
- dataSource+"&var-platform="+benchmarkConfigs.platform+
- "&var-uuid="+benchmarkConfigs.uuid+
- "&var-workload="+benchmarkConfigs.benchmark
- }
-
- getTimeFormat = status !== "upstream_failed" ?
- formatTime(benchmarkConfigs.job_duration &&
- benchmarkConfigs.job_duration || benchmarkConfigs.jobDuration)
- : "Skipped"
- getGrafanaLink =
-
-
- }
- return {
- getGrafanaLink, getTimeFormat, status
- }
-}
diff --git a/frontend/src/components/atoms/LinkIcon/index.jsx b/frontend/src/components/atoms/LinkIcon/index.jsx
new file mode 100644
index 00000000..be63dfda
--- /dev/null
+++ b/frontend/src/components/atoms/LinkIcon/index.jsx
@@ -0,0 +1,21 @@
+import Proptypes from "prop-types";
+const LinkIcon = (props) => {
+ const { link, target, src, altText, height, width } = props;
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+LinkIcon.propTypes = {
+ link: Proptypes.string,
+ target: Proptypes.string,
+ src: Proptypes.node,
+ altText: Proptypes.string,
+ height: Proptypes.number,
+ width: Proptypes.number,
+};
+export default LinkIcon;
diff --git a/frontend/src/components/molecules/ExpandedRow/index.jsx b/frontend/src/components/molecules/ExpandedRow/index.jsx
index 5d6e930f..0edff39e 100644
--- a/frontend/src/components/molecules/ExpandedRow/index.jsx
+++ b/frontend/src/components/molecules/ExpandedRow/index.jsx
@@ -1,10 +1,13 @@
+import "./index.less";
+
import * as CONSTANTS from "@/assets/constants/metadataConstants";
-import { Card, CardBody, Grid, GridItem } from "@patternfly/react-core";
+import { Card, CardBody, Grid, GridItem, Title } from "@patternfly/react-core";
import MetadataRow from "../MetaDataRow";
import PlotGraph from "@/components/atoms/PlotGraph";
import PropTypes from "prop-types";
+import TasksInfo from "@/components/molecules/TasksInfo";
import { uid } from "@/utils/helper.js";
import { useSelector } from "react-redux";
@@ -43,6 +46,10 @@ const RowContent = (props) => {
>
))}
+
+ Tasks ran
+
+
diff --git a/frontend/src/components/molecules/ExpandedRow/index.less b/frontend/src/components/molecules/ExpandedRow/index.less
new file mode 100644
index 00000000..02499992
--- /dev/null
+++ b/frontend/src/components/molecules/ExpandedRow/index.less
@@ -0,0 +1,3 @@
+.type_heading {
+ padding: 10px 0;
+}
\ No newline at end of file
diff --git a/frontend/src/components/molecules/MetaDataRow/index.jsx b/frontend/src/components/molecules/MetaDataRow/index.jsx
index 6e4eb6dd..c6d46e57 100644
--- a/frontend/src/components/molecules/MetaDataRow/index.jsx
+++ b/frontend/src/components/molecules/MetaDataRow/index.jsx
@@ -28,7 +28,9 @@ const MetadataRow = (props) => {
return (
<>
- {props.heading}
+
+ {props.heading}
+
diff --git a/frontend/src/components/molecules/TasksInfo/index.jsx b/frontend/src/components/molecules/TasksInfo/index.jsx
new file mode 100644
index 00000000..4c385ec3
--- /dev/null
+++ b/frontend/src/components/molecules/TasksInfo/index.jsx
@@ -0,0 +1,108 @@
+import "./index.less";
+
+import * as CONSTANTS from "@/assets/constants/grafanaConstants";
+
+import {
+ CheckCircleIcon,
+ ExclamationCircleIcon,
+ ExclamationTriangleIcon,
+} from "@patternfly/react-icons";
+
+import GrafanaIcon from "@/assets/images/grafana-icon.png";
+import JenkinsIcon from "@/assets/images/jenkins-icon.svg";
+import LinkIcon from "@/components/atoms/LinkIcon";
+import Proptypes from "prop-types";
+import ProwIcon from "@/assets/images/prow-icon.png";
+import { formatTime } from "@/helpers/Formatters.js";
+import { useMemo } from "react";
+
+const TasksInfo = (props) => {
+ const { config } = props;
+
+ const startDate = useMemo(
+ () => new Date(config?.startDate).valueOf(),
+ [config?.startDate]
+ );
+ const endDate = useMemo(
+ () => new Date(config?.endDate).valueOf(),
+ [config?.endDate]
+ );
+
+ const status = useMemo(
+ () => config.jobStatus?.toLowerCase(),
+ [config.jobStatus]
+ );
+
+ const grafanaLink = useMemo(() => {
+ const ciSystem_lCase = config.ciSystem?.toLowerCase();
+ const isProw = ciSystem_lCase === "prow";
+ const discreteBenchmark =
+ CONSTANTS.ciSystemMap[ciSystem_lCase]?.[ciSystem_lCase?.benchmark];
+
+ const hasBenchmark = Object.prototype.hasOwnProperty.call(
+ CONSTANTS.ciSystemMap?.[ciSystem_lCase],
+ config.benchmark
+ );
+ const datasource = isProw
+ ? CONSTANTS.PROW_DATASOURCE
+ : hasBenchmark
+ ? discreteBenchmark?.dataSource
+ : CONSTANTS.DEFAULT_DATASOURCE;
+
+ const dashboardURL =
+ discreteBenchmark?.dashboardURL ?? CONSTANTS.DASHBOARD_KUBE_BURNER;
+
+ const datePart = `&from=${startDate}&to=${endDate}`;
+ const uuidPart = `&var-uuid=${config.uuid}`;
+
+ if (config.benchmark === CONSTANTS.QUAY_LOAD_TEST)
+ return `${CONSTANTS.GRAFANA_BASE_URL}${CONSTANTS.DASHBOARD_QUAY}${datePart}${uuidPart}`;
+ return `${CONSTANTS.GRAFANA_BASE_URL}${dashboardURL}${datasource}${datePart}&var-platform=${config.platform}"&var-workload=${config.benchmark}${uuidPart}`;
+ }, [
+ config.benchmark,
+ config.ciSystem,
+ config.platform,
+ config.uuid,
+ endDate,
+ startDate,
+ ]);
+
+ const icons = useMemo(
+ () => ({
+ failed: ,
+ failure: ,
+ success: ,
+ upstream_failed: ,
+ }),
+ []
+ );
+ return (
+ <>
+
+
{icons[status] ?? status.toUpperCase()}
+
{config.benchmark}
+
{`(${formatTime(config?.jobDuration)})`}
+
+
+
+ >
+ );
+};
+TasksInfo.propTypes = {
+ config: Proptypes.object,
+};
+export default TasksInfo;
diff --git a/frontend/src/components/molecules/TasksInfo/index.less b/frontend/src/components/molecules/TasksInfo/index.less
new file mode 100644
index 00000000..dd088b46
--- /dev/null
+++ b/frontend/src/components/molecules/TasksInfo/index.less
@@ -0,0 +1,5 @@
+.info-wrapper {
+ display: flex;
+ justify-content: space-between;
+ width: 50%;
+}
\ No newline at end of file
diff --git a/frontend/src/helpers/Formatters.js b/frontend/src/helpers/Formatters.js
index 275660a1..8e6c5776 100644
--- a/frontend/src/helpers/Formatters.js
+++ b/frontend/src/helpers/Formatters.js
@@ -1,31 +1,29 @@
export function formatTime(time) {
- // Hours, minutes and seconds
- var hrs = ~~(time / 3600);
- var mins = ~~((time % 3600) / 60);
- var secs = ~~time % 60;
+ // Hours, minutes and seconds
+ let hrs = ~~(time / 3600);
+ let mins = ~~((time % 3600) / 60);
+ let secs = ~~time % 60;
- // Output like "1:01" or "4:03:59" or "123:03:59"
- var ret = "";
- if (hrs > 0) {
- ret += "" + hrs + ":" + (mins < 10 ? "0" : "");
- }
- ret += "" + mins + ":" + (secs < 10 ? "0" : "");
- ret += "" + secs;
- return ret;
+ // Output like "1:01" or "4:03:59" or "123:03:59"
+ let ret = "";
+ if (hrs > 0) {
+ ret += "" + hrs + ":" + (mins < 10 ? "0" : "");
+ }
+ ret += "" + mins + ":" + (secs < 10 ? "0" : "");
+ ret += "" + secs;
+ return ret;
}
export function getFormattedDate(date) {
- let d = new Date();
- if (typeof date === 'string' || date instanceof String)
- d = new Date(date);
- else
- d = new Date(date.props.value);
+ let d = new Date();
+ if (typeof date === "string" || date instanceof String) d = new Date(date);
+ else d = new Date(date.props.value);
- var year = d.getFullYear();
- var month = ("0" + (d.getMonth() + 1)).slice(-2);
- var day = ("0" + (d.getDate())).slice(-2);
- var hour = ("0" + (d.getHours())).slice(-2);
- var min = ("0" + (d.getMinutes())).slice(-2);
- var sec = ("0" + (d.getSeconds())).slice(-2);
- return year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec;
+ let year = d.getFullYear();
+ let month = ("0" + (d.getMonth() + 1)).slice(-2);
+ let day = ("0" + d.getDate()).slice(-2);
+ let hour = ("0" + d.getHours()).slice(-2);
+ let min = ("0" + d.getMinutes()).slice(-2);
+ let sec = ("0" + d.getSeconds()).slice(-2);
+ return year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec;
}
diff --git a/frontend/vite.config.js b/frontend/vite.config.js
index 575c3e66..d2526bd7 100644
--- a/frontend/vite.config.js
+++ b/frontend/vite.config.js
@@ -2,7 +2,6 @@ import { defineConfig } from "vite";
import path from "path";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
-
export default defineConfig({
plugins: [react()],
esbuild: {