mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2026-02-11 13:31:45 +01:00
add not configured info cards, show short job filter options if one active filter
This commit is contained in:
2
go.sum
2
go.sum
@@ -4,8 +4,6 @@ github.com/99designs/gqlgen v0.17.85 h1:EkGx3U2FDcxQm8YDLQSpXIAVmpDyZ3IcBMOJi2nH
|
||||
github.com/99designs/gqlgen v0.17.85/go.mod h1:yvs8s0bkQlRfqg03YXr3eR4OQUowVhODT/tHzCXnbOU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/ClusterCockpit/cc-lib/v2 v2.2.1 h1:iCVas+Jc61zFH5S2VG3H1sc7tsn+U4lOJwUYjYZEims=
|
||||
github.com/ClusterCockpit/cc-lib/v2 v2.2.1/go.mod h1:JuxMAuEOaLLNEnnL9U3ejha8kMvsSatLdKPZEgJw6iw=
|
||||
github.com/ClusterCockpit/cc-lib/v2 v2.2.2 h1:ye4RY57I19c2cXr3XWZBS/QYYgQVeGFvsiu5HkyKq9E=
|
||||
github.com/ClusterCockpit/cc-lib/v2 v2.2.2/go.mod h1:JuxMAuEOaLLNEnnL9U3ejha8kMvsSatLdKPZEgJw6iw=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
import {
|
||||
init,
|
||||
groupByScope,
|
||||
checkMetricDisabled,
|
||||
checkMetricAvailability,
|
||||
} from "./generic/utils.js";
|
||||
import Metric from "./job/Metric.svelte";
|
||||
import MetricSelection from "./generic/select/MetricSelection.svelte";
|
||||
@@ -151,17 +151,17 @@
|
||||
}
|
||||
return names;
|
||||
}, []);
|
||||
|
||||
//
|
||||
return metricNames.filter(
|
||||
(metric) =>
|
||||
!metrics.some((jm) => jm.name == metric) &&
|
||||
selectedMetrics.includes(metric) &&
|
||||
!checkMetricDisabled(
|
||||
(checkMetricAvailability(
|
||||
globalMetrics,
|
||||
metric,
|
||||
thisJob.cluster,
|
||||
thisJob.subCluster,
|
||||
),
|
||||
) == "configured")
|
||||
);
|
||||
} else {
|
||||
return []
|
||||
@@ -212,7 +212,7 @@
|
||||
inputMetrics.map((metric) => ({
|
||||
metric: metric,
|
||||
data: grouped.find((group) => group[0].name == metric),
|
||||
disabled: checkMetricDisabled(
|
||||
availability: checkMetricAvailability(
|
||||
globalMetrics,
|
||||
metric,
|
||||
thisJob.cluster,
|
||||
@@ -333,7 +333,17 @@
|
||||
{:else if thisJob && $jobMetrics?.data?.scopedJobStats}
|
||||
<!-- Note: Ignore '#snippet' Error in IDE -->
|
||||
{#snippet gridContent(item)}
|
||||
{#if item?.disabled}
|
||||
{#if item.availability == "none"}
|
||||
<Card color="light" class="mt-2">
|
||||
<CardHeader class="mb-0">
|
||||
<b>Metric not configured</b>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p>No datasets returned for <b>{item.metric}</b>.</p>
|
||||
<p class="mb-1">Metric is not configured for cluster <b>{thisJob.cluster}</b>.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
{:else if item.availability == "disabled"}
|
||||
<Card color="info" class="mt-2">
|
||||
<CardHeader class="mb-0">
|
||||
<b>Disabled Metric</b>
|
||||
|
||||
@@ -142,7 +142,8 @@
|
||||
<Filters
|
||||
bind:this={filterComponent}
|
||||
{filterPresets}
|
||||
shortJobQuickSelect
|
||||
startTimeQuickSelect
|
||||
shortJobQuickSelect={(filterBuffer.length > 0)}
|
||||
shortJobCutoff={ccconfig?.jobList_hideShortRunningJobs}
|
||||
showFilter={!showCompare}
|
||||
matchedJobs={showCompare? matchedCompareJobs: matchedListJobs}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
} from "@urql/svelte";
|
||||
import {
|
||||
init,
|
||||
checkMetricDisabled,
|
||||
checkMetricAvailability,
|
||||
} from "./generic/utils.js";
|
||||
import PlotGrid from "./generic/PlotGrid.svelte";
|
||||
import MetricPlot from "./generic/plots/MetricPlot.svelte";
|
||||
@@ -242,17 +242,17 @@
|
||||
{item.name}
|
||||
{systemUnits[item.name] ? "(" + systemUnits[item.name] + ")" : ""}
|
||||
</h4>
|
||||
{#if item.disabled === false && item.metric}
|
||||
<MetricPlot
|
||||
metric={item.name}
|
||||
timestep={item.metric.timestep}
|
||||
cluster={clusterInfos.find((c) => c.name == cluster)}
|
||||
subCluster={$nodeMetricsData.data.nodeMetrics[0].subCluster}
|
||||
series={item.metric.series}
|
||||
enableFlip
|
||||
forNode
|
||||
/>
|
||||
{:else if item.disabled === true && item.metric}
|
||||
{#if item.availability == "none"}
|
||||
<Card color="light" class="mx-2">
|
||||
<CardHeader class="mb-0">
|
||||
<b>Metric not configured</b>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p>No datasets returned for <b>{item.name}</b>.</p>
|
||||
<p class="mb-1">Metric is not configured for cluster <b>{cluster}</b>.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
{:else if item.availability == "disabled"}
|
||||
<Card color="info" class="mx-2">
|
||||
<CardHeader class="mb-0">
|
||||
<b>Disabled Metric</b>
|
||||
@@ -262,6 +262,16 @@
|
||||
<p class="mb-1">Metric has been disabled for subcluster <b>{$nodeMetricsData.data.nodeMetrics[0].subCluster}</b>.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
{:else if item?.metric}
|
||||
<MetricPlot
|
||||
metric={item.name}
|
||||
timestep={item.metric.timestep}
|
||||
cluster={clusterInfos.find((c) => c.name == cluster)}
|
||||
subCluster={$nodeMetricsData.data.nodeMetrics[0].subCluster}
|
||||
series={item.metric.series}
|
||||
enableFlip
|
||||
forNode
|
||||
/>
|
||||
{:else}
|
||||
<Card color="warning" class="mx-2">
|
||||
<CardHeader class="mb-0">
|
||||
@@ -279,7 +289,7 @@
|
||||
items={$nodeMetricsData.data.nodeMetrics[0].metrics
|
||||
.map((m) => ({
|
||||
...m,
|
||||
disabled: checkMetricDisabled(
|
||||
availability: checkMetricAvailability(
|
||||
globalMetrics,
|
||||
m.name,
|
||||
cluster,
|
||||
|
||||
@@ -219,7 +219,8 @@
|
||||
<Filters
|
||||
bind:this={filterComponent}
|
||||
{filterPresets}
|
||||
shortJobQuickSelect
|
||||
startTimeQuickSelect
|
||||
shortJobQuickSelect={(filterBuffer.length > 0)}
|
||||
shortJobCutoff={ccconfig?.jobList_hideShortRunningJobs}
|
||||
showFilter={!showCompare}
|
||||
matchedJobs={showCompare? matchedCompareJobs: matchedListJobs}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<script>
|
||||
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
||||
import { Card, Spinner } from "@sveltestrap/sveltestrap";
|
||||
import { maxScope, checkMetricDisabled } from "../utils.js";
|
||||
import { maxScope, checkMetricAvailability } from "../utils.js";
|
||||
import JobInfo from "./JobInfo.svelte";
|
||||
import MetricPlot from "../plots/MetricPlot.svelte";
|
||||
import JobFootprint from "../helper/JobFootprint.svelte";
|
||||
@@ -145,7 +145,7 @@
|
||||
metricList.forEach((metricName) => {
|
||||
const pendingMetric = {
|
||||
name: metricName,
|
||||
disabled: checkMetricDisabled(
|
||||
availability: checkMetricAvailability(
|
||||
globalMetrics,
|
||||
metricName,
|
||||
job.cluster,
|
||||
@@ -207,7 +207,12 @@
|
||||
{/if}
|
||||
{#each refinedData as metric, i (metric?.name || i)}
|
||||
<td>
|
||||
{#if metric?.disabled}
|
||||
{#if metric?.availability == "none"}
|
||||
<Card body class="mx-2" color="light">
|
||||
<p>No dataset(s) returned for <b>{metrics[i]}</b></p>
|
||||
<p class="mb-1">Metric is not configured for cluster <b>{job.cluster}</b>.</p>
|
||||
</Card>
|
||||
{:else if metric?.availability == "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>
|
||||
|
||||
@@ -302,20 +302,36 @@ export function stickyHeader(datatableHeaderSelector, updatePading) {
|
||||
onDestroy(() => document.removeEventListener("scroll", onscroll));
|
||||
}
|
||||
|
||||
export function checkMetricDisabled(gm, m, c, s) { // [g]lobal[m]etrics, [m]etric, [c]luster, [s]ubcluster
|
||||
const available = gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s)
|
||||
// Return inverse logic
|
||||
return !available
|
||||
export function checkMetricAvailability(gms, m, c, s = "") { // [g]lobal[m]etrics, [m]etric, [c]luster, [s]ubcluster
|
||||
let pendingAvailability = "none"
|
||||
const configured = gms?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)
|
||||
if (configured) {
|
||||
pendingAvailability = "configured"
|
||||
if (s != "") {
|
||||
const enabled = configured.subClusters?.includes(s)
|
||||
// Test inverse logic
|
||||
if (!enabled) {
|
||||
pendingAvailability = "disabled"
|
||||
}
|
||||
}
|
||||
}
|
||||
return pendingAvailability;
|
||||
}
|
||||
|
||||
export function checkMetricsDisabled(gm, ma, c, s) { // [g]lobal[m]etrics, [m]etric[a]rray, [c]luster, [s]ubcluster
|
||||
let result = {};
|
||||
ma.forEach((m) => {
|
||||
// Return named inverse logic: !available
|
||||
result[m] = !(gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s))
|
||||
});
|
||||
return result
|
||||
}
|
||||
// export function checkMetricDisabled(gm, m, c, s) { // [g]lobal[m]etrics, [m]etric, [c]luster, [s]ubcluster
|
||||
// const available = gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s)
|
||||
// // Return inverse logic
|
||||
// return !available
|
||||
// }
|
||||
|
||||
// export function checkMetricsDisabled(gm, ma, c, s) { // [g]lobal[m]etrics, [m]etric[a]rray, [c]luster, [s]ubcluster
|
||||
// let aresult = {};
|
||||
// ma.forEach((m) => {
|
||||
// // Return named inverse logic: !available
|
||||
// aresult[m] = !(gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s))
|
||||
// });
|
||||
// return aresult
|
||||
// }
|
||||
|
||||
export function getStatsItems(presetStats = []) {
|
||||
// console.time('stats')
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script>
|
||||
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
||||
import { Row, Col, Card, CardHeader, CardBody, Spinner, Badge } from "@sveltestrap/sveltestrap";
|
||||
import { checkMetricDisabled } from "../generic/utils.js";
|
||||
import { checkMetricAvailability } from "../generic/utils.js";
|
||||
import MetricPlot from "../generic/plots/MetricPlot.svelte";
|
||||
|
||||
/* Svelte 5 Props */
|
||||
@@ -87,6 +87,7 @@
|
||||
},
|
||||
}));
|
||||
|
||||
const notConfigured = $derived(checkMetricAvailability(globalMetrics, selectedMetric, cluster) == "none");
|
||||
const mappedData = $derived(handleQueryData($nodesQuery?.data));
|
||||
const filteredData = $derived(mappedData.filter((h) => {
|
||||
if (hostnameFilter) {
|
||||
@@ -120,7 +121,7 @@
|
||||
data: h.metrics.filter(
|
||||
(m) => m?.name == selectedMetric && m.scope == "node",
|
||||
),
|
||||
disabled: checkMetricDisabled(globalMetrics, selectedMetric, cluster, h.subCluster),
|
||||
availability: checkMetricAvailability(globalMetrics, selectedMetric, cluster, h.subCluster),
|
||||
}))
|
||||
.sort((a, b) => a.host.localeCompare(b.host))
|
||||
}
|
||||
@@ -161,7 +162,7 @@
|
||||
</Badge>
|
||||
</span>
|
||||
</div>
|
||||
{#if item?.disabled}
|
||||
{#if item?.availability == "disabled"}
|
||||
<Card color="info">
|
||||
<CardHeader class="mb-0">
|
||||
<b>Disabled Metric</b>
|
||||
@@ -213,6 +214,18 @@
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Row>
|
||||
{:else if notConfigured}
|
||||
<Row class="mx-1">
|
||||
<Card class="px-0" color="light">
|
||||
<CardHeader>
|
||||
<b>Metric not configured</b>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p>No datasets returned for <b>{selectedMetric}</b>.</p>
|
||||
<p class="mb-1">Metric is not configured for cluster <b>{cluster}</b>.</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Row>
|
||||
{:else}
|
||||
<Row class="mx-1">
|
||||
<Card class="px-0" color="warning">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
} from "@urql/svelte";
|
||||
import uPlot from "uplot";
|
||||
import { Card, CardBody, Spinner } from "@sveltestrap/sveltestrap";
|
||||
import { maxScope, checkMetricDisabled, scramble, scrambleNames } from "../../generic/utils.js";
|
||||
import { maxScope, checkMetricAvailability, scramble, scrambleNames } from "../../generic/utils.js";
|
||||
import MetricPlot from "../../generic/plots/MetricPlot.svelte";
|
||||
import NodeInfo from "./NodeInfo.svelte";
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
const extendedLegendData = $derived($nodeJobsData?.data ? buildExtendedLegend() : null);
|
||||
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.availability == "configured").map((enabled) => (enabled?.data?.metric?.series?.length > 0)));
|
||||
|
||||
/* Functions */
|
||||
function sortAndSelectScope(metricList = [], nodeMetrics = []) {
|
||||
@@ -81,7 +81,7 @@
|
||||
metricList.forEach((metricName) => {
|
||||
const pendingMetric = {
|
||||
name: metricName,
|
||||
disabled: checkMetricDisabled(
|
||||
availability: checkMetricAvailability(
|
||||
globalMetrics,
|
||||
metricName,
|
||||
cluster,
|
||||
@@ -130,23 +130,6 @@
|
||||
return pendingExtendedLegendData;
|
||||
}
|
||||
|
||||
/* Inspect */
|
||||
// $inspect(selectedMetrics).with((type, selectedMetrics) => {
|
||||
// console.log(type, 'selectedMetrics', selectedMetrics)
|
||||
// });
|
||||
|
||||
// $inspect(nodeData).with((type, nodeData) => {
|
||||
// console.log(type, 'nodeData', nodeData)
|
||||
// });
|
||||
|
||||
// $inspect(refinedData).with((type, refinedData) => {
|
||||
// console.log(type, 'refinedData', refinedData)
|
||||
// });
|
||||
|
||||
// $inspect(dataHealth).with((type, dataHealth) => {
|
||||
// console.log(type, 'dataHealth', dataHealth)
|
||||
// });
|
||||
|
||||
</script>
|
||||
|
||||
<tr>
|
||||
@@ -169,7 +152,12 @@
|
||||
</td>
|
||||
{#each refinedData as metricData, i (metricData?.data?.name || i)}
|
||||
<td>
|
||||
{#if metricData?.disabled}
|
||||
{#if metricData?.availability == "none"}
|
||||
<Card body class="mx-2" color="light">
|
||||
<p>No dataset(s) returned for <b>{selectedMetrics[i]}</b></p>
|
||||
<p class="mb-1">Metric is not configured for cluster <b>{cluster}</b>.</p>
|
||||
</Card>
|
||||
{:else if metricData?.availability == "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>
|
||||
@@ -177,7 +165,7 @@
|
||||
{: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>
|
||||
<p class="mb-1">Metric or host 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 -->
|
||||
|
||||
Reference in New Issue
Block a user