mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-11-20 16:57:22 +01:00
review status view components, make node states refreshable
This commit is contained in:
@@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
{#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;">
|
<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>
|
||||||
|
|
||||||
@@ -61,3 +79,4 @@
|
|||||||
</TabPane>
|
</TabPane>
|
||||||
</TabContent>
|
</TabContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 -->
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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"];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user