mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2026-02-11 21:41:46 +01:00
review handling of disabled metrics in frontend
This commit is contained in:
@@ -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">
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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,39 +207,36 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#each refinedData as metric, i (metric?.name || i)}
|
{#each refinedData as metric, i (metric?.name || i)}
|
||||||
<td>
|
<td>
|
||||||
{#key metric}
|
{#if metric?.disabled}
|
||||||
{#if metric?.data}
|
<Card body class="mx-2" color="info">
|
||||||
{#if metric?.disabled}
|
<p>No dataset(s) returned for <b>{metrics[i]}</b></p>
|
||||||
<Card body class="mx-2" color="info">
|
<p class="mb-1">Metric has been disabled for subcluster <b>{job.subCluster}</b>.</p>
|
||||||
Metric <b>{metric.data.name}</b>: Disabled for subcluster <code>{job.subCluster}</code>
|
</Card>
|
||||||
</Card>
|
{:else if metric?.data}
|
||||||
{:else}
|
<MetricPlot
|
||||||
<MetricPlot
|
onZoom={(detail) => handleZoom(detail, metric.data.name)}
|
||||||
onZoom={(detail) => handleZoom(detail, metric.data.name)}
|
height={plotHeight}
|
||||||
height={plotHeight}
|
timestep={metric.data.metric.timestep}
|
||||||
timestep={metric.data.metric.timestep}
|
scope={metric.data.scope}
|
||||||
scope={metric.data.scope}
|
series={metric.data.metric.series}
|
||||||
series={metric.data.metric.series}
|
statisticsSeries={metric.data.metric.statisticsSeries}
|
||||||
statisticsSeries={metric.data.metric.statisticsSeries}
|
metric={metric.data.name}
|
||||||
metric={metric.data.name}
|
cluster={clusterInfos.find((c) => c.name == job.cluster)}
|
||||||
cluster={clusterInfos.find((c) => c.name == job.cluster)}
|
subCluster={job.subCluster}
|
||||||
subCluster={job.subCluster}
|
isShared={job.shared != "none"}
|
||||||
isShared={job.shared != "none"}
|
numhwthreads={job.numHWThreads}
|
||||||
numhwthreads={job.numHWThreads}
|
numaccs={job.numAcc}
|
||||||
numaccs={job.numAcc}
|
zoomState={zoomStates[metric.data.name] || null}
|
||||||
zoomState={zoomStates[metric.data.name] || null}
|
thresholdState={thresholdStates[metric.data.name] || null}
|
||||||
thresholdState={thresholdStates[metric.data.name] || null}
|
/>
|
||||||
/>
|
{:else}
|
||||||
{/if}
|
<Card body class="mx-2" color="warning">
|
||||||
{:else}
|
<p>No dataset(s) returned for <b>{metrics[i]}</b></p>
|
||||||
<Card body class="mx-2" color="warning">
|
<p class="mb-1">Metric or host was not found in metric store for cluster <b>{job.cluster}</b>:</p>
|
||||||
<p>No dataset(s) returned for <b>{metrics[i]}</b></p>
|
<p class="mb-1">Identical messages in <i>{metrics[i]} column</i>: Metric not found.</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>job {job.jobId} row</i>: Host not found.</p>
|
||||||
<p class="mb-1">Identical messages in <i>{metrics[i]} column</i>: Metric not found.</p>
|
</Card>
|
||||||
<p class="mb-1">Identical messages in <i>job {job.jobId} row</i>: Host not found.</p>
|
{/if}
|
||||||
</Card>
|
|
||||||
{/if}
|
|
||||||
{/key}
|
|
||||||
</td>
|
</td>
|
||||||
{:else}
|
{:else}
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@@ -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,35 +161,32 @@
|
|||||||
</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>
|
||||||
<!-- #key: X-axis keeps last selected timerange otherwise -->
|
{:else if item?.data}
|
||||||
{#key item.data[0].metric.series[0].data.length}
|
<!-- "Empty Series"-Warning included in MetricPlot-Component -->
|
||||||
<MetricPlot
|
<!-- #key: X-axis keeps last selected timerange otherwise -->
|
||||||
timestep={item.data[0].metric.timestep}
|
{#key item.data[0].metric.series[0].data.length}
|
||||||
series={item.data[0].metric.series}
|
<MetricPlot
|
||||||
metric={item.data[0].name}
|
timestep={item.data[0].metric.timestep}
|
||||||
{cluster}
|
series={item.data[0].metric.series}
|
||||||
subCluster={item.subCluster}
|
metric={item.data[0].name}
|
||||||
forNode
|
{cluster}
|
||||||
enableFlip
|
subCluster={item.subCluster}
|
||||||
/>
|
forNode
|
||||||
{/key}
|
enableFlip
|
||||||
{:else}
|
/>
|
||||||
<Card body class="mx-3" color="info">
|
{/key}
|
||||||
Global Metric List Not Initialized
|
|
||||||
Can not determine {selectedMetric} availability: Please Reload Page
|
|
||||||
</Card>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
{:else}
|
||||||
|
<!-- Should Not Appear -->
|
||||||
<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>
|
||||||
|
|||||||
@@ -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,68 +168,59 @@
|
|||||||
{/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"
|
<p>No dataset(s) returned for <b>{selectedMetrics[i]}</b></p>
|
||||||
>Metric <b>{selectedMetrics[i]}</b> disabled for subcluster <code
|
<p class="mb-1">Metric has been disabled for subcluster <b>{nodeData.subCluster}</b>.</p>
|
||||||
>{nodeData.subCluster}</code
|
</Card>
|
||||||
></Card
|
{:else if !metricData?.data}
|
||||||
>
|
<Card body class="mx-2" color="warning">
|
||||||
{:else if !metricData?.data}
|
<p>No dataset(s) returned for <b>{selectedMetrics[i]}</b></p>
|
||||||
<Card body class="mx-2" color="warning">
|
<p class="mb-1">Metric was not found in metric store for cluster <b>{cluster}</b>.</p>
|
||||||
<p>No dataset(s) returned for <b>{selectedMetrics[i]}</b></p>
|
</Card>
|
||||||
<p class="mb-1">Metric was not found in metric store for cluster <b>{cluster}</b>.</p>
|
{:else if !!metricData.data?.metric.statisticsSeries}
|
||||||
</Card>
|
<!-- "No Data"-Warning included in MetricPlot-Component -->
|
||||||
{:else if !metricData?.data?.name}
|
<MetricPlot
|
||||||
<Card body class="mx-2" color="warning"
|
{cluster}
|
||||||
>Metric without name for subcluster <code
|
subCluster={nodeData.subCluster}
|
||||||
>{`Metric Index ${i}`}:{nodeData.subCluster}</code
|
metric={metricData.data.name}
|
||||||
></Card
|
scope={metricData.data.scope}
|
||||||
>
|
timestep={metricData.data.metric.timestep}
|
||||||
{:else if !!metricData.data?.metric.statisticsSeries}
|
series={metricData.data.metric.series}
|
||||||
<!-- "No Data"-Warning included in MetricPlot-Component -->
|
statisticsSeries={metricData.data?.metric.statisticsSeries}
|
||||||
<MetricPlot
|
useStatsSeries={!!metricData.data?.metric.statisticsSeries}
|
||||||
{cluster}
|
height={175}
|
||||||
subCluster={nodeData.subCluster}
|
{plotSync}
|
||||||
metric={metricData.data.name}
|
forNode
|
||||||
scope={metricData.data.scope}
|
/>
|
||||||
timestep={metricData.data.metric.timestep}
|
<div class="my-2"></div>
|
||||||
series={metricData.data.metric.series}
|
{#key extendedLegendData}
|
||||||
statisticsSeries={metricData.data?.metric.statisticsSeries}
|
<MetricPlot
|
||||||
useStatsSeries={!!metricData.data?.metric.statisticsSeries}
|
{cluster}
|
||||||
height={175}
|
subCluster={nodeData.subCluster}
|
||||||
{plotSync}
|
metric={metricData.data.name}
|
||||||
forNode
|
scope={metricData.data.scope}
|
||||||
/>
|
timestep={metricData.data.metric.timestep}
|
||||||
<div class="my-2"></div>
|
series={metricData.data.metric.series}
|
||||||
{#key extendedLegendData}
|
height={175}
|
||||||
<MetricPlot
|
{extendedLegendData}
|
||||||
{cluster}
|
{plotSync}
|
||||||
subCluster={nodeData.subCluster}
|
forNode
|
||||||
metric={metricData.data.name}
|
/>
|
||||||
scope={metricData.data.scope}
|
{/key}
|
||||||
timestep={metricData.data.metric.timestep}
|
{:else}
|
||||||
series={metricData.data.metric.series}
|
<MetricPlot
|
||||||
height={175}
|
{cluster}
|
||||||
{extendedLegendData}
|
subCluster={nodeData.subCluster}
|
||||||
{plotSync}
|
metric={metricData.data.name}
|
||||||
forNode
|
scope={metricData.data.scope}
|
||||||
/>
|
timestep={metricData.data.metric.timestep}
|
||||||
{/key}
|
series={metricData.data.metric.series}
|
||||||
{:else}
|
height={375}
|
||||||
<MetricPlot
|
forNode
|
||||||
{cluster}
|
/>
|
||||||
subCluster={nodeData.subCluster}
|
{/if}
|
||||||
metric={metricData.data.name}
|
</td>
|
||||||
scope={metricData.data.scope}
|
|
||||||
timestep={metricData.data.metric.timestep}
|
|
||||||
series={metricData.data.metric.series}
|
|
||||||
height={375}
|
|
||||||
forNode
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/key}
|
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user