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 {
getContext
} from "svelte"
import {
init,
} from "./generic/utils.js";
import {
Row,
Col,
Card,
CardBody,
TabContent,
TabPane
TabPane,
Spinner
} from "@sveltestrap/sveltestrap";
import StatusDash from "./status/StatusDash.svelte";
@@ -28,8 +32,8 @@
} = $props();
/*Const Init */
const { query: initq } = init();
const useCbColors = getContext("cc-config")?.plotConfiguration_colorblindMode || false
</script>
<!-- Loading indicator & Refresh -->
@@ -40,24 +44,39 @@
</Col>
</Row>
<Card class="overflow-auto" style="height: auto;">
<TabContent>
<TabPane tabId="status-dash" tab="Status" active>
<CardBody>
<StatusDash {presetCluster} {useCbColors} useAltColors></StatusDash>
</CardBody>
</TabPane>
<TabPane tabId="usage-dash" tab="Usage">
<CardBody>
<UsageDash {presetCluster} {useCbColors}></UsageDash>
</CardBody>
</TabPane>
<TabPane tabId="metric-dash" tab="Statistics">
<CardBody>
<StatisticsDash {presetCluster} {useCbColors}></StatisticsDash>
</CardBody>
</TabPane>
</TabContent>
</Card>
{#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>
<TabPane tabId="status-dash" tab="Status" active>
<CardBody>
<StatusDash clusters={$initq.data.clusters} {presetCluster} {useCbColors} useAltColors></StatusDash>
</CardBody>
</TabPane>
<TabPane tabId="usage-dash" tab="Usage">
<CardBody>
<UsageDash {presetCluster} {useCbColors}></UsageDash>
</CardBody>
</TabPane>
<TabPane tabId="metric-dash" tab="Statistics">
<CardBody>
<StatisticsDash {presetCluster} {useCbColors}></StatisticsDash>
</CardBody>
</TabPane>
</TabContent>
</Card>
{/if}

View File

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

View File

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

View File

@@ -22,9 +22,6 @@
gql,
getContextClient,
} from "@urql/svelte";
import {
init,
} from "../generic/utils.js";
import { formatDurationTime } from "../generic/units.js";
import Refresher from "../generic/helper/Refresher.svelte";
import TimeSelection from "../generic/select/TimeSelection.svelte";
@@ -34,13 +31,13 @@
/* Svelte 5 Props */
let {
clusters,
presetCluster,
useCbColors = false,
useAltColors = false,
} = $props();
/* Const Init */
const { query: initq } = init();
const client = getContextClient();
/* State Init */
@@ -67,34 +64,6 @@
let totalAccs = $state({});
/* 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
const statesTimed = $derived(queryStore({
client: client,
@@ -117,6 +86,7 @@
typeNode: "node",
typeHealth: "health"
},
requestPolicy: "network-only"
}));
// Note: nodeMetrics are requested on configured $timestep resolution
@@ -194,6 +164,11 @@
schedulerState
}
}
# Get Current States fir Pie Charts
nodeStates(filter: $nodeFilter) {
state
count
}
# totalNodes includes multiples if shared jobs
jobsStatistics(
filter: $jobFilter
@@ -221,10 +196,22 @@
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 */
$effect(() => {
if ($initq.data && $statusQuery.data) {
let subClusters = $initq.data.clusters.find(
if ($statusQuery.data) {
let subClusters = clusters.find(
(c) => c.name == cluster,
).subClusters;
for (let subCluster of subClusters) {
@@ -404,9 +391,12 @@
<Col xs="12" md="5" lg="4" xl="3">
<Refresher
initially={120}
onRefresh={() => {
onRefresh={(interval) => {
from = new Date(Date.now() - 5 * 60 * 1000);
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>
@@ -415,7 +405,7 @@
<hr/>
<!-- Node Stack Charts Dev-->
{#if $initq.data && $statesTimed.data}
{#if $statesTimed.data}
<Row cols={{ md: 2 , sm: 1}} class="mb-3 justify-content-center">
<Col class="px-3 mt-2 mt-lg-0">
<div bind:clientWidth={stackedWidth1}>
@@ -459,7 +449,7 @@
<hr/>
<!-- 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">
<Col class="px-3 mt-2 mt-lg-0">
<div bind:clientWidth={pieWidth}>
@@ -545,8 +535,8 @@
<hr/>
<!-- Gauges & Roofline per Subcluster-->
{#if $initq.data && $statusQuery.data}
{#each $initq.data.clusters.find((c) => c.name == cluster).subClusters as subCluster, i}
{#if $statusQuery.data}
{#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">
<Col class="px-3">
<Card class="h-auto mt-1">

View File

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