mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-11-20 08:47:22 +01:00
review status view components, make node states refreshable
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user