review handling of disabled metrics in frontend

This commit is contained in:
Christoph Kluge
2026-02-09 15:33:59 +01:00
parent 7dd3ee3084
commit d1e7ea09bc
5 changed files with 189 additions and 196 deletions

View File

@@ -333,7 +333,18 @@
{:else if thisJob && $jobMetrics?.data?.scopedJobStats} {:else if thisJob && $jobMetrics?.data?.scopedJobStats}
<!-- Note: Ignore '#snippet' Error in IDE --> <!-- Note: Ignore '#snippet' Error in IDE -->
{#snippet gridContent(item)} {#snippet gridContent(item)}
{#if item.data} {#if item?.disabled}
<Card color="info" class="mt-2">
<CardHeader class="mb-0">
<b>Disabled Metric</b>
</CardHeader>
<CardBody>
<p>No dataset(s) returned for <b>{item.metric}</b></p>
<p class="mb-1">Metric has been disabled for subcluster <b>{thisJob.subCluster}</b>.</p>
<p class="mb-1">To remove this card, open metric selection, de-select the metric, and press "Close and Apply".</p>
</CardBody>
</Card>
{:else if item?.data}
<Metric <Metric
bind:this={plots[item.metric]} bind:this={plots[item.metric]}
job={thisJob} job={thisJob}
@@ -343,16 +354,6 @@
presetScopes={item.data.map((x) => x.scope)} presetScopes={item.data.map((x) => x.scope)}
isShared={thisJob.shared != "none"} isShared={thisJob.shared != "none"}
/> />
{:else if item.disabled == true}
<Card color="info">
<CardHeader class="mb-0">
<b>Disabled Metric</b>
</CardHeader>
<CardBody>
<p>Metric <b>{item.metric}</b> is disabled for cluster <b>{thisJob.cluster}:{thisJob.subCluster}</b>.</p>
<p class="mb-1">To remove this card, open metric selection and press "Close and Apply".</p>
</CardBody>
</Card>
{:else} {:else}
<Card color="warning" class="mt-2"> <Card color="warning" class="mt-2">
<CardHeader class="mb-0"> <CardHeader class="mb-0">

View File

@@ -253,12 +253,15 @@
forNode forNode
/> />
{:else if item.disabled === true && item.metric} {:else if item.disabled === true && item.metric}
<Card style="margin-left: 2rem;margin-right: 2rem;" body color="info" <Card color="info" class="mx-2">
>Metric disabled for subcluster <code <CardHeader class="mb-0">
>{item.name}:{$nodeMetricsData.data.nodeMetrics[0] <b>Disabled Metric</b>
.subCluster}</code </CardHeader>
></Card <CardBody>
> <p>No dataset(s) returned for <b>{item.name}</b></p>
<p class="mb-1">Metric has been disabled for subcluster <b>{$nodeMetricsData.data.nodeMetrics[0].subCluster}</b>.</p>
</CardBody>
</Card>
{:else} {:else}
<Card color="warning" class="mx-2"> <Card color="warning" class="mx-2">
<CardHeader class="mb-0"> <CardHeader class="mb-0">

View File

@@ -99,7 +99,7 @@
}) })
); );
const refinedData = $derived($metricsQuery?.data?.jobMetrics ? sortAndSelectScope($metricsQuery.data.jobMetrics) : []); const refinedData = $derived($metricsQuery?.data?.jobMetrics ? sortAndSelectScope(metrics, $metricsQuery.data.jobMetrics) : []);
/* Effects */ /* Effects */
$effect(() => { $effect(() => {
@@ -140,6 +140,26 @@
}); });
} }
function sortAndSelectScope(metricList = [], jobMetrics = []) {
const pendingData = [];
metricList.forEach((metricName) => {
const pendingMetric = {
name: metricName,
disabled: checkMetricDisabled(
globalMetrics,
metricName,
job.cluster,
job.subCluster,
),
data: null
};
const scopesData = jobMetrics.filter((jobMetric) => jobMetric.name == metricName)
if (scopesData.length > 0) pendingMetric.data = selectScope(scopesData)
pendingData.push(pendingMetric)
});
return pendingData;
};
const selectScope = (jobMetrics) => const selectScope = (jobMetrics) =>
jobMetrics.reduce( jobMetrics.reduce(
(a, b) => (a, b) =>
@@ -152,30 +172,6 @@
: a, : a,
jobMetrics[0], jobMetrics[0],
); );
const sortAndSelectScope = (jobMetrics) =>
metrics
.map((name) => jobMetrics.filter((jobMetric) => jobMetric.name == name))
.map((jobMetrics) => ({
disabled: false,
data: jobMetrics.length > 0 ? selectScope(jobMetrics) : null,
}))
.map((jobMetric) => {
if (jobMetric.data) {
return {
name: jobMetric.data.name,
disabled: checkMetricDisabled(
globalMetrics,
jobMetric.data.name,
job.cluster,
job.subCluster,
),
data: jobMetric.data,
};
} else {
return jobMetric;
}
});
</script> </script>
<tr> <tr>
@@ -211,13 +207,12 @@
{/if} {/if}
{#each refinedData as metric, i (metric?.name || i)} {#each refinedData as metric, i (metric?.name || i)}
<td> <td>
{#key metric}
{#if metric?.data}
{#if metric?.disabled} {#if metric?.disabled}
<Card body class="mx-2" color="info"> <Card body class="mx-2" color="info">
Metric <b>{metric.data.name}</b>: Disabled for subcluster <code>{job.subCluster}</code> <p>No dataset(s) returned for <b>{metrics[i]}</b></p>
<p class="mb-1">Metric has been disabled for subcluster <b>{job.subCluster}</b>.</p>
</Card> </Card>
{:else} {:else if metric?.data}
<MetricPlot <MetricPlot
onZoom={(detail) => handleZoom(detail, metric.data.name)} onZoom={(detail) => handleZoom(detail, metric.data.name)}
height={plotHeight} height={plotHeight}
@@ -234,7 +229,6 @@
zoomState={zoomStates[metric.data.name] || null} zoomState={zoomStates[metric.data.name] || null}
thresholdState={thresholdStates[metric.data.name] || null} thresholdState={thresholdStates[metric.data.name] || null}
/> />
{/if}
{:else} {:else}
<Card body class="mx-2" color="warning"> <Card body class="mx-2" color="warning">
<p>No dataset(s) returned for <b>{metrics[i]}</b></p> <p>No dataset(s) returned for <b>{metrics[i]}</b></p>
@@ -243,7 +237,6 @@
<p class="mb-1">Identical messages in <i>job {job.jobId} row</i>: Host not found.</p> <p class="mb-1">Identical messages in <i>job {job.jobId} row</i>: Host not found.</p>
</Card> </Card>
{/if} {/if}
{/key}
</td> </td>
{:else} {:else}
<td> <td>

View File

@@ -120,7 +120,6 @@
data: h.metrics.filter( data: h.metrics.filter(
(m) => m?.name == selectedMetric && m.scope == "node", (m) => m?.name == selectedMetric && m.scope == "node",
), ),
// TODO: Move To New Func Variant With Disabled Check on WHole Cluster Level: This never Triggers!
disabled: checkMetricDisabled(globalMetrics, selectedMetric, cluster, h.subCluster), disabled: checkMetricDisabled(globalMetrics, selectedMetric, cluster, h.subCluster),
})) }))
.sort((a, b) => a.host.localeCompare(b.host)) .sort((a, b) => a.host.localeCompare(b.host))
@@ -162,16 +161,18 @@
</Badge> </Badge>
</span> </span>
</div> </div>
{#if item?.data} {#if item?.disabled}
{#if item.disabled === true} <Card color="info">
<!-- TODO: Will never be Shown: Overview Single Metric Return Will be Null, see Else Case--> <CardHeader class="mb-0">
<Card body class="mx-3" color="info" <b>Disabled Metric</b>
>Metric disabled for subcluster <code </CardHeader>
>{selectedMetric}:{item.subCluster}</code <CardBody>
></Card <p>No dataset(s) returned for <b>{selectedMetric}</b></p>
> <p class="mb-1">Metric has been disabled for subcluster <b>{item.subCluster}</b>.</p>
{:else if item.disabled === false} </CardBody>
<!-- "No Data"-Warning included in MetricPlot-Component --> </Card>
{:else if item?.data}
<!-- "Empty Series"-Warning included in MetricPlot-Component -->
<!-- #key: X-axis keeps last selected timerange otherwise --> <!-- #key: X-axis keeps last selected timerange otherwise -->
{#key item.data[0].metric.series[0].data.length} {#key item.data[0].metric.series[0].data.length}
<MetricPlot <MetricPlot
@@ -185,12 +186,7 @@
/> />
{/key} {/key}
{:else} {:else}
<Card body class="mx-3" color="info"> <!-- Should Not Appear -->
Global Metric List Not Initialized
Can not determine {selectedMetric} availability: Please Reload Page
</Card>
{/if}
{:else}
<Card color="warning"> <Card color="warning">
<CardHeader class="mb-0"> <CardHeader class="mb-0">
<b>Missing Metric</b> <b>Missing Metric</b>
@@ -205,10 +201,22 @@
{/each} {/each}
{/key} {/key}
</Row> </Row>
{:else if hostnameFilter || hoststateFilter != 'all'}
<Row class="mx-1">
<Card class="px-0">
<CardHeader>
<b>Empty Filter Return</b>
</CardHeader>
<CardBody>
<p>No datasets returned for <b>{selectedMetric}</b>.</p>
<p class="mb-1">Hostname filter and/or host state filter returned no matches.</p>
</CardBody>
</Card>
</Row>
{:else} {:else}
<Row> <Row class="mx-1">
<Card color="warning"> <Card class="px-0" color="warning">
<CardHeader class="mb-0"> <CardHeader>
<b>Missing Metric</b> <b>Missing Metric</b>
</CardHeader> </CardHeader>
<CardBody> <CardBody>

View File

@@ -72,10 +72,30 @@
); );
const extendedLegendData = $derived($nodeJobsData?.data ? buildExtendedLegend() : null); const extendedLegendData = $derived($nodeJobsData?.data ? buildExtendedLegend() : null);
const refinedData = $derived(nodeData?.metrics ? sortAndSelectScope(nodeData.metrics) : []); const refinedData = $derived(nodeData?.metrics ? sortAndSelectScope(selectedMetrics, nodeData.metrics) : []);
const dataHealth = $derived(refinedData.filter((rd) => rd.disabled === false).map((enabled) => (enabled?.data?.metric?.series?.length > 0))); const dataHealth = $derived(refinedData.filter((rd) => rd.disabled === false).map((enabled) => (enabled?.data?.metric?.series?.length > 0)));
/* Functions */ /* Functions */
function sortAndSelectScope(metricList = [], nodeMetrics = []) {
const pendingData = [];
metricList.forEach((metricName) => {
const pendingMetric = {
name: metricName,
disabled: checkMetricDisabled(
globalMetrics,
metricName,
cluster,
nodeData.subCluster,
),
data: null
};
const scopesData = nodeMetrics.filter((nodeMetric) => nodeMetric.name == metricName)
if (scopesData.length > 0) pendingMetric.data = selectScope(scopesData)
pendingData.push(pendingMetric)
});
return pendingData;
};
const selectScope = (nodeMetrics) => const selectScope = (nodeMetrics) =>
nodeMetrics.reduce( nodeMetrics.reduce(
(a, b) => (a, b) =>
@@ -83,29 +103,6 @@
nodeMetrics[0], nodeMetrics[0],
); );
const sortAndSelectScope = (allNodeMetrics) =>
selectedMetrics
.map((selectedName) => allNodeMetrics.filter((nodeMetric) => nodeMetric.name == selectedName))
.map((matchedNodeMetrics) => ({
disabled: false,
data: matchedNodeMetrics.length > 0 ? selectScope(matchedNodeMetrics) : null,
}))
.map((scopedNodeMetric) => {
if (scopedNodeMetric?.data) {
return {
disabled: checkMetricDisabled(
globalMetrics,
scopedNodeMetric.data.name,
cluster,
nodeData.subCluster,
),
data: scopedNodeMetric.data,
};
} else {
return scopedNodeMetric;
}
});
function buildExtendedLegend() { function buildExtendedLegend() {
let pendingExtendedLegendData = null let pendingExtendedLegendData = null
// Build Extended for allocated nodes [Commented: Only Build extended Legend For Shared Nodes] // Build Extended for allocated nodes [Commented: Only Build extended Legend For Shared Nodes]
@@ -171,25 +168,17 @@
{/if} {/if}
</td> </td>
{#each refinedData as metricData, i (metricData?.data?.name || i)} {#each refinedData as metricData, i (metricData?.data?.name || i)}
{#key metricData}
<td> <td>
{#if metricData?.disabled} {#if metricData?.disabled}
<Card body class="mx-2" color="info" <Card body class="mx-2" color="info">
>Metric <b>{selectedMetrics[i]}</b> disabled for subcluster <code <p>No dataset(s) returned for <b>{selectedMetrics[i]}</b></p>
>{nodeData.subCluster}</code <p class="mb-1">Metric has been disabled for subcluster <b>{nodeData.subCluster}</b>.</p>
></Card </Card>
>
{:else if !metricData?.data} {:else if !metricData?.data}
<Card body class="mx-2" color="warning"> <Card body class="mx-2" color="warning">
<p>No dataset(s) returned for <b>{selectedMetrics[i]}</b></p> <p>No dataset(s) returned for <b>{selectedMetrics[i]}</b></p>
<p class="mb-1">Metric was not found in metric store for cluster <b>{cluster}</b>.</p> <p class="mb-1">Metric was not found in metric store for cluster <b>{cluster}</b>.</p>
</Card> </Card>
{:else if !metricData?.data?.name}
<Card body class="mx-2" color="warning"
>Metric without name for subcluster <code
>{`Metric Index ${i}`}:{nodeData.subCluster}</code
></Card
>
{:else if !!metricData.data?.metric.statisticsSeries} {:else if !!metricData.data?.metric.statisticsSeries}
<!-- "No Data"-Warning included in MetricPlot-Component --> <!-- "No Data"-Warning included in MetricPlot-Component -->
<MetricPlot <MetricPlot
@@ -233,6 +222,5 @@
/> />
{/if} {/if}
</td> </td>
{/key}
{/each} {/each}
</tr> </tr>