mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2026-02-12 05:51:45 +01:00
review handling of disabled metrics in frontend
This commit is contained in:
@@ -333,7 +333,18 @@
|
||||
{:else if thisJob && $jobMetrics?.data?.scopedJobStats}
|
||||
<!-- Note: Ignore '#snippet' Error in IDE -->
|
||||
{#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
|
||||
bind:this={plots[item.metric]}
|
||||
job={thisJob}
|
||||
@@ -343,16 +354,6 @@
|
||||
presetScopes={item.data.map((x) => x.scope)}
|
||||
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}
|
||||
<Card color="warning" class="mt-2">
|
||||
<CardHeader class="mb-0">
|
||||
|
||||
@@ -253,12 +253,15 @@
|
||||
forNode
|
||||
/>
|
||||
{:else if item.disabled === true && item.metric}
|
||||
<Card style="margin-left: 2rem;margin-right: 2rem;" body color="info"
|
||||
>Metric disabled for subcluster <code
|
||||
>{item.name}:{$nodeMetricsData.data.nodeMetrics[0]
|
||||
.subCluster}</code
|
||||
></Card
|
||||
>
|
||||
<Card color="info" class="mx-2">
|
||||
<CardHeader class="mb-0">
|
||||
<b>Disabled Metric</b>
|
||||
</CardHeader>
|
||||
<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}
|
||||
<Card color="warning" class="mx-2">
|
||||
<CardHeader class="mb-0">
|
||||
|
||||
@@ -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 */
|
||||
$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) =>
|
||||
jobMetrics.reduce(
|
||||
(a, b) =>
|
||||
@@ -152,30 +172,6 @@
|
||||
: a,
|
||||
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>
|
||||
|
||||
<tr>
|
||||
@@ -211,39 +207,36 @@
|
||||
{/if}
|
||||
{#each refinedData as metric, i (metric?.name || i)}
|
||||
<td>
|
||||
{#key metric}
|
||||
{#if metric?.data}
|
||||
{#if metric?.disabled}
|
||||
<Card body class="mx-2" color="info">
|
||||
Metric <b>{metric.data.name}</b>: Disabled for subcluster <code>{job.subCluster}</code>
|
||||
</Card>
|
||||
{:else}
|
||||
<MetricPlot
|
||||
onZoom={(detail) => handleZoom(detail, metric.data.name)}
|
||||
height={plotHeight}
|
||||
timestep={metric.data.metric.timestep}
|
||||
scope={metric.data.scope}
|
||||
series={metric.data.metric.series}
|
||||
statisticsSeries={metric.data.metric.statisticsSeries}
|
||||
metric={metric.data.name}
|
||||
cluster={clusterInfos.find((c) => c.name == job.cluster)}
|
||||
subCluster={job.subCluster}
|
||||
isShared={job.shared != "none"}
|
||||
numhwthreads={job.numHWThreads}
|
||||
numaccs={job.numAcc}
|
||||
zoomState={zoomStates[metric.data.name] || null}
|
||||
thresholdState={thresholdStates[metric.data.name] || null}
|
||||
/>
|
||||
{/if}
|
||||
{:else}
|
||||
<Card body class="mx-2" color="warning">
|
||||
<p>No dataset(s) returned for <b>{metrics[i]}</b></p>
|
||||
<p class="mb-1">Metric or host was not found in metric store for cluster <b>{job.cluster}</b>:</p>
|
||||
<p class="mb-1">Identical messages in <i>{metrics[i]} column</i>: Metric not found.</p>
|
||||
<p class="mb-1">Identical messages in <i>job {job.jobId} row</i>: Host not found.</p>
|
||||
</Card>
|
||||
{/if}
|
||||
{/key}
|
||||
{#if metric?.disabled}
|
||||
<Card body class="mx-2" color="info">
|
||||
<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>
|
||||
{:else if metric?.data}
|
||||
<MetricPlot
|
||||
onZoom={(detail) => handleZoom(detail, metric.data.name)}
|
||||
height={plotHeight}
|
||||
timestep={metric.data.metric.timestep}
|
||||
scope={metric.data.scope}
|
||||
series={metric.data.metric.series}
|
||||
statisticsSeries={metric.data.metric.statisticsSeries}
|
||||
metric={metric.data.name}
|
||||
cluster={clusterInfos.find((c) => c.name == job.cluster)}
|
||||
subCluster={job.subCluster}
|
||||
isShared={job.shared != "none"}
|
||||
numhwthreads={job.numHWThreads}
|
||||
numaccs={job.numAcc}
|
||||
zoomState={zoomStates[metric.data.name] || null}
|
||||
thresholdState={thresholdStates[metric.data.name] || null}
|
||||
/>
|
||||
{:else}
|
||||
<Card body class="mx-2" color="warning">
|
||||
<p>No dataset(s) returned for <b>{metrics[i]}</b></p>
|
||||
<p class="mb-1">Metric or host was not found in metric store for cluster <b>{job.cluster}</b>:</p>
|
||||
<p class="mb-1">Identical messages in <i>{metrics[i]} column</i>: Metric not found.</p>
|
||||
<p class="mb-1">Identical messages in <i>job {job.jobId} row</i>: Host not found.</p>
|
||||
</Card>
|
||||
{/if}
|
||||
</td>
|
||||
{:else}
|
||||
<td>
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
let pendingMapped = [];
|
||||
if (rawData.length > 0) {
|
||||
pendingMapped = rawData.map((h) => ({
|
||||
@@ -120,12 +120,11 @@
|
||||
data: h.metrics.filter(
|
||||
(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),
|
||||
}))
|
||||
.sort((a, b) => a.host.localeCompare(b.host))
|
||||
}
|
||||
|
||||
|
||||
return pendingMapped;
|
||||
}
|
||||
</script>
|
||||
@@ -162,35 +161,32 @@
|
||||
</Badge>
|
||||
</span>
|
||||
</div>
|
||||
{#if item?.data}
|
||||
{#if item.disabled === true}
|
||||
<!-- TODO: Will never be Shown: Overview Single Metric Return Will be Null, see Else Case-->
|
||||
<Card body class="mx-3" color="info"
|
||||
>Metric disabled for subcluster <code
|
||||
>{selectedMetric}:{item.subCluster}</code
|
||||
></Card
|
||||
>
|
||||
{:else if item.disabled === false}
|
||||
<!-- "No Data"-Warning included in MetricPlot-Component -->
|
||||
<!-- #key: X-axis keeps last selected timerange otherwise -->
|
||||
{#key item.data[0].metric.series[0].data.length}
|
||||
<MetricPlot
|
||||
timestep={item.data[0].metric.timestep}
|
||||
series={item.data[0].metric.series}
|
||||
metric={item.data[0].name}
|
||||
{cluster}
|
||||
subCluster={item.subCluster}
|
||||
forNode
|
||||
enableFlip
|
||||
/>
|
||||
{/key}
|
||||
{:else}
|
||||
<Card body class="mx-3" color="info">
|
||||
Global Metric List Not Initialized
|
||||
Can not determine {selectedMetric} availability: Please Reload Page
|
||||
</Card>
|
||||
{/if}
|
||||
{#if item?.disabled}
|
||||
<Card color="info">
|
||||
<CardHeader class="mb-0">
|
||||
<b>Disabled Metric</b>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<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>
|
||||
</CardBody>
|
||||
</Card>
|
||||
{:else if item?.data}
|
||||
<!-- "Empty Series"-Warning included in MetricPlot-Component -->
|
||||
<!-- #key: X-axis keeps last selected timerange otherwise -->
|
||||
{#key item.data[0].metric.series[0].data.length}
|
||||
<MetricPlot
|
||||
timestep={item.data[0].metric.timestep}
|
||||
series={item.data[0].metric.series}
|
||||
metric={item.data[0].name}
|
||||
{cluster}
|
||||
subCluster={item.subCluster}
|
||||
forNode
|
||||
enableFlip
|
||||
/>
|
||||
{/key}
|
||||
{:else}
|
||||
<!-- Should Not Appear -->
|
||||
<Card color="warning">
|
||||
<CardHeader class="mb-0">
|
||||
<b>Missing Metric</b>
|
||||
@@ -205,10 +201,22 @@
|
||||
{/each}
|
||||
{/key}
|
||||
</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}
|
||||
<Row>
|
||||
<Card color="warning">
|
||||
<CardHeader class="mb-0">
|
||||
<Row class="mx-1">
|
||||
<Card class="px-0" color="warning">
|
||||
<CardHeader>
|
||||
<b>Missing Metric</b>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
|
||||
@@ -72,10 +72,30 @@
|
||||
);
|
||||
|
||||
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)));
|
||||
|
||||
/* 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) =>
|
||||
nodeMetrics.reduce(
|
||||
(a, b) =>
|
||||
@@ -83,29 +103,6 @@
|
||||
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() {
|
||||
let pendingExtendedLegendData = null
|
||||
// Build Extended for allocated nodes [Commented: Only Build extended Legend For Shared Nodes]
|
||||
@@ -171,68 +168,59 @@
|
||||
{/if}
|
||||
</td>
|
||||
{#each refinedData as metricData, i (metricData?.data?.name || i)}
|
||||
{#key metricData}
|
||||
<td>
|
||||
{#if metricData?.disabled}
|
||||
<Card body class="mx-2" color="info"
|
||||
>Metric <b>{selectedMetrics[i]}</b> disabled for subcluster <code
|
||||
>{nodeData.subCluster}</code
|
||||
></Card
|
||||
>
|
||||
{:else if !metricData?.data}
|
||||
<Card body class="mx-2" color="warning">
|
||||
<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>
|
||||
</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}
|
||||
<!-- "No Data"-Warning included in MetricPlot-Component -->
|
||||
<MetricPlot
|
||||
{cluster}
|
||||
subCluster={nodeData.subCluster}
|
||||
metric={metricData.data.name}
|
||||
scope={metricData.data.scope}
|
||||
timestep={metricData.data.metric.timestep}
|
||||
series={metricData.data.metric.series}
|
||||
statisticsSeries={metricData.data?.metric.statisticsSeries}
|
||||
useStatsSeries={!!metricData.data?.metric.statisticsSeries}
|
||||
height={175}
|
||||
{plotSync}
|
||||
forNode
|
||||
/>
|
||||
<div class="my-2"></div>
|
||||
{#key extendedLegendData}
|
||||
<MetricPlot
|
||||
{cluster}
|
||||
subCluster={nodeData.subCluster}
|
||||
metric={metricData.data.name}
|
||||
scope={metricData.data.scope}
|
||||
timestep={metricData.data.metric.timestep}
|
||||
series={metricData.data.metric.series}
|
||||
height={175}
|
||||
{extendedLegendData}
|
||||
{plotSync}
|
||||
forNode
|
||||
/>
|
||||
{/key}
|
||||
{:else}
|
||||
<MetricPlot
|
||||
{cluster}
|
||||
subCluster={nodeData.subCluster}
|
||||
metric={metricData.data.name}
|
||||
scope={metricData.data.scope}
|
||||
timestep={metricData.data.metric.timestep}
|
||||
series={metricData.data.metric.series}
|
||||
height={375}
|
||||
forNode
|
||||
/>
|
||||
{/if}
|
||||
</td>
|
||||
{/key}
|
||||
<td>
|
||||
{#if metricData?.disabled}
|
||||
<Card body class="mx-2" color="info">
|
||||
<p>No dataset(s) returned for <b>{selectedMetrics[i]}</b></p>
|
||||
<p class="mb-1">Metric has been disabled for subcluster <b>{nodeData.subCluster}</b>.</p>
|
||||
</Card>
|
||||
{:else if !metricData?.data}
|
||||
<Card body class="mx-2" color="warning">
|
||||
<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>
|
||||
</Card>
|
||||
{:else if !!metricData.data?.metric.statisticsSeries}
|
||||
<!-- "No Data"-Warning included in MetricPlot-Component -->
|
||||
<MetricPlot
|
||||
{cluster}
|
||||
subCluster={nodeData.subCluster}
|
||||
metric={metricData.data.name}
|
||||
scope={metricData.data.scope}
|
||||
timestep={metricData.data.metric.timestep}
|
||||
series={metricData.data.metric.series}
|
||||
statisticsSeries={metricData.data?.metric.statisticsSeries}
|
||||
useStatsSeries={!!metricData.data?.metric.statisticsSeries}
|
||||
height={175}
|
||||
{plotSync}
|
||||
forNode
|
||||
/>
|
||||
<div class="my-2"></div>
|
||||
{#key extendedLegendData}
|
||||
<MetricPlot
|
||||
{cluster}
|
||||
subCluster={nodeData.subCluster}
|
||||
metric={metricData.data.name}
|
||||
scope={metricData.data.scope}
|
||||
timestep={metricData.data.metric.timestep}
|
||||
series={metricData.data.metric.series}
|
||||
height={175}
|
||||
{extendedLegendData}
|
||||
{plotSync}
|
||||
forNode
|
||||
/>
|
||||
{/key}
|
||||
{:else}
|
||||
<MetricPlot
|
||||
{cluster}
|
||||
subCluster={nodeData.subCluster}
|
||||
metric={metricData.data.name}
|
||||
scope={metricData.data.scope}
|
||||
timestep={metricData.data.metric.timestep}
|
||||
series={metricData.data.metric.series}
|
||||
height={375}
|
||||
forNode
|
||||
/>
|
||||
{/if}
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user