diff --git a/internal/metricdata/prometheus.go b/internal/metricdata/prometheus.go index d93a3c5..0d3848f 100644 --- a/internal/metricdata/prometheus.go +++ b/internal/metricdata/prometheus.go @@ -326,7 +326,6 @@ func (pdb *PrometheusDataRepository) LoadData( Timestep: metricConfig.Timestep, Series: make([]schema.Series, 0), } - jobData[metric][scope] = jobMetric } step := int64(metricConfig.Timestep) steps := int64(to.Sub(from).Seconds()) / step @@ -335,6 +334,10 @@ func (pdb *PrometheusDataRepository) LoadData( jobMetric.Series = append(jobMetric.Series, pdb.RowToSeries(from, step, steps, row)) } + // only add metric if at least one host returned data + if !ok && len(jobMetric.Series) > 0{ + jobData[metric][scope] = jobMetric + } // sort by hostname to get uniform coloring sort.Slice(jobMetric.Series, func(i, j int) bool { return (jobMetric.Series[i].Hostname < jobMetric.Series[j].Hostname) diff --git a/web/frontend/src/Analysis.root.svelte b/web/frontend/src/Analysis.root.svelte index 2e6f5b5..1cfadc9 100644 --- a/web/frontend/src/Analysis.root.svelte +++ b/web/frontend/src/Analysis.root.svelte @@ -1,10 +1,11 @@ + +{#if data.length > 0} +
+{:else} + Cannot render histogram: No data! +{/if} + + diff --git a/web/frontend/src/plots/MetricPlot.svelte b/web/frontend/src/plots/MetricPlot.svelte index 1565cde..7915c68 100644 --- a/web/frontend/src/plots/MetricPlot.svelte +++ b/web/frontend/src/plots/MetricPlot.svelte @@ -26,16 +26,17 @@ import { getContext, onMount, onDestroy } from 'svelte' import { Card } from 'sveltestrap' + export let metric + export let scope = 'node' + export let resources = [] export let width export let height export let timestep export let series + export let useStatsSeries = null export let statisticsSeries = null export let cluster export let subCluster - export let metric - export let useStatsSeries = null - export let scope = 'node' export let isShared = false export let forNode = false @@ -54,6 +55,70 @@ const backgroundColors = { normal: 'rgba(255, 255, 255, 1.0)', caution: 'rgba(255, 128, 0, 0.3)', alert: 'rgba(255, 0, 0, 0.3)' } const thresholds = findThresholds(metricConfig, scope, typeof subCluster == 'string' ? cluster.subClusters.find(sc => sc.name == subCluster) : subCluster) + // converts the legend into a simple tooltip + function legendAsTooltipPlugin({ className, style = { backgroundColor:"rgba(255, 249, 196, 0.92)", color: "black" } } = {}) { + let legendEl; + const dataSize = series.length + + function init(u, opts) { + legendEl = u.root.querySelector(".u-legend"); + + legendEl.classList.remove("u-inline"); + className && legendEl.classList.add(className); + + uPlot.assign(legendEl.style, { + textAlign: "left", + pointerEvents: "none", + display: "none", + position: "absolute", + left: 0, + top: 0, + zIndex: 100, + boxShadow: "2px 2px 10px rgba(0,0,0,0.5)", + ...style + }); + + // conditional hide series color markers: + if (useStatsSeries === true || // Min/Max/Avg Self-Explanatory + dataSize === 1 || // Only one Y-Dataseries + dataSize > 6 ){ // More than 6 Y-Dataseries + const idents = legendEl.querySelectorAll(".u-marker"); + for (let i = 0; i < idents.length; i++) + idents[i].style.display = "none"; + } + + const overEl = u.over; + overEl.style.overflow = "visible"; + + // move legend into plot bounds + overEl.appendChild(legendEl); + + // show/hide tooltip on enter/exit + overEl.addEventListener("mouseenter", () => {legendEl.style.display = null;}); + overEl.addEventListener("mouseleave", () => {legendEl.style.display = "none";}); + + // let tooltip exit plot + // overEl.style.overflow = "visible"; + } + + function update(u) { + const { left, top } = u.cursor; + const width = u.over.querySelector(".u-legend").offsetWidth; + legendEl.style.transform = "translate(" + (left - width - 15) + "px, " + (top + 15) + "px)"; + } + + if (dataSize <= 12 || useStatsSeries === true) { + return { + hooks: { + init: init, + setCursor: update, + } + } + } else { // Setting legend-opts show/live as object with false here will not work ... + return {} + } + } + function backgroundColor() { if (clusterCockpitConfig.plot_general_colorBackground == false || !thresholds @@ -94,7 +159,7 @@ ? (statisticsSeries.max.reduce((max, x) => Math.max(max, x), thresholds.normal) || thresholds.normal) : (series.reduce((max, series) => Math.max(max, series.statistics?.max), thresholds.normal) || thresholds.normal) : null - const plotSeries = [{}] + const plotSeries = [{label: 'Runtime', value: (u, ts, sidx, didx) => didx == null ? null : formatTime(ts)}] const plotData = [new Array(longestSeries)] if (forNode === true) { @@ -113,14 +178,17 @@ plotData.push(statisticsSeries.min) plotData.push(statisticsSeries.max) plotData.push(statisticsSeries.mean) + if (forNode === true) { // timestamp 0 with null value for reversed time axis if (plotData[1].length != 0) plotData[1].push(null) if (plotData[2].length != 0) plotData[2].push(null) if (plotData[3].length != 0) plotData[3].push(null) } - plotSeries.push({ scale: 'y', width: lineWidth, stroke: 'red' }) - plotSeries.push({ scale: 'y', width: lineWidth, stroke: 'green' }) - plotSeries.push({ scale: 'y', width: lineWidth, stroke: 'black' }) + + plotSeries.push({ label: 'min', scale: 'y', width: lineWidth, stroke: 'red' }) + plotSeries.push({ label: 'max', scale: 'y', width: lineWidth, stroke: 'green' }) + plotSeries.push({ label: 'mean', scale: 'y', width: lineWidth, stroke: 'black' }) + plotBands = [ { series: [2,3], fill: 'rgba(0,255,0,0.1)' }, { series: [3,1], fill: 'rgba(255,0,0,0.1)' } @@ -130,6 +198,9 @@ plotData.push(series[i].data) if (forNode === true && plotData[1].length != 0) plotData[1].push(null) // timestamp 0 with null value for reversed time axis plotSeries.push({ + label: scope === 'node' ? resources[i].hostname : + // scope === 'accelerator' ? resources[0].accelerators[i] : + scope + ' #' + (i+1), scale: 'y', width: lineWidth, stroke: lineColor(i, series.length) @@ -140,6 +211,9 @@ const opts = { width, height, + plugins: [ + legendAsTooltipPlugin() + ], series: plotSeries, axes: [ { @@ -193,8 +267,11 @@ x: { time: false }, y: maxY ? { range: [0., maxY * 1.1] } : {} }, - cursor: { show: false }, - legend: { show: false, live: false } + legend : { // Display legend until max 12 Y-dataseries + show: (series.length <= 12 || useStatsSeries === true) ? true : false, + live: (series.length <= 12 || useStatsSeries === true) ? true : false + }, + cursor: { drag: { x: true, y: true } } } // console.log(opts) @@ -265,16 +342,21 @@ }