diff --git a/backend/app/api/v1/commons/example_responses.py b/backend/app/api/v1/commons/example_responses.py index d5b527e3..da95c682 100644 --- a/backend/app/api/v1/commons/example_responses.py +++ b/backend/app/api/v1/commons/example_responses.py @@ -124,17 +124,21 @@ def response_422(): "endDate": "2023-09-20", "results": [ { + "uuid": "4de79cb7a1b071bce282e8d0ce2006c7", + "encryptedData": "gAAAAABmR0NYcO6AZo4nGjkT1IBVeoD=", "ciSystem": "Jenkins", - "uuid": "2cc5d4ca895ca5d84cab0fd7923db93b", - "encrypted": "gAAAAABmQALtP0g5UPMsOjQw46tZ-aBz77yl-8QNI4jwLfIEV1POnOlA1ny89cp3Nrik3OzpNwXrWO3K4ZwtOliTfk0SO5NkNZHY8reJhvOVJBGFEw2enyjRaHp9hIaJdE0Vrfuqt_NjiYX-vOZo0Sjc84R76LvxjAC6f_urceGGZICH36IkT2g=", - "releaseStream": "Release Candidate", + "benchmark": "deployment", + "shortVersion": "4.16", + "ocpVersion": "4.16.0-0.nightly-2024-05-16-092402", + "releaseStream": "Nightly", + "nodeName": "kni-qe-66", + "cpu": "Intel(R) Xeon? Gold 5423N", + "formal": "true", + "startDate": "2024-05-16 19:39:48+00:00", + "endDate": "2024-05-16 20:41:48+00:00", + "buildUrl": "https://ci-jenkins-csb-kniqe.apps.ocp-c1.prod.psi.redhat.com/job/ocp-far-edge-vran-tests/10506", "jobStatus": "success", - "buildUrl": "https://ci-jenkins-csb-kniqe.apps.ocp-c1.prod.psi.redhat.com/job/ocp-far-edge-vran-tests/532", - "startDate": "2024-05-09 14:10:51+00:00", - "endDate": "2024-05-09 14:43:51+00:00", - "product": "telco", - "version": "4.16", - "testName": "reboot" + "jobDuration": 3720 }, ] } diff --git a/backend/app/api/v1/commons/telco.py b/backend/app/api/v1/commons/telco.py index 01b2c0f6..c2af92fd 100644 --- a/backend/app/api/v1/commons/telco.py +++ b/backend/app/api/v1/commons/telco.py @@ -3,6 +3,7 @@ from app.services.splunk import SplunkService import app.api.v1.commons.hasher as hasher from datetime import datetime, timezone +import app.api.v1.commons.utils as utils async def getData(start_datetime: date, end_datetime: date, configpath: str): @@ -35,18 +36,25 @@ async def getData(start_datetime: date, end_datetime: date, configpath: str): start_timestamp = end_timestamp - execution_time_seconds start_time_utc = datetime.fromtimestamp(start_timestamp, tz=timezone.utc) end_time_utc = datetime.fromtimestamp(end_timestamp, tz=timezone.utc) + kernel = test_data['kernel'] if 'kernel' in test_data else "Undefined" mapped_list.append({ "uuid": hash_digest, "encryptedData": encrypted_data.decode('utf-8'), "ciSystem": "Jenkins", - "testName": test_data['test_type'], - "version": test_data['ocp_version'], - "releaseStream": test_data['ocp_build'], + "benchmark": test_data['test_type'], + "kernel": kernel, + "shortVersion": test_data['ocp_version'], + "ocpVersion": test_data['ocp_build'], + "releaseStream": utils.getReleaseStream({'releaseStream': test_data['ocp_build']}), + "nodeName": test_data['node_name'], + "cpu": test_data['cpu'], + 'formal': test_data['formal'], "startDate": str(start_time_utc), "endDate": str(end_time_utc), "buildUrl": jenkins_url + str(test_data['cluster_artifacts']['ref']['jenkins_build']), - "jobStatus": "success" + "jobStatus": "success", + "jobDuration": execution_time_seconds, }) jobs = pd.json_normalize(mapped_list) diff --git a/backend/app/api/v1/endpoints/cpt/maps/telco.py b/backend/app/api/v1/endpoints/cpt/maps/telco.py index 4052c923..c924f0f8 100644 --- a/backend/app/api/v1/endpoints/cpt/maps/telco.py +++ b/backend/app/api/v1/endpoints/cpt/maps/telco.py @@ -10,4 +10,6 @@ async def telcoMapper(start_datetime: date, end_datetime: date): df = await getData(start_datetime, end_datetime, f'telco.splunk') df.insert(len(df.columns), "product", "telco") df["releaseStream"] = df.apply(getReleaseStream, axis=1) + df["version"] = df["shortVersion"] + df["testName"] = df["benchmark"] return df diff --git a/frontend/public/assets/images/splunk-icon.png b/frontend/public/assets/images/splunk-icon.png new file mode 100644 index 00000000..b96ad790 Binary files /dev/null and b/frontend/public/assets/images/splunk-icon.png differ diff --git a/frontend/src/App.js b/frontend/src/App.js index 2715590a..4b8c6382 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -6,13 +6,14 @@ import { PageSection, PageSectionVariants, } from '@patternfly/react-core'; -import {fetchOCPJobsData, fetchCPTJobsData, fetchQuayJobsData} from "./store/Actions/ActionCreator"; +import {fetchOCPJobsData, fetchCPTJobsData, fetchQuayJobsData, fetchTelcoJobsData} from "./store/Actions/ActionCreator"; import {useDispatch} from "react-redux"; import {Route, Switch, BrowserRouter as Router} from "react-router-dom"; import {NavBar} from "./components/NavBar/NavBar"; import {HomeView} from "./components/Home/HomeView"; import {OCPHome} from './components/OCP/OCPHome'; import {QuayHome} from './components/Quay/QuayHome'; +import {TelcoHome} from './components/Telco/TelcoHome'; export const App = () => { @@ -23,6 +24,7 @@ export const App = () => { await dispatch(fetchOCPJobsData()) await dispatch(fetchCPTJobsData()) await dispatch(fetchQuayJobsData()) + await dispatch(fetchTelcoJobsData()) } fetchData() }, [dispatch]) @@ -45,6 +47,7 @@ export const App = () => { + diff --git a/frontend/src/components/NavBar/ToolBar.js b/frontend/src/components/NavBar/ToolBar.js index 3179a916..24161d93 100644 --- a/frontend/src/components/NavBar/ToolBar.js +++ b/frontend/src/components/NavBar/ToolBar.js @@ -23,9 +23,10 @@ export const ToolBar = () => { return styles; }; - // Selectors for both ocpJobs and quayJobs + // Selectors for OCP, Quay and Telco jobs const ocpJobResults = useSelector((state) => state.ocpJobs); const quayJobResults = useSelector((state) => state.quayJobs); + const telcoJobResults = useSelector((state) => state.telcoJobs); const NavItems = ( <> @@ -45,7 +46,6 @@ export const ToolBar = () => { onClick={() => setActive("/ocp")} /> - {/* New Quay ToolbarItem */} { onClick={() => setActive("/quay")} /> + + setActive("/telco")} + /> + ); @@ -70,7 +78,11 @@ export const ToolBar = () => { diff --git a/frontend/src/components/Telco/BenchmarkResults.js b/frontend/src/components/Telco/BenchmarkResults.js new file mode 100644 index 00000000..b2692dd7 --- /dev/null +++ b/frontend/src/components/Telco/BenchmarkResults.js @@ -0,0 +1,21 @@ +import {Grid, GridItem} from "@patternfly/react-core"; +import InstallCard from "./InstallCard"; +import React from "react"; +import {DisplayGraph} from "./DisplayGraph"; + + +export const BenchmarkResults = ({dataset, isExpanded}) => { + return ( + <> { + ( isExpanded && + + + + + + ) || <>NO Data + } + + ) +} diff --git a/frontend/src/components/Telco/DisplayGraph.js b/frontend/src/components/Telco/DisplayGraph.js new file mode 100644 index 00000000..a9e18244 --- /dev/null +++ b/frontend/src/components/Telco/DisplayGraph.js @@ -0,0 +1,38 @@ +import {PlotlyView} from "../ReactGraphs/plotly/PlotlyView"; +import React, {useEffect} from "react"; +import {useDispatch, useSelector} from "react-redux"; +import CardView from "../PatternflyComponents/Card/CardView"; +import {Text6} from "../PatternflyComponents/Text/Text"; +import {SplitView} from "../PatternflyComponents/Split/SplitView"; +import {Spinner} from "@patternfly/react-core"; +import {fetchGraphData} from "../../store/Actions/ActionCreator"; + + +export const DisplayGraph = ({uuid, benchmark}) => { + + const [isExpanded, setExpanded] = React.useState(true) + const onExpand = () => setExpanded(!isExpanded) + + const dispatch = useDispatch() + const job_results = useSelector(state => state.graph) + const graphData = job_results.uuid_results[uuid] + + useEffect(() => { + dispatch(fetchGraphData(uuid)) + }, [dispatch, uuid]) + + const getGraphBody = () => { + return (job_results.graphError && ) || + (graphData && ) || + , ]} /> + } + + return <> + } + body={ getGraphBody() } + isExpanded={isExpanded} + expandView={true} + onExpand={onExpand} + /> + +} diff --git a/frontend/src/components/Telco/InstallCard.js b/frontend/src/components/Telco/InstallCard.js new file mode 100644 index 00000000..895401e4 --- /dev/null +++ b/frontend/src/components/Telco/InstallCard.js @@ -0,0 +1,97 @@ +import React from 'react'; +import { Card, CardTitle, CardBody, CardFooter, CardHeader, CardExpandableContent } from '@patternfly/react-core'; +import { Grid, GridItem } from '@patternfly/react-core'; +import { Spinner } from '@patternfly/react-core'; +import { formatTime } from '../../helpers/Formatters' +import { FaCheck, FaExclamationCircle, FaExclamationTriangle } from "react-icons/fa"; +import { DisplaySplunk } from '../commons/DisplaySplunk'; + + +export default function InstallCard(props) { + let config = props.data + const [isExpanded, setExpanded] = React.useState([true, null]) + + + const onExpand = () => { + setExpanded(!isExpanded) + }; + + const icons = { + "failed": <>, + "success": <>, + "upstream_failed": <>, + + } + + if (config) { + return ( + + + Configs + + + + + + + Cluster Metadata + +
    +
  • Release Binary: {config.cluster_version && config.cluster_version || config.ocpVersion}
  • +
  • Cluster Name: {config.cluster_name && config.cluster_name || config.nodeName}
  • +
  • Cluster Type: {config.cluster_type && config.cluster_type || "SNO spoke"}
  • +
  • Network Type: {config.network_type && config.network_type || "OVNKubernetes"}
  • +
  • CPU: {config.cpu_version && config.cpu_version || config.cpu}
  • +
  • Kernel: {config.kernel_version && config.kernel_version || config.kernel}
  • +
  • IsFormal: {config.is_formal && config.is_formal || config.formal}
  • +
  • Benchmark Status: {icons[config.job_status && config.job_status || config.jobStatus] || config.job_status && config.job_status || config.jobStatus}
  • +
  • Duration: {formatTime(config.job_duration && config.job_duration || config.jobDuration)}
  • +
+
+
+
+ + + Node Types + +
    +
  • Master: {config.master_type && config.master_type || "Baremetal"}
  • +
  • Workload: {config.workload_type && config.workload_type || config.benchmark}
  • +
+
+
+
+ + Node Counts + +
    +
  • Master: {config.master_count && config.master_count || "1"}
  • +
  • Total Nodes: {config.workload_count && config.workload_count || "1"}
  • +
+
+
+
+
+
+ + +
) + } else { + return ( + + Install Configuration +
Awaiting Results
+ +
+ ) + } + +} diff --git a/frontend/src/components/Telco/TelcoHome.js b/frontend/src/components/Telco/TelcoHome.js new file mode 100644 index 00000000..557198c4 --- /dev/null +++ b/frontend/src/components/Telco/TelcoHome.js @@ -0,0 +1,90 @@ +import '../css/PlatformView.css'; +import "@patternfly/react-core/dist/styles/base.css"; +import React, {useEffect, useState} from 'react'; + +import {HomeLayout} from "../templates/HomeLayout"; +import {useDispatch, useSelector} from "react-redux"; +import {useHistory, useLocation} from "react-router-dom"; +import {updateTelcoDataFilter} from "../../store/reducers/TelcoJobsReducer"; +import {fetchTelcoJobsData} from "../../store/Actions/ActionCreator"; +import {BenchmarkResults} from "./BenchmarkResults"; + + +export function TelcoHome() { + + const dispatch = useDispatch() + const {search} = useLocation(); + const searchParams = new URLSearchParams(search); + const history = useHistory(); + const telcoJobs = useSelector(state => state.telcoJobs) + + const topHeadersData = [ + {loading: telcoJobs.waitForUpdate, title: 'No. Jobs', value: telcoJobs.total}, + {loading: telcoJobs.waitForUpdate, title: 'Success', value: telcoJobs.success}, + {loading: telcoJobs.waitForUpdate, title: 'Failure', value: telcoJobs.failure}, + {loading: telcoJobs.waitForUpdate, title: 'Others', value: telcoJobs.others}, + {loading: telcoJobs.waitForUpdate, title: 'Duration Running', value: telcoJobs.duration} + ] + + const [ciSystem, setCiSystem] = useState(searchParams.get("ciSystem") || telcoJobs.selectedCiSystem) + const [benchmark, setBenchmark] = useState(searchParams.get("benchmark") || telcoJobs.selectedBenchmark) + const [version, setVersion] = useState(searchParams.get("version") || telcoJobs.selectedVersion) + const [releaseStream, setReleaseStream] = useState(searchParams.get("releaseStream") || telcoJobs.selectedReleaseStream) + const [formal, setFormal] = useState(searchParams.get("formal") || telcoJobs.selectedFormal) + const [cpu, setCpu] = useState(searchParams.get("cpu") || telcoJobs.selectedCpu) + const [nodeName, setNodeName] = useState(searchParams.get("nodeName") || telcoJobs.selectedNodeName) + const [startDate, setStartDate] = useState(searchParams.get("startDate") || telcoJobs.startDate) || "" + const [endDate, setEndDate] = useState(searchParams.get("endDate") || telcoJobs.endDate) || "" + + const sidebarComponents = [ + {name: "DateComponent", options: [], onChange: null, value: null, startDate: startDate, endDate: endDate, setStartDate: setStartDate, setEndDate: setEndDate}, + {name: "Ci System", onChange: setCiSystem, value: ciSystem, options: telcoJobs.ciSystems}, + {name: "Benchmark", onChange: setBenchmark, value: benchmark, options: telcoJobs.benchmarks }, + {name: "Versions", onChange: setVersion, value: version, options: telcoJobs.versions}, + {name: "Release Streams", onChange: setReleaseStream, value: releaseStream, options: telcoJobs.releaseStreams}, + {name: "isFormal", onChange: setFormal, value: formal, options: telcoJobs.formals}, + {name: "Cpu", onChange: setCpu, value: cpu, options: telcoJobs.cpus}, + {name: "Node Name", onChange: setNodeName, value: nodeName, options: telcoJobs.nodeNames}, + ] + + useEffect(() => { + let buildParams = '' + if(ciSystem !== '') buildParams += `&ciSystem=${ciSystem}` + if(benchmark !== '') buildParams += `&benchmark=${benchmark}` + if(version !== '') buildParams += `&version=${version}` + if(releaseStream !== '') buildParams += `&releaseStream=${releaseStream}` + if(formal !== '') buildParams += `&formal=${formal}` + if(cpu !== '') buildParams += `&cpu=${cpu}` + if(nodeName !== '') buildParams += `&nodeName=${nodeName}` + if(startDate !== '') buildParams += `&startDate=${startDate}` + if(endDate !== '') buildParams += `&endDate=${endDate}` + history.push(`/telco?${buildParams.substring(1)}`, { replace: true }); + + }, [history, ciSystem, benchmark, version, releaseStream, formal, cpu, nodeName, startDate, endDate]) + + useEffect( ()=>{ + dispatch(updateTelcoDataFilter({ciSystem, benchmark, version, releaseStream, formal, cpu, nodeName})) + }, [ ciSystem, benchmark, version, releaseStream, formal, cpu, nodeName, dispatch ]) + + useEffect(() => { + if(startDate || endDate){ + dispatch(fetchTelcoJobsData(startDate, endDate)) + } + }, [startDate, endDate, dispatch]) + + useEffect(() => { + if(endDate === ""){ + setEndDate(telcoJobs.endDate) + setStartDate(telcoJobs.startDate) + } + }, [endDate, setEndDate, setStartDate, telcoJobs.startDate, telcoJobs.endDate]) + + + return ( + + ); +} diff --git a/frontend/src/components/commons/DisplayGrafana.js b/frontend/src/components/commons/DisplayGrafana.js index 84c27f6b..d200527a 100644 --- a/frontend/src/components/commons/DisplayGrafana.js +++ b/frontend/src/components/commons/DisplayGrafana.js @@ -5,6 +5,7 @@ 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}) { @@ -38,18 +39,6 @@ export function DisplayGrafana({benchmarkConfigs}) { } -const getBuildLink = (benchmarkConfigs) => { - var icon = "assets/images/jenkins-icon.svg" - if (benchmarkConfigs.ciSystem === "PROW") { - icon = "assets/images/prow-icon.png" - } - return - - -} - - const getGrafanaData = (benchmarkConfigs) => { const grafanaURL = "https://grafana.rdu2.scalelab.redhat.com:3000/d/"; const dashboardKubeBurner = "D5E8c5XVz/kube-burner-report-mode?orgId=1" diff --git a/frontend/src/components/commons/DisplaySplunk.js b/frontend/src/components/commons/DisplaySplunk.js new file mode 100644 index 00000000..d391490b --- /dev/null +++ b/frontend/src/components/commons/DisplaySplunk.js @@ -0,0 +1,91 @@ +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 DisplaySplunk({benchmarkConfigs}) { + + + + const icons = { + "failed": , + "failure": , + "success": , + "upstream_failed": , + + } + + const { getSplunkLink, getTimeFormat, status } = getSplunkData(benchmarkConfigs) + + return ( + ] + } + /> || } + /> + ) + +} + +const getSplunkData = (benchmarkConfigs) => { + const splunkURL = "https://rhcorporate.splunkcloud.com/en-GB/app/search/"; + let status = null; + let getTimeFormat = null; + let getSplunkLink = null; + if(benchmarkConfigs){ + const startDate = new Date(benchmarkConfigs.startDate) + const endDate = new Date(benchmarkConfigs.endDate) + + // Adding 10 more minutes for a buffer. + endDate.setMinutes(endDate.getMinutes() + 10); + + let getSplunkURL = null; + switch(benchmarkConfigs.benchmark) { + case 'cyclictest': + getSplunkURL = `${splunkURL}cyclictest_kpis?form.global_time.earliest=${encodeURIComponent(startDate.toISOString())}&form.global_time.latest=${encodeURIComponent(endDate.toISOString())}&form.ocp_view=ocp_version&form.formal_tag=${encodeURIComponent(benchmarkConfigs.formal)}&form.ocp_version=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.ocp_build=${encodeURIComponent(benchmarkConfigs.ocpVersion)}&form.node_name=${encodeURIComponent(benchmarkConfigs.nodeName)}&form.general_statistics=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.dashboard_kernels=${encodeURIComponent(benchmarkConfigs.kernel)}`; + break; + case 'cpu_util': + getSplunkURL = `${splunkURL}cpu_util_kpis?form.global_time.earliest=${encodeURIComponent(startDate.toISOString())}&form.global_time.latest=${encodeURIComponent(endDate.toISOString())}&form.high_cpu_treshhold=0.03&form.formal_tag=${encodeURIComponent(benchmarkConfigs.formal)}&form.ocp_version=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.ocp_build=${encodeURIComponent(benchmarkConfigs.ocpVersion)}&form.node_name=${encodeURIComponent(benchmarkConfigs.nodeName)}&form.dashboard_kernels=${encodeURIComponent(benchmarkConfigs.kernel)}&form.selected_duration=*&form.general_statistics=${encodeURIComponent(benchmarkConfigs.shortVersion)}`; + break; + case 'deployment': + getSplunkURL = `${splunkURL}deployment_kpis?form.global_time.earliest=${encodeURIComponent(startDate.toISOString())}&form.global_time.latest=${encodeURIComponent(endDate.toISOString())}&form.formal_tag=${encodeURIComponent(benchmarkConfigs.formal)}&form.charts_comparison=ocp_version&form.ocp_version=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.ocp_build=${encodeURIComponent(benchmarkConfigs.ocpVersion)}&form.node_name=${encodeURIComponent(benchmarkConfigs.nodeName)}&form.general_statistics=${encodeURIComponent(benchmarkConfigs.shortVersion)}`; + break; + case 'oslat': + getSplunkURL = `${splunkURL}oslat_kpis?form.global_time.earliest=${encodeURIComponent(startDate.toISOString())}&form.global_time.latest=${encodeURIComponent(endDate.toISOString())}&form.ocp_view=ocp_version&form.formal_tag=${encodeURIComponent(benchmarkConfigs.formal)}&form.ocp_version=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.ocp_build=${encodeURIComponent(benchmarkConfigs.ocpVersion)}&form.node_name=${encodeURIComponent(benchmarkConfigs.nodeName)}&form.general_statistics=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.dashboard_kernels=${encodeURIComponent(benchmarkConfigs.kernel)}`; + break; + case 'ptp': + getSplunkURL = `${splunkURL}ptp_kpis?form.global_time.earliest=${encodeURIComponent(startDate.toISOString())}&form.global_time.latest=${encodeURIComponent(endDate.toISOString())}&form.formal_tag=${encodeURIComponent(benchmarkConfigs.formal)}&form.bubble_chart_legend=ocp_build&form.ocp_version=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.ocp_build=${encodeURIComponent(benchmarkConfigs.ocpVersion)}&form.node_name=${encodeURIComponent(benchmarkConfigs.nodeName)}&form.general_statistics=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.dashboard_kernels=${encodeURIComponent(benchmarkConfigs.kernel)}`; + break; + case 'reboot': + getSplunkURL = `${splunkURL}reboot_kpis?form.global_time.earliest=${encodeURIComponent(startDate.toISOString())}&form.global_time.latest=${encodeURIComponent(endDate.toISOString())}&form.formal_tag=${encodeURIComponent(benchmarkConfigs.formal)}&form.charts_comparison=ocp_version&form.reboot_type=soft_reboot&form.ocp_version=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.ocp_build=${encodeURIComponent(benchmarkConfigs.ocpVersion)}&form.node_name=${encodeURIComponent(benchmarkConfigs.nodeName)}&form.general_statistics=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.dashboard_kernels=${encodeURIComponent(benchmarkConfigs.kernel)}`; + break; + case 'rfc-2544': + getSplunkURL = `${splunkURL}rfc2544_?form.global_time.earliest=${encodeURIComponent(startDate.toISOString())}&form.global_time.latest=${encodeURIComponent(endDate.toISOString())}&form.bubble_chart_legend=kernel&form.formal_tag=${encodeURIComponent(benchmarkConfigs.formal)}&form.ocp_version=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.ocp_build=${encodeURIComponent(benchmarkConfigs.ocpVersion)}&form.node_name=${encodeURIComponent(benchmarkConfigs.nodeName)}&form.general_statistics=${encodeURIComponent(benchmarkConfigs.shortVersion)}&form.histogram=${encodeURIComponent(benchmarkConfigs.ocpVersion)}&form.dashboard_kernels=${encodeURIComponent(benchmarkConfigs.kernel)}`; + break; + } + + getTimeFormat = status !== "upstream_failed" ? + formatTime(benchmarkConfigs.job_duration && + benchmarkConfigs.job_duration || benchmarkConfigs.jobDuration) + : "Skipped" + getSplunkLink = + {"Splunk + + } + return { + getSplunkLink, getTimeFormat, status + } +} diff --git a/frontend/src/components/commons/commons.js b/frontend/src/components/commons/commons.js new file mode 100644 index 00000000..4cced70e --- /dev/null +++ b/frontend/src/components/commons/commons.js @@ -0,0 +1,10 @@ +export function getBuildLink(benchmarkConfigs) { + var icon = "assets/images/jenkins-icon.svg" + if (benchmarkConfigs.ciSystem === "PROW") { + icon = "assets/images/prow-icon.png" + } + return + + +} \ No newline at end of file diff --git a/frontend/src/store/Actions/ActionCreator.js b/frontend/src/store/Actions/ActionCreator.js index a6112761..938d914e 100644 --- a/frontend/src/store/Actions/ActionCreator.js +++ b/frontend/src/store/Actions/ActionCreator.js @@ -1,5 +1,5 @@ -import {BASE_URL, OCP_GRAPH_API_V1, OCP_JOBS_API_V1, CPT_JOBS_API_V1, QUAY_JOBS_API_V1, QUAY_GRAPH_API_V1} from "../Shared"; +import {BASE_URL, OCP_GRAPH_API_V1, OCP_JOBS_API_V1, CPT_JOBS_API_V1, QUAY_JOBS_API_V1, QUAY_GRAPH_API_V1, TELCO_JOBS_API_V1} from "../Shared"; import axios from "axios"; import { errorOCPCall, @@ -19,6 +19,12 @@ import { setWaitForQuayUpdate, updateQuayMetaData } from "../reducers/QuayJobsReducer"; +import { + errorTelcoCall, + getTelcoJobsData, + setWaitForTelcoUpdate, + updateTelcoMetaData +} from "../reducers/TelcoJobsReducer"; import {getUuidResults, setGraphError} from "../reducers/GraphReducer"; import {getQuayUuidResults, setQuayGraphError} from "../reducers/QuayGraphReducer"; @@ -158,6 +164,48 @@ export const fetchQuayJobsData = (startDate = '', endDate='') => async dispatch } } +export const fetchTelcoJobsData = (startDate = '', endDate='') => async dispatch => { + let buildUrl = `${BASE_URL}${TELCO_JOBS_API_V1}` + dispatch(setWaitForTelcoUpdate({waitForUpdate:true})) + if(startDate !== '' && endDate !== '') { + buildUrl += `?start_date=${startDate}&end_date=${endDate}` + } + try{ + let api_data = await fetchAPI(buildUrl) + dispatch(setWaitForTelcoUpdate({waitForUpdate:false})) + api_data = JSON.parse(api_data) + if(api_data){ + const results = api_data.results + if(results){ + const benchmarks = GetBenchmarks(results) + const versions = GetVersions(results) + const releaseStreams = GetReleaseStreams(results) + const ciSystems = GetCiSystems(results) + const formals = GetFormals(results) + const nodeNames = GetNodeNames(results) + const cpus = GetCpus(results) + const updatedTime = new Date().toLocaleString().replace(', ', ' ').toString(); + await dispatch(getTelcoJobsData({ + data: results, benchmarks, versions, releaseStreams, waitForUpdate: false, formals, nodeNames, cpus, + updatedTime, ciSystems, startDate: api_data.startDate, endDate: api_data.endDate + })) + await dispatch(updateTelcoMetaData({data: results})) + } + } + } + catch (e) { + const error = e + if(error.response){ + await dispatch(errorTelcoCall({error: error.response.data.error})) + alert(error.response.data.error) + } + else{ + console.log(error) + } + dispatch(setWaitForTelcoUpdate({waitForUpdate:false})) + } +} + export const fetchCPTJobsData = (startDate = '', endDate='') => async dispatch => { let buildUrl = `${BASE_URL}${CPT_JOBS_API_V1}` dispatch(setWaitForCPTUpdate({waitForUpdate:true})) @@ -206,7 +254,7 @@ const GetCiSystems = (api_data) => { } export const GetVersions = (api_data) => { - return Array.from(new Set(api_data.map(item => item.shortVersion.toLowerCase().trim()))).sort() + return Array.from(new Set(api_data.map(item => item.shortVersion).filter(shortVersion => shortVersion !== null && shortVersion !== "").map(shortVersion => shortVersion.toUpperCase().trim()))).sort(); } export const GetBenchmarks = (api_data) => { @@ -249,7 +297,19 @@ const GetStatuses = (api_data) => { } const GetReleaseStreams = (api_data) => { - return Array.from(new Set(api_data.map(item => item.releaseStream.toUpperCase().trim()))).sort() + return Array.from(new Set(api_data.map(item => item.releaseStream).filter(releaseStream => releaseStream !== null && releaseStream !== "").map(releaseStream => releaseStream.toUpperCase().trim()))).sort(); +} + +const GetFormals = (api_data) => { + return Array.from(new Set(api_data.map(item => item.formal).filter(formal => formal !== null && formal !== "").map(formal => formal.toUpperCase().trim()))).sort(); +} + +const GetNodeNames = (api_data) => { + return Array.from(new Set(api_data.map(item => item.nodeName).filter(nodeName => nodeName !== null && nodeName !== "").map(nodeName => nodeName.toUpperCase().trim()))).sort(); +} + +const GetCpus = (api_data) => { + return Array.from(new Set(api_data.map(item => item.cpu).filter(cpu => cpu !== null && cpu !== "").map(cpu => cpu.toUpperCase().trim()))).sort(); } const GetTestNames = (api_data) => { diff --git a/frontend/src/store/Shared.js b/frontend/src/store/Shared.js index 31a078c9..fd2856f5 100644 --- a/frontend/src/store/Shared.js +++ b/frontend/src/store/Shared.js @@ -13,4 +13,6 @@ export const OCP_GRAPH_API_V1 = "/api/v1/ocp/graph" export const CPT_JOBS_API_V1 = "/api/v1/cpt/jobs" export const QUAY_JOBS_API_V1 = "/api/v1/quay/jobs" -export const QUAY_GRAPH_API_V1 = "/api/v1/quay/graph" \ No newline at end of file +export const QUAY_GRAPH_API_V1 = "/api/v1/quay/graph" + +export const TELCO_JOBS_API_V1 = "/api/v1/telco/jobs" \ No newline at end of file diff --git a/frontend/src/store/reducers/InitialData.js b/frontend/src/store/reducers/InitialData.js index f70aa1bc..e60bbfbe 100644 --- a/frontend/src/store/reducers/InitialData.js +++ b/frontend/src/store/reducers/InitialData.js @@ -91,6 +91,44 @@ export const QUAY_INITIAL_DATA = { {name: "Status", value: "jobStatus"}], } +export const TELCO_INITIAL_DATA = { + initialState: true, + success: 0, + failure: 0, + total: 0, + others: 0, + duration:0, + ciSystems: ["All"], + benchmarks: ["All"], + versions: ["All"], + releaseStreams: ["All"], + formals: ["All"], + nodeNames: ["All"], + cpus: ["All"], + selectedCiSystem: "All", + selectedBenchmark: "All", + selectedVersion: "All", + selectedReleaseStream: "All", + selectedFormal: "All", + selectedCpu: "All", + selectedNodeName: "All", + waitForUpdate: false, + copyData: [], + data: [], + updatedTime: 'Loading', + error: null, + startDate: '', + endDate: '', + tableData : [{ name: "Benchmark", value: "benchmark" }, + {name:"Release Stream", value: "releaseStream"}, + {name:"Build", value: "ocpVersion"}, + {name:"CPU", value: "cpu"}, + {name:"Node Name", value: "nodeName"}, + {name: "Start Date", value: "startDate"}, + {name: "End Date", value: "endDate"}, + {name: "Status", value: "jobStatus"}], +} + export const CPT_INITIAL_DATA = { initialState: true, success: 0, diff --git a/frontend/src/store/reducers/TelcoJobsReducer.js b/frontend/src/store/reducers/TelcoJobsReducer.js new file mode 100644 index 00000000..8e9f8654 --- /dev/null +++ b/frontend/src/store/reducers/TelcoJobsReducer.js @@ -0,0 +1,61 @@ +import {createSlice, original} from "@reduxjs/toolkit"; +import {TELCO_INITIAL_DATA} from "./InitialData"; +import { getTelcoUpdatedData, getTelcoSummary } from './Utils'; + +const jobsSlice = createSlice({ + initialState: { + ...TELCO_INITIAL_DATA, + }, + name: 'telcoSplunk', + reducers: { + getTelcoJobsData: (state, action) => { + state.initialState = false + state.copyData = action.payload.data + state.data = action.payload.data + state.benchmarks = ["All", ...action.payload.benchmarks] + state.versions = ["All", ...action.payload.versions] + state.releaseStreams = ["All", ...action.payload.releaseStreams] + state.ciSystems = ["All", ...action.payload.ciSystems] + state.formals = ["All", ...action.payload.formals] + state.nodeNames = ["All", ...action.payload.nodeNames] + state.cpus = ["All", ...action.payload.cpus] + state.waitForUpdate = action.payload.waitForUpdate + state.updatedTime = action.payload.updatedTime + state.error = null + Object.assign(state, getTelcoSummary(state.data)) + state.startDate = action.payload.startDate + state.endDate = action.payload.endDate + }, + updateTelcoDataFilter: (state, action) => { + const {ciSystem, benchmark, version, releaseStream, formal, nodeName, cpu} = action.payload + state.selectedBenchmark = benchmark + state.selectedVersion = version + state.selectedReleaseStream = releaseStream + state.selectedCiSystem = ciSystem + state.selectedFormal = formal + state.selectedNodeName = nodeName + state.selectedCpu = cpu + state.data = getTelcoUpdatedData(original(state.copyData), benchmark, version, releaseStream, ciSystem, formal, nodeName, cpu) + Object.assign(state, getTelcoSummary(state.data)) + }, + updateTelcoMetaData: (state, action) => { + state.data = getTelcoUpdatedData(action.payload.data, state.selectedBenchmark, state.selectedVersion, state.selectedReleaseStream, + state.selectedCiSystem, state.selectedFormal, state.selectedNodeName, state.selectedCpu) + Object.assign(state, getTelcoSummary(state.data)) + }, + setWaitForTelcoUpdate: (state, action) => { + state.waitForUpdate = action.payload.waitForUpdate + }, + errorTelcoCall: (state, action) => { + state.error = action.payload.error + } + } +}) +export const { + getTelcoJobsData, + updateTelcoDataFilter, + updateTelcoMetaData, + setWaitForTelcoUpdate, + errorTelcoCall, +} = jobsSlice.actions +export default jobsSlice.reducer diff --git a/frontend/src/store/reducers/Utils.js b/frontend/src/store/reducers/Utils.js index 5ae40fb2..a826b4c2 100644 --- a/frontend/src/store/reducers/Utils.js +++ b/frontend/src/store/reducers/Utils.js @@ -56,6 +56,22 @@ const getQuayUpdatedData = (data, platform, benchmark, releaseStream, workerCoun return filteredData } +const getTelcoUpdatedData = (data, benchmark, version, releaseStream, ciSystem, formal, nodeName, cpu) => { + const filterValues = { + "cpu": cpu, "benchmark": benchmark, "shortVersion": version, + "releaseStream": releaseStream, "formal": formal, "ciSystem": ciSystem, + "nodeName": nodeName, + } + let filteredData = data + console.log(filteredData) + for (let [keyName, value] of Object.entries(filterValues)) + filteredData = getFilteredData(filteredData, value, keyName) + console.log(filterValues) + console.log(filteredData) + + return filteredData +} + const getCPTSummary = (api_data) => { let success = 0; let failure = 0; @@ -100,5 +116,20 @@ const getQuaySummary = (api_data) => { return { success, failure, others, total, duration }; } +const getTelcoSummary = (api_data) => { + let success = 0; + let failure = 0; + let others = 0; + let duration = 0; + api_data.forEach(item => { + if(item.jobStatus.toLowerCase() === "success") success++ + else if(item.jobStatus.toLowerCase() === "failure") failure++; + else others++; + duration += parseInt(item.jobDuration) ? parseInt(item.jobDuration) : 0; + }) + const total = success + failure + others + + return { success, failure, others, total, duration }; +} -export { getCPTUpdatedData, getOCPUpdatedData, getQuayUpdatedData, getCPTSummary, getOCPSummary, getQuaySummary }; \ No newline at end of file +export { getCPTUpdatedData, getOCPUpdatedData, getQuayUpdatedData, getTelcoUpdatedData, getCPTSummary, getOCPSummary, getQuaySummary, getTelcoSummary }; \ No newline at end of file diff --git a/frontend/src/store/reducers/index.js b/frontend/src/store/reducers/index.js index a057312d..ea1b6f90 100644 --- a/frontend/src/store/reducers/index.js +++ b/frontend/src/store/reducers/index.js @@ -1,6 +1,7 @@ import ocpJobsReducer from "./OCPJobsReducer"; import cptJobsReducer from "./CPTJobsReducer"; import quayJobsReducer from "./QuayJobsReducer"; +import telcoJobsReducer from "./TelcoJobsReducer"; import graphReducer from "./GraphReducer"; import quayGraphReducer from "./QuayGraphReducer"; @@ -9,6 +10,7 @@ export const rootReducer = { 'ocpJobs': ocpJobsReducer, 'cptJobs': cptJobsReducer, 'quayJobs': quayJobsReducer, + 'telcoJobs': telcoJobsReducer, 'graph': graphReducer, 'quayGraph': quayGraphReducer, }