review status view components, make node states refreshable

This commit is contained in:
Christoph Kluge
2025-11-13 17:27:41 +01:00
parent 9fe342a7e9
commit 3b533938a6
5 changed files with 80 additions and 82 deletions

View File

@@ -9,13 +9,17 @@
import { import {
getContext getContext
} from "svelte" } from "svelte"
import {
init,
} from "./generic/utils.js";
import { import {
Row, Row,
Col, Col,
Card, Card,
CardBody, CardBody,
TabContent, TabContent,
TabPane TabPane,
Spinner
} from "@sveltestrap/sveltestrap"; } from "@sveltestrap/sveltestrap";
import StatusDash from "./status/StatusDash.svelte"; import StatusDash from "./status/StatusDash.svelte";
@@ -28,8 +32,8 @@
} = $props(); } = $props();
/*Const Init */ /*Const Init */
const { query: initq } = init();
const useCbColors = getContext("cc-config")?.plotConfiguration_colorblindMode || false const useCbColors = getContext("cc-config")?.plotConfiguration_colorblindMode || false
</script> </script>
<!-- Loading indicator & Refresh --> <!-- Loading indicator & Refresh -->
@@ -40,11 +44,25 @@
</Col> </Col>
</Row> </Row>
<Card class="overflow-auto" style="height: auto;">
{#if $initq.fetching}
<Row cols={1} class="text-center mt-3">
<Col>
<Spinner />
</Col>
</Row>
{:else if $initq.error}
<Row cols={1} class="text-center mt-3">
<Col>
<Card body color="danger">{$initq.error.message}</Card>
</Col>
</Row>
{:else}
<Card class="overflow-auto" style="height: auto;">
<TabContent> <TabContent>
<TabPane tabId="status-dash" tab="Status" active> <TabPane tabId="status-dash" tab="Status" active>
<CardBody> <CardBody>
<StatusDash {presetCluster} {useCbColors} useAltColors></StatusDash> <StatusDash clusters={$initq.data.clusters} {presetCluster} {useCbColors} useAltColors></StatusDash>
</CardBody> </CardBody>
</TabPane> </TabPane>
@@ -60,4 +78,5 @@
</CardBody> </CardBody>
</TabPane> </TabPane>
</TabContent> </TabContent>
</Card> </Card>
{/if}

View File

@@ -27,7 +27,7 @@
function refreshIntervalChanged() { function refreshIntervalChanged() {
if (refreshIntervalId != null) clearInterval(refreshIntervalId); if (refreshIntervalId != null) clearInterval(refreshIntervalId);
if (refreshInterval == null) return; if (refreshInterval == null) return;
refreshIntervalId = setInterval(() => onRefresh(), refreshInterval); refreshIntervalId = setInterval(() => onRefresh(refreshInterval), refreshInterval);
} }
/* Svelte 5 onMount */ /* Svelte 5 onMount */
@@ -51,7 +51,7 @@
</Input> </Input>
<Button <Button
outline outline
onclick={() => onRefresh()} onclick={() => onRefresh(refreshInterval)}
disabled={refreshInterval != null} disabled={refreshInterval != null}
> >
<Icon name="arrow-clockwise" /> Refresh <Icon name="arrow-clockwise" /> Refresh

View File

@@ -21,7 +21,6 @@
getContextClient, getContextClient,
} from "@urql/svelte"; } from "@urql/svelte";
import { import {
init,
convert2uplot, convert2uplot,
} from "../generic/utils.js"; } from "../generic/utils.js";
import PlotGrid from "../generic/PlotGrid.svelte"; import PlotGrid from "../generic/PlotGrid.svelte";
@@ -35,7 +34,6 @@
} = $props(); } = $props();
/* Const Init */ /* Const Init */
const { query: initq } = init();
const ccconfig = getContext("cc-config"); const ccconfig = getContext("cc-config");
const client = getContextClient(); const client = getContextClient();
@@ -101,25 +99,18 @@
</Row> </Row>
<Row cols={1} class="text-center mt-3"> <Row cols={1} class="text-center mt-3">
{#if $metricStatusQuery.fetching}
<Col> <Col>
{#if $initq.fetching || $metricStatusQuery.fetching}
<Spinner /> <Spinner />
{:else if $initq.error}
<Card body color="danger">{$initq.error.message}</Card>
{:else}
<!-- ... -->
{/if}
</Col> </Col>
</Row> {:else if $metricStatusQuery.error}
{#if $metricStatusQuery.error}
<Row cols={1}>
<Col> <Col>
<Card body color="danger">{$metricStatusQuery.error.message}</Card> <Card body color="danger">{$metricStatusQuery.error.message}</Card>
</Col> </Col>
</Row> {/if}
{/if} </Row>
{#if $initq.data && $metricStatusQuery.data} {#if $metricStatusQuery.data}
<!-- Selectable Stats as Histograms : Average Values of Running Jobs --> <!-- Selectable Stats as Histograms : Average Values of Running Jobs -->
{#if selectedHistograms} {#if selectedHistograms}
<!-- Note: Ignore '#snippet' Error in IDE --> <!-- Note: Ignore '#snippet' Error in IDE -->

View File

@@ -22,9 +22,6 @@
gql, gql,
getContextClient, getContextClient,
} from "@urql/svelte"; } from "@urql/svelte";
import {
init,
} from "../generic/utils.js";
import { formatDurationTime } from "../generic/units.js"; import { formatDurationTime } from "../generic/units.js";
import Refresher from "../generic/helper/Refresher.svelte"; import Refresher from "../generic/helper/Refresher.svelte";
import TimeSelection from "../generic/select/TimeSelection.svelte"; import TimeSelection from "../generic/select/TimeSelection.svelte";
@@ -34,13 +31,13 @@
/* Svelte 5 Props */ /* Svelte 5 Props */
let { let {
clusters,
presetCluster, presetCluster,
useCbColors = false, useCbColors = false,
useAltColors = false, useAltColors = false,
} = $props(); } = $props();
/* Const Init */ /* Const Init */
const { query: initq } = init();
const client = getContextClient(); const client = getContextClient();
/* State Init */ /* State Init */
@@ -67,34 +64,6 @@
let totalAccs = $state({}); let totalAccs = $state({});
/* Derived */ /* Derived */
// Accumulated NodeStates for Piecharts
const nodesStateCounts = $derived(queryStore({
client: client,
query: gql`
query ($filter: [NodeFilter!]) {
nodeStates(filter: $filter) {
state
count
}
}
`,
variables: {
filter: { cluster: { eq: cluster }}
},
}));
const refinedStateData = $derived.by(() => {
return $nodesStateCounts?.data?.nodeStates.
filter((e) => ['allocated', 'reserved', 'idle', 'mixed','down', 'unknown'].includes(e.state)).
sort((a, b) => b.count - a.count)
});
const refinedHealthData = $derived.by(() => {
return $nodesStateCounts?.data?.nodeStates.
filter((e) => ['full', 'partial', 'failed'].includes(e.state)).
sort((a, b) => b.count - a.count)
});
// States for Stacked charts // States for Stacked charts
const statesTimed = $derived(queryStore({ const statesTimed = $derived(queryStore({
client: client, client: client,
@@ -117,6 +86,7 @@
typeNode: "node", typeNode: "node",
typeHealth: "health" typeHealth: "health"
}, },
requestPolicy: "network-only"
})); }));
// Note: nodeMetrics are requested on configured $timestep resolution // Note: nodeMetrics are requested on configured $timestep resolution
@@ -194,6 +164,11 @@
schedulerState schedulerState
} }
} }
# Get Current States fir Pie Charts
nodeStates(filter: $nodeFilter) {
state
count
}
# totalNodes includes multiples if shared jobs # totalNodes includes multiples if shared jobs
jobsStatistics( jobsStatistics(
filter: $jobFilter filter: $jobFilter
@@ -221,10 +196,22 @@
requestPolicy: "network-only" requestPolicy: "network-only"
})); }));
const refinedStateData = $derived.by(() => {
return $statusQuery?.data?.nodeStates.
filter((e) => ['allocated', 'reserved', 'idle', 'mixed','down', 'unknown'].includes(e.state)).
sort((a, b) => b.count - a.count)
});
const refinedHealthData = $derived.by(() => {
return $statusQuery?.data?.nodeStates.
filter((e) => ['full', 'partial', 'failed'].includes(e.state)).
sort((a, b) => b.count - a.count)
});
/* Effects */ /* Effects */
$effect(() => { $effect(() => {
if ($initq.data && $statusQuery.data) { if ($statusQuery.data) {
let subClusters = $initq.data.clusters.find( let subClusters = clusters.find(
(c) => c.name == cluster, (c) => c.name == cluster,
).subClusters; ).subClusters;
for (let subCluster of subClusters) { for (let subCluster of subClusters) {
@@ -404,9 +391,12 @@
<Col xs="12" md="5" lg="4" xl="3"> <Col xs="12" md="5" lg="4" xl="3">
<Refresher <Refresher
initially={120} initially={120}
onRefresh={() => { onRefresh={(interval) => {
from = new Date(Date.now() - 5 * 60 * 1000); from = new Date(Date.now() - 5 * 60 * 1000);
to = new Date(Date.now()); to = new Date(Date.now());
if (interval) stackedFrom += Math.floor(interval / 1000);
else stackedFrom += 1 // Workaround: TineSelection not linked, just trigger new data on manual refresh
}} }}
/> />
</Col> </Col>
@@ -415,7 +405,7 @@
<hr/> <hr/>
<!-- Node Stack Charts Dev--> <!-- Node Stack Charts Dev-->
{#if $initq.data && $statesTimed.data} {#if $statesTimed.data}
<Row cols={{ md: 2 , sm: 1}} class="mb-3 justify-content-center"> <Row cols={{ md: 2 , sm: 1}} class="mb-3 justify-content-center">
<Col class="px-3 mt-2 mt-lg-0"> <Col class="px-3 mt-2 mt-lg-0">
<div bind:clientWidth={stackedWidth1}> <div bind:clientWidth={stackedWidth1}>
@@ -459,7 +449,7 @@
<hr/> <hr/>
<!-- Node Health Pis, later Charts --> <!-- Node Health Pis, later Charts -->
{#if $initq.data && $nodesStateCounts.data} {#if $statusQuery?.data?.nodeStates}
<Row cols={{ lg: 4, md: 2 , sm: 1}} class="mb-3 justify-content-center"> <Row cols={{ lg: 4, md: 2 , sm: 1}} class="mb-3 justify-content-center">
<Col class="px-3 mt-2 mt-lg-0"> <Col class="px-3 mt-2 mt-lg-0">
<div bind:clientWidth={pieWidth}> <div bind:clientWidth={pieWidth}>
@@ -545,8 +535,8 @@
<hr/> <hr/>
<!-- Gauges & Roofline per Subcluster--> <!-- Gauges & Roofline per Subcluster-->
{#if $initq.data && $statusQuery.data} {#if $statusQuery.data}
{#each $initq.data.clusters.find((c) => c.name == cluster).subClusters as subCluster, i} {#each clusters.find((c) => c.name == cluster).subClusters as subCluster, i}
<Row cols={{ lg: 3, md: 1 , sm: 1}} class="mb-3 justify-content-center"> <Row cols={{ lg: 3, md: 1 , sm: 1}} class="mb-3 justify-content-center">
<Col class="px-3"> <Col class="px-3">
<Card class="h-auto mt-1"> <Card class="h-auto mt-1">

View File

@@ -24,7 +24,6 @@
getContextClient, getContextClient,
} from "@urql/svelte"; } from "@urql/svelte";
import { import {
init,
scramble, scramble,
scrambleNames, scrambleNames,
convert2uplot, convert2uplot,
@@ -41,7 +40,6 @@
} = $props(); } = $props();
/* Const Init */ /* Const Init */
const { query: initq } = init();
const client = getContextClient(); const client = getContextClient();
const durationBinOptions = ["1m","10m","1h","6h","12h"]; const durationBinOptions = ["1m","10m","1h","6h","12h"];