mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-07-22 04:31:40 +02:00
add scheduler and health status pie charts
This commit is contained in:
@@ -321,6 +321,9 @@ func (r *NodeRepository) CountNodeStates(ctx context.Context, filters []*model.N
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add Group and Order
|
||||||
|
query = query.GroupBy("state").OrderBy("count DESC")
|
||||||
|
|
||||||
rows, err := query.RunWith(r.stmtCache).Query()
|
rows, err := query.RunWith(r.stmtCache).Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
queryString, queryVars, _ := query.ToSql()
|
queryString, queryVars, _ := query.ToSql()
|
||||||
@@ -367,6 +370,9 @@ func (r *NodeRepository) CountHealthStates(ctx context.Context, filters []*model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add Group and Order
|
||||||
|
query = query.GroupBy("state").OrderBy("count DESC")
|
||||||
|
|
||||||
rows, err := query.RunWith(r.stmtCache).Query()
|
rows, err := query.RunWith(r.stmtCache).Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
queryString, queryVars, _ := query.ToSql()
|
queryString, queryVars, _ := query.ToSql()
|
||||||
|
@@ -9,6 +9,8 @@
|
|||||||
import {
|
import {
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
|
Table,
|
||||||
|
Icon
|
||||||
} from "@sveltestrap/sveltestrap";
|
} from "@sveltestrap/sveltestrap";
|
||||||
import {
|
import {
|
||||||
queryStore,
|
queryStore,
|
||||||
@@ -18,8 +20,9 @@
|
|||||||
import {
|
import {
|
||||||
init,
|
init,
|
||||||
} from "../generic/utils.js";
|
} from "../generic/utils.js";
|
||||||
import Roofline from "../generic/plots/Roofline.svelte";
|
//import Roofline from "../generic/plots/Roofline.svelte";
|
||||||
import NewBubbleRoofline from "../generic/plots/NewBubbleRoofline.svelte";
|
import NewBubbleRoofline from "../generic/plots/NewBubbleRoofline.svelte";
|
||||||
|
import Pie, { colors } from "../generic/plots/Pie.svelte";
|
||||||
|
|
||||||
/* Svelte 5 Props */
|
/* Svelte 5 Props */
|
||||||
let {
|
let {
|
||||||
@@ -34,8 +37,10 @@
|
|||||||
let from = $state(new Date(Date.now() - 5 * 60 * 1000));
|
let from = $state(new Date(Date.now() - 5 * 60 * 1000));
|
||||||
let to = $state(new Date(Date.now()));
|
let to = $state(new Date(Date.now()));
|
||||||
let plotWidths = $state([]);
|
let plotWidths = $state([]);
|
||||||
let nodesCounts = $state({});
|
let statesWidth = $state(0);
|
||||||
let jobsJounts = $state({});
|
let healthWidth = $state(0);
|
||||||
|
// let nodesCounts = $state({});
|
||||||
|
// let jobsJounts = $state({});
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
// Note: nodeMetrics are requested on configured $timestep resolution
|
// Note: nodeMetrics are requested on configured $timestep resolution
|
||||||
@@ -183,6 +188,33 @@
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// 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 }}
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
$inspect($nodesStateCounts?.data?.nodeStates)
|
||||||
|
|
||||||
|
const refinedStateData = $derived.by(() => {
|
||||||
|
return $nodesStateCounts?.data?.nodeStates.filter((e) => ['allocated', 'reserved', 'idle', 'mixed','down', 'unknown'].includes(e.state))
|
||||||
|
});
|
||||||
|
|
||||||
|
const refinedHealthData = $derived.by(() => {
|
||||||
|
return $nodesStateCounts?.data?.nodeStates.filter((e) => ['full', 'partial', 'failed'].includes(e.state))
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/* Function */
|
/* Function */
|
||||||
function transformJobsStatsToData(subclusterData) {
|
function transformJobsStatsToData(subclusterData) {
|
||||||
/* c will contain values from 0 to 1 representing the duration */
|
/* c will contain values from 0 to 1 representing the duration */
|
||||||
@@ -339,3 +371,92 @@
|
|||||||
</Row>
|
</Row>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
{#if $initq.data && $nodesStateCounts.data}
|
||||||
|
<Row cols={{ lg: 4, md: 2 , sm: 1}} class="mb-3 justify-content-center">
|
||||||
|
<Col class="px-3 mt-2 mt-lg-0">
|
||||||
|
<b>Node State</b>
|
||||||
|
<div bind:clientWidth={statesWidth}>
|
||||||
|
{#key refinedStateData}
|
||||||
|
<b>Total: {refinedStateData.reduce((sum, item) => {
|
||||||
|
return sum + item.count;
|
||||||
|
}, 0)} Nodes
|
||||||
|
</b>
|
||||||
|
<Pie
|
||||||
|
canvasId="hpcpie-slurm"
|
||||||
|
size={statesWidth}
|
||||||
|
sliceLabel="Nodes"
|
||||||
|
quantities={refinedStateData.map(
|
||||||
|
(sd) => sd.count,
|
||||||
|
)}
|
||||||
|
entities={refinedStateData.map(
|
||||||
|
(sd) => sd.state,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{/key}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
<Col class="px-4 py-2">
|
||||||
|
{#key refinedStateData}
|
||||||
|
<Table>
|
||||||
|
<tr class="mb-2">
|
||||||
|
<th>Legend</th>
|
||||||
|
<th>Current State</th>
|
||||||
|
<th>#Nodes</th>
|
||||||
|
</tr>
|
||||||
|
{#each refinedStateData as sd, i}
|
||||||
|
<tr>
|
||||||
|
<td><Icon name="circle-fill" style="color: {colors[i]};" /></td>
|
||||||
|
<td>{sd.state}</td>
|
||||||
|
<td>{sd.count}</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</Table>
|
||||||
|
{/key}
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col class="px-3 mt-2 mt-lg-0">
|
||||||
|
<b>Node Health</b>
|
||||||
|
<div bind:clientWidth={healthWidth}>
|
||||||
|
{#key refinedHealthData}
|
||||||
|
<b>Total: {refinedStateData.reduce((sum, item) => {
|
||||||
|
return sum + item.count;
|
||||||
|
}, 0)} Nodes
|
||||||
|
</b>
|
||||||
|
<Pie
|
||||||
|
canvasId="hpcpie-health"
|
||||||
|
size={healthWidth}
|
||||||
|
sliceLabel="Nodes"
|
||||||
|
quantities={refinedHealthData.map(
|
||||||
|
(sd) => sd.count,
|
||||||
|
)}
|
||||||
|
entities={refinedHealthData.map(
|
||||||
|
(sd) => sd.state,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{/key}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
<Col class="px-4 py-2">
|
||||||
|
{#key refinedHealthData}
|
||||||
|
<Table>
|
||||||
|
<tr class="mb-2">
|
||||||
|
<th>Legend</th>
|
||||||
|
<th>Current Health</th>
|
||||||
|
<th>#Nodes</th>
|
||||||
|
</tr>
|
||||||
|
{#each refinedHealthData as hd, i}
|
||||||
|
<tr>
|
||||||
|
<td><Icon name="circle-fill" style="color: {colors[i]};" /></td>
|
||||||
|
<td>{hd.state}</td>
|
||||||
|
<td>{hd.count}</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</Table>
|
||||||
|
{/key}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{/if}
|
||||||
|
Reference in New Issue
Block a user