mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2026-03-15 04:17:30 +01:00
Consolidate UsageDash into single GraphQL query
Merge three separate queries (topJobsQuery, topNodesQuery, topAccsQuery) into one topStatsQuery with 6 aliased jobsStatistics fields, reducing 3 HTTP round trips to 1 on the status dashboard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Entire-Checkpoint: 40d806a3240c
This commit is contained in:
@@ -19,13 +19,9 @@
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
Input,
|
Input,
|
||||||
InputGroup,
|
InputGroup,
|
||||||
InputGroupText
|
InputGroupText,
|
||||||
} from "@sveltestrap/sveltestrap";
|
} from "@sveltestrap/sveltestrap";
|
||||||
import {
|
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
||||||
queryStore,
|
|
||||||
gql,
|
|
||||||
getContextClient,
|
|
||||||
} from "@urql/svelte";
|
|
||||||
import {
|
import {
|
||||||
scramble,
|
scramble,
|
||||||
scrambleNames,
|
scrambleNames,
|
||||||
@@ -49,28 +45,34 @@
|
|||||||
const durationBinOptions = ["1m", "10m", "1h", "6h", "12h"];
|
const durationBinOptions = ["1m", "10m", "1h", "6h", "12h"];
|
||||||
|
|
||||||
/* State Init */
|
/* State Init */
|
||||||
let pagingState = $state({page: 1, itemsPerPage: 10}) // Top 10
|
let pagingState = $state({ page: 1, itemsPerPage: 10 }); // Top 10
|
||||||
let selectedHistograms = $state([]) // Dummy For Refresh
|
let selectedHistograms = $state([]); // Dummy For Refresh
|
||||||
let colWidthJobs = $state(0);
|
let colWidthJobs = $state(0);
|
||||||
let colWidthNodes = $state(0);
|
let colWidthNodes = $state(0);
|
||||||
let colWidthAccs = $state(0);
|
let colWidthAccs = $state(0);
|
||||||
let numDurationBins = $state("1h");
|
let numDurationBins = $state("1h");
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
const canvasPrefix = $derived(`${presetCluster}-${presetSubCluster ? presetSubCluster : ''}`)
|
const canvasPrefix = $derived(
|
||||||
|
`${presetCluster}-${presetSubCluster ? presetSubCluster : ""}`,
|
||||||
const statusFilter = $derived(presetSubCluster
|
|
||||||
? [{ state: ["running"] }, { cluster: { eq: presetCluster} }, { subCluster: { eq: presetSubCluster } }]
|
|
||||||
: [{ state: ["running"] }, { cluster: { eq: presetCluster} }]
|
|
||||||
);
|
);
|
||||||
const topJobsQuery = $derived(loadMe ? queryStore({
|
|
||||||
|
const statusFilter = $derived(
|
||||||
|
presetSubCluster
|
||||||
|
? [
|
||||||
|
{ state: ["running"] },
|
||||||
|
{ cluster: { eq: presetCluster } },
|
||||||
|
{ subCluster: { eq: presetSubCluster } },
|
||||||
|
]
|
||||||
|
: [{ state: ["running"] }, { cluster: { eq: presetCluster } }],
|
||||||
|
);
|
||||||
|
const topStatsQuery = $derived(
|
||||||
|
loadMe
|
||||||
|
? queryStore({
|
||||||
client: client,
|
client: client,
|
||||||
query: gql`
|
query: gql`
|
||||||
query (
|
query ($filter: [JobFilter!]!, $paging: PageRequest!) {
|
||||||
$filter: [JobFilter!]!
|
topUserJobs: jobsStatistics(
|
||||||
$paging: PageRequest!
|
|
||||||
) {
|
|
||||||
topUser: jobsStatistics(
|
|
||||||
filter: $filter
|
filter: $filter
|
||||||
page: $paging
|
page: $paging
|
||||||
sortBy: TOTALJOBS
|
sortBy: TOTALJOBS
|
||||||
@@ -80,7 +82,7 @@
|
|||||||
name
|
name
|
||||||
totalJobs
|
totalJobs
|
||||||
}
|
}
|
||||||
topProjects: jobsStatistics(
|
topProjectJobs: jobsStatistics(
|
||||||
filter: $filter
|
filter: $filter
|
||||||
page: $paging
|
page: $paging
|
||||||
sortBy: TOTALJOBS
|
sortBy: TOTALJOBS
|
||||||
@@ -89,23 +91,7 @@
|
|||||||
id
|
id
|
||||||
totalJobs
|
totalJobs
|
||||||
}
|
}
|
||||||
}
|
topUserNodes: jobsStatistics(
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
filter: statusFilter,
|
|
||||||
paging: pagingState // Top 10
|
|
||||||
},
|
|
||||||
requestPolicy: "network-only"
|
|
||||||
}) : null);
|
|
||||||
|
|
||||||
const topNodesQuery = $derived(loadMe ? queryStore({
|
|
||||||
client: client,
|
|
||||||
query: gql`
|
|
||||||
query (
|
|
||||||
$filter: [JobFilter!]!
|
|
||||||
$paging: PageRequest!
|
|
||||||
) {
|
|
||||||
topUser: jobsStatistics(
|
|
||||||
filter: $filter
|
filter: $filter
|
||||||
page: $paging
|
page: $paging
|
||||||
sortBy: TOTALNODES
|
sortBy: TOTALNODES
|
||||||
@@ -115,7 +101,7 @@
|
|||||||
name
|
name
|
||||||
totalNodes
|
totalNodes
|
||||||
}
|
}
|
||||||
topProjects: jobsStatistics(
|
topProjectNodes: jobsStatistics(
|
||||||
filter: $filter
|
filter: $filter
|
||||||
page: $paging
|
page: $paging
|
||||||
sortBy: TOTALNODES
|
sortBy: TOTALNODES
|
||||||
@@ -124,23 +110,7 @@
|
|||||||
id
|
id
|
||||||
totalNodes
|
totalNodes
|
||||||
}
|
}
|
||||||
}
|
topUserAccs: jobsStatistics(
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
filter: statusFilter,
|
|
||||||
paging: pagingState
|
|
||||||
},
|
|
||||||
requestPolicy: "network-only"
|
|
||||||
}) : null);
|
|
||||||
|
|
||||||
const topAccsQuery = $derived(loadMe ? queryStore({
|
|
||||||
client: client,
|
|
||||||
query: gql`
|
|
||||||
query (
|
|
||||||
$filter: [JobFilter!]!
|
|
||||||
$paging: PageRequest!
|
|
||||||
) {
|
|
||||||
topUser: jobsStatistics(
|
|
||||||
filter: $filter
|
filter: $filter
|
||||||
page: $paging
|
page: $paging
|
||||||
sortBy: TOTALACCS
|
sortBy: TOTALACCS
|
||||||
@@ -150,7 +120,7 @@
|
|||||||
name
|
name
|
||||||
totalAccs
|
totalAccs
|
||||||
}
|
}
|
||||||
topProjects: jobsStatistics(
|
topProjectAccs: jobsStatistics(
|
||||||
filter: $filter
|
filter: $filter
|
||||||
page: $paging
|
page: $paging
|
||||||
sortBy: TOTALACCS
|
sortBy: TOTALACCS
|
||||||
@@ -163,13 +133,17 @@
|
|||||||
`,
|
`,
|
||||||
variables: {
|
variables: {
|
||||||
filter: statusFilter,
|
filter: statusFilter,
|
||||||
paging: pagingState
|
paging: pagingState, // Top 10
|
||||||
},
|
},
|
||||||
requestPolicy: "network-only"
|
requestPolicy: "network-only",
|
||||||
}): null);
|
})
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
|
||||||
// Note: nodeMetrics are requested on configured $timestep resolution
|
// Note: nodeMetrics are requested on configured $timestep resolution
|
||||||
const nodeStatusQuery = $derived(loadMe ? queryStore({
|
const nodeStatusQuery = $derived(
|
||||||
|
loadMe
|
||||||
|
? queryStore({
|
||||||
client: client,
|
client: client,
|
||||||
query: gql`
|
query: gql`
|
||||||
query (
|
query (
|
||||||
@@ -177,7 +151,11 @@
|
|||||||
$selectedHistograms: [String!]
|
$selectedHistograms: [String!]
|
||||||
$numDurationBins: String
|
$numDurationBins: String
|
||||||
) {
|
) {
|
||||||
jobsStatistics(filter: $filter, metrics: $selectedHistograms, numDurationBins: $numDurationBins) {
|
jobsStatistics(
|
||||||
|
filter: $filter
|
||||||
|
metrics: $selectedHistograms
|
||||||
|
numDurationBins: $numDurationBins
|
||||||
|
) {
|
||||||
histDuration {
|
histDuration {
|
||||||
count
|
count
|
||||||
value
|
value
|
||||||
@@ -198,19 +176,21 @@
|
|||||||
selectedHistograms: selectedHistograms, // No Metrics requested for node hardware stats
|
selectedHistograms: selectedHistograms, // No Metrics requested for node hardware stats
|
||||||
numDurationBins: numDurationBins,
|
numDurationBins: numDurationBins,
|
||||||
},
|
},
|
||||||
requestPolicy: "network-only"
|
requestPolicy: "network-only",
|
||||||
}) : null);
|
})
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
|
||||||
/* Functions */
|
/* Functions */
|
||||||
function legendColors(targetIdx) {
|
function legendColors(targetIdx) {
|
||||||
// Reuses first color if targetIdx overflows
|
// Reuses first color if targetIdx overflows
|
||||||
let c;
|
let c;
|
||||||
if (useCbColors) {
|
if (useCbColors) {
|
||||||
c = [...colors['colorblind']];
|
c = [...colors["colorblind"]];
|
||||||
} else if (useAltColors) {
|
} else if (useAltColors) {
|
||||||
c = [...colors['alternative']];
|
c = [...colors["alternative"]];
|
||||||
} else {
|
} else {
|
||||||
c = [...colors['default']];
|
c = [...colors["default"]];
|
||||||
}
|
}
|
||||||
return c[(c.length + targetIdx) % c.length];
|
return c[(c.length + targetIdx) % c.length];
|
||||||
}
|
}
|
||||||
@@ -223,9 +203,7 @@
|
|||||||
<InputGroupText>
|
<InputGroupText>
|
||||||
<Icon name="bar-chart-line-fill" />
|
<Icon name="bar-chart-line-fill" />
|
||||||
</InputGroupText>
|
</InputGroupText>
|
||||||
<InputGroupText>
|
<InputGroupText>Duration Bin Size</InputGroupText>
|
||||||
Duration Bin Size
|
|
||||||
</InputGroupText>
|
|
||||||
<Input type="select" bind:value={numDurationBins}>
|
<Input type="select" bind:value={numDurationBins}>
|
||||||
{#each durationBinOptions as dbin}
|
{#each durationBinOptions as dbin}
|
||||||
<option value={dbin}>{dbin}</option>
|
<option value={dbin}>{dbin}</option>
|
||||||
@@ -247,14 +225,16 @@
|
|||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<!-- Job Duration, Top Users and Projects-->
|
<!-- Job Duration, Top Users and Projects-->
|
||||||
{#if $topJobsQuery?.fetching || $nodeStatusQuery?.fetching}
|
{#if $topStatsQuery?.fetching || $nodeStatusQuery?.fetching}
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{:else if $topJobsQuery?.data && $nodeStatusQuery?.data}
|
{:else if $topStatsQuery?.data && $nodeStatusQuery?.data}
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs="12" lg="4" class="p-2">
|
<Col xs="12" lg="4" class="p-2">
|
||||||
{#key $nodeStatusQuery.data.jobsStatistics[0].histDuration}
|
{#key $nodeStatusQuery.data.jobsStatistics[0].histDuration}
|
||||||
<Histogram
|
<Histogram
|
||||||
data={convert2uplot($nodeStatusQuery.data.jobsStatistics[0].histDuration)}
|
data={convert2uplot(
|
||||||
|
$nodeStatusQuery.data.jobsStatistics[0].histDuration,
|
||||||
|
)}
|
||||||
title="Duration Distribution"
|
title="Duration Distribution"
|
||||||
xlabel="Current Job Runtimes"
|
xlabel="Current Job Runtimes"
|
||||||
xunit="Runtime"
|
xunit="Runtime"
|
||||||
@@ -269,18 +249,18 @@
|
|||||||
</Col>
|
</Col>
|
||||||
<Col xs="6" md="3" lg="2" class="p-2">
|
<Col xs="6" md="3" lg="2" class="p-2">
|
||||||
<div bind:clientWidth={colWidthJobs}>
|
<div bind:clientWidth={colWidthJobs}>
|
||||||
<h4 class="text-center">
|
<h4 class="text-center">Top Users: Jobs</h4>
|
||||||
Top Users: Jobs
|
|
||||||
</h4>
|
|
||||||
<Pie
|
<Pie
|
||||||
{useAltColors}
|
{useAltColors}
|
||||||
canvasId="{canvasPrefix}-hpcpie-jobs-users"
|
canvasId="{canvasPrefix}-hpcpie-jobs-users"
|
||||||
size={colWidthJobs * 0.75}
|
size={colWidthJobs * 0.75}
|
||||||
sliceLabel="Jobs"
|
sliceLabel="Jobs"
|
||||||
quantities={$topJobsQuery.data.topUser.map(
|
quantities={$topStatsQuery.data.topUserJobs.map(
|
||||||
(tu) => tu['totalJobs'],
|
(tu) => tu["totalJobs"],
|
||||||
|
)}
|
||||||
|
entities={$topStatsQuery.data.topUserJobs.map((tu) =>
|
||||||
|
scrambleNames ? scramble(tu.id) : tu.id,
|
||||||
)}
|
)}
|
||||||
entities={$topJobsQuery.data.topUser.map((tu) => scrambleNames ? scramble(tu.id) : tu.id)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -291,11 +271,17 @@
|
|||||||
<th style="padding-left: 0.5rem;">User</th>
|
<th style="padding-left: 0.5rem;">User</th>
|
||||||
<th>Jobs</th>
|
<th>Jobs</th>
|
||||||
</tr>
|
</tr>
|
||||||
{#each $topJobsQuery.data.topUser as tu, i}
|
{#each $topStatsQuery.data.topUserJobs as tu, i}
|
||||||
<tr>
|
<tr>
|
||||||
<td><Icon name="circle-fill" style="color: {legendColors(i)};" /></td>
|
<td
|
||||||
|
><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
|
||||||
|
>
|
||||||
<td id="{canvasPrefix}-topName-jobs-{tu.id}">
|
<td id="{canvasPrefix}-topName-jobs-{tu.id}">
|
||||||
<a target="_blank" href="/monitoring/user/{tu.id}?cluster={presetCluster}{presetSubCluster ? '&partition='+presetSubCluster : ''}&state=running"
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="/monitoring/user/{tu.id}?cluster={presetCluster}{presetSubCluster
|
||||||
|
? '&partition=' + presetSubCluster
|
||||||
|
: ''}&state=running"
|
||||||
>{scrambleNames ? scramble(tu.id) : tu.id}
|
>{scrambleNames ? scramble(tu.id) : tu.id}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@@ -306,25 +292,25 @@
|
|||||||
>{scrambleNames ? scramble(tu.name) : tu.name}</Tooltip
|
>{scrambleNames ? scramble(tu.name) : tu.name}</Tooltip
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
<td>{tu['totalJobs']}</td>
|
<td>{tu["totalJobs"]}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</Table>
|
</Table>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col xs="6" md="3" lg="2" class="p-2">
|
<Col xs="6" md="3" lg="2" class="p-2">
|
||||||
<h4 class="text-center">
|
<h4 class="text-center">Top Projects: Jobs</h4>
|
||||||
Top Projects: Jobs
|
|
||||||
</h4>
|
|
||||||
<Pie
|
<Pie
|
||||||
{useAltColors}
|
{useAltColors}
|
||||||
canvasId="{canvasPrefix}-hpcpie-jobs-projects"
|
canvasId="{canvasPrefix}-hpcpie-jobs-projects"
|
||||||
size={colWidthJobs * 0.75}
|
size={colWidthJobs * 0.75}
|
||||||
sliceLabel={'Jobs'}
|
sliceLabel={"Jobs"}
|
||||||
quantities={$topJobsQuery.data.topProjects.map(
|
quantities={$topStatsQuery.data.topProjectJobs.map(
|
||||||
(tp) => tp['totalJobs'],
|
(tp) => tp["totalJobs"],
|
||||||
|
)}
|
||||||
|
entities={$topStatsQuery.data.topProjectJobs.map((tp) =>
|
||||||
|
scrambleNames ? scramble(tp.id) : tp.id,
|
||||||
)}
|
)}
|
||||||
entities={$topJobsQuery.data.topProjects.map((tp) => scrambleNames ? scramble(tp.id) : tp.id)}
|
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs="6" md="3" lg="2" class="p-2">
|
<Col xs="6" md="3" lg="2" class="p-2">
|
||||||
@@ -334,34 +320,44 @@
|
|||||||
<th style="padding-left: 0.5rem;">Project</th>
|
<th style="padding-left: 0.5rem;">Project</th>
|
||||||
<th>Jobs</th>
|
<th>Jobs</th>
|
||||||
</tr>
|
</tr>
|
||||||
{#each $topJobsQuery.data.topProjects as tp, i}
|
{#each $topStatsQuery.data.topProjectJobs as tp, i}
|
||||||
<tr>
|
<tr>
|
||||||
<td><Icon name="circle-fill" style="color: {legendColors(i)};" /></td>
|
<td
|
||||||
|
><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
|
||||||
|
>
|
||||||
<td>
|
<td>
|
||||||
<a target="_blank" href="/monitoring/jobs/?cluster={presetCluster}{presetSubCluster ? '&partition='+presetSubCluster : ''}&state=running&project={tp.id}&projectMatch=eq"
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="/monitoring/jobs/?cluster={presetCluster}{presetSubCluster
|
||||||
|
? '&partition=' + presetSubCluster
|
||||||
|
: ''}&state=running&project={tp.id}&projectMatch=eq"
|
||||||
>{scrambleNames ? scramble(tp.id) : tp.id}
|
>{scrambleNames ? scramble(tp.id) : tp.id}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{tp['totalJobs']}</td>
|
<td>{tp["totalJobs"]}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</Table>
|
</Table>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{:else}
|
{:else}
|
||||||
<Card class="mx-4" body color="warning">Cannot render job status charts: No data!</Card>
|
<Card class="mx-4" body color="warning"
|
||||||
|
>Cannot render job status charts: No data!</Card
|
||||||
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<!-- Node Distribution, Top Users and Projects-->
|
<!-- Node Distribution, Top Users and Projects-->
|
||||||
{#if $topNodesQuery?.fetching || $nodeStatusQuery?.fetching}
|
{#if $topStatsQuery?.fetching || $nodeStatusQuery?.fetching}
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{:else if $topNodesQuery?.data && $nodeStatusQuery?.data}
|
{:else if $topStatsQuery?.data && $nodeStatusQuery?.data}
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs="12" lg="4" class="p-2">
|
<Col xs="12" lg="4" class="p-2">
|
||||||
<Histogram
|
<Histogram
|
||||||
data={convert2uplot($nodeStatusQuery.data.jobsStatistics[0].histNumNodes)}
|
data={convert2uplot(
|
||||||
|
$nodeStatusQuery.data.jobsStatistics[0].histNumNodes,
|
||||||
|
)}
|
||||||
title="Number of Nodes Distribution"
|
title="Number of Nodes Distribution"
|
||||||
xlabel="Allocated Nodes"
|
xlabel="Allocated Nodes"
|
||||||
xunit="Nodes"
|
xunit="Nodes"
|
||||||
@@ -373,18 +369,18 @@
|
|||||||
</Col>
|
</Col>
|
||||||
<Col xs="6" md="3" lg="2" class="p-2">
|
<Col xs="6" md="3" lg="2" class="p-2">
|
||||||
<div bind:clientWidth={colWidthNodes}>
|
<div bind:clientWidth={colWidthNodes}>
|
||||||
<h4 class="text-center">
|
<h4 class="text-center">Top Users: Nodes</h4>
|
||||||
Top Users: Nodes
|
|
||||||
</h4>
|
|
||||||
<Pie
|
<Pie
|
||||||
{useAltColors}
|
{useAltColors}
|
||||||
canvasId="{canvasPrefix}-hpcpie-nodes-users"
|
canvasId="{canvasPrefix}-hpcpie-nodes-users"
|
||||||
size={colWidthNodes * 0.75}
|
size={colWidthNodes * 0.75}
|
||||||
sliceLabel="Nodes"
|
sliceLabel="Nodes"
|
||||||
quantities={$topNodesQuery.data.topUser.map(
|
quantities={$topStatsQuery.data.topUserNodes.map(
|
||||||
(tu) => tu['totalNodes'],
|
(tu) => tu["totalNodes"],
|
||||||
|
)}
|
||||||
|
entities={$topStatsQuery.data.topUserNodes.map((tu) =>
|
||||||
|
scrambleNames ? scramble(tu.id) : tu.id,
|
||||||
)}
|
)}
|
||||||
entities={$topNodesQuery.data.topUser.map((tu) => scrambleNames ? scramble(tu.id) : tu.id)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -395,11 +391,17 @@
|
|||||||
<th style="padding-left: 0.5rem;">User</th>
|
<th style="padding-left: 0.5rem;">User</th>
|
||||||
<th>Nodes</th>
|
<th>Nodes</th>
|
||||||
</tr>
|
</tr>
|
||||||
{#each $topNodesQuery.data.topUser as tu, i}
|
{#each $topStatsQuery.data.topUserNodes as tu, i}
|
||||||
<tr>
|
<tr>
|
||||||
<td><Icon name="circle-fill" style="color: {legendColors(i)};" /></td>
|
<td
|
||||||
|
><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
|
||||||
|
>
|
||||||
<td id="{canvasPrefix}-topName-nodes-{tu.id}">
|
<td id="{canvasPrefix}-topName-nodes-{tu.id}">
|
||||||
<a target="_blank" href="/monitoring/user/{tu.id}?cluster={presetCluster}{presetSubCluster ? '&partition='+presetSubCluster : ''}&state=running"
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="/monitoring/user/{tu.id}?cluster={presetCluster}{presetSubCluster
|
||||||
|
? '&partition=' + presetSubCluster
|
||||||
|
: ''}&state=running"
|
||||||
>{scrambleNames ? scramble(tu.id) : tu.id}
|
>{scrambleNames ? scramble(tu.id) : tu.id}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@@ -410,25 +412,25 @@
|
|||||||
>{scrambleNames ? scramble(tu.name) : tu.name}</Tooltip
|
>{scrambleNames ? scramble(tu.name) : tu.name}</Tooltip
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
<td>{tu['totalNodes']}</td>
|
<td>{tu["totalNodes"]}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</Table>
|
</Table>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col xs="6" md="3" lg="2" class="p-2">
|
<Col xs="6" md="3" lg="2" class="p-2">
|
||||||
<h4 class="text-center">
|
<h4 class="text-center">Top Projects: Nodes</h4>
|
||||||
Top Projects: Nodes
|
|
||||||
</h4>
|
|
||||||
<Pie
|
<Pie
|
||||||
{useAltColors}
|
{useAltColors}
|
||||||
canvasId="{canvasPrefix}-hpcpie-nodes-projects"
|
canvasId="{canvasPrefix}-hpcpie-nodes-projects"
|
||||||
size={colWidthNodes * 0.75}
|
size={colWidthNodes * 0.75}
|
||||||
sliceLabel={'Nodes'}
|
sliceLabel={"Nodes"}
|
||||||
quantities={$topNodesQuery.data.topProjects.map(
|
quantities={$topStatsQuery.data.topProjectNodes.map(
|
||||||
(tp) => tp['totalNodes'],
|
(tp) => tp["totalNodes"],
|
||||||
|
)}
|
||||||
|
entities={$topStatsQuery.data.topProjectNodes.map((tp) =>
|
||||||
|
scrambleNames ? scramble(tp.id) : tp.id,
|
||||||
)}
|
)}
|
||||||
entities={$topNodesQuery.data.topProjects.map((tp) => scrambleNames ? scramble(tp.id) : tp.id)}
|
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs="6" md="3" lg="2" class="p-2">
|
<Col xs="6" md="3" lg="2" class="p-2">
|
||||||
@@ -438,34 +440,44 @@
|
|||||||
<th style="padding-left: 0.5rem;">Project</th>
|
<th style="padding-left: 0.5rem;">Project</th>
|
||||||
<th>Nodes</th>
|
<th>Nodes</th>
|
||||||
</tr>
|
</tr>
|
||||||
{#each $topNodesQuery.data.topProjects as tp, i}
|
{#each $topStatsQuery.data.topProjectNodes as tp, i}
|
||||||
<tr>
|
<tr>
|
||||||
<td><Icon name="circle-fill" style="color: {legendColors(i)};" /></td>
|
<td
|
||||||
|
><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
|
||||||
|
>
|
||||||
<td>
|
<td>
|
||||||
<a target="_blank" href="/monitoring/jobs/?cluster={presetCluster}{presetSubCluster ? '&partition='+presetSubCluster : ''}&state=running&project={tp.id}&projectMatch=eq"
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="/monitoring/jobs/?cluster={presetCluster}{presetSubCluster
|
||||||
|
? '&partition=' + presetSubCluster
|
||||||
|
: ''}&state=running&project={tp.id}&projectMatch=eq"
|
||||||
>{scrambleNames ? scramble(tp.id) : tp.id}
|
>{scrambleNames ? scramble(tp.id) : tp.id}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{tp['totalNodes']}</td>
|
<td>{tp["totalNodes"]}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</Table>
|
</Table>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{:else}
|
{:else}
|
||||||
<Card class="mx-4" body color="warning">Cannot render node status charts: No data!</Card>
|
<Card class="mx-4" body color="warning"
|
||||||
|
>Cannot render node status charts: No data!</Card
|
||||||
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<!-- Acc Distribution, Top Users and Projects-->
|
<!-- Acc Distribution, Top Users and Projects-->
|
||||||
{#if $topAccsQuery?.fetching || $nodeStatusQuery?.fetching}
|
{#if $topStatsQuery?.fetching || $nodeStatusQuery?.fetching}
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{:else if $topAccsQuery?.data && $nodeStatusQuery?.data}
|
{:else if $topStatsQuery?.data && $nodeStatusQuery?.data}
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs="12" lg="4" class="p-2">
|
<Col xs="12" lg="4" class="p-2">
|
||||||
<Histogram
|
<Histogram
|
||||||
data={convert2uplot($nodeStatusQuery.data.jobsStatistics[0].histNumAccs)}
|
data={convert2uplot(
|
||||||
|
$nodeStatusQuery.data.jobsStatistics[0].histNumAccs,
|
||||||
|
)}
|
||||||
title="Number of Accelerators Distribution"
|
title="Number of Accelerators Distribution"
|
||||||
xlabel="Allocated Accs"
|
xlabel="Allocated Accs"
|
||||||
xunit="Accs"
|
xunit="Accs"
|
||||||
@@ -477,18 +489,18 @@
|
|||||||
</Col>
|
</Col>
|
||||||
<Col xs="6" md="3" lg="2" class="p-2">
|
<Col xs="6" md="3" lg="2" class="p-2">
|
||||||
<div bind:clientWidth={colWidthAccs}>
|
<div bind:clientWidth={colWidthAccs}>
|
||||||
<h4 class="text-center">
|
<h4 class="text-center">Top Users: GPUs</h4>
|
||||||
Top Users: GPUs
|
|
||||||
</h4>
|
|
||||||
<Pie
|
<Pie
|
||||||
{useAltColors}
|
{useAltColors}
|
||||||
canvasId="{canvasPrefix}-hpcpie-accs-users"
|
canvasId="{canvasPrefix}-hpcpie-accs-users"
|
||||||
size={colWidthAccs * 0.75}
|
size={colWidthAccs * 0.75}
|
||||||
sliceLabel="GPUs"
|
sliceLabel="GPUs"
|
||||||
quantities={$topAccsQuery.data.topUser.map(
|
quantities={$topStatsQuery.data.topUserAccs.map(
|
||||||
(tu) => tu['totalAccs'],
|
(tu) => tu["totalAccs"],
|
||||||
|
)}
|
||||||
|
entities={$topStatsQuery.data.topUserAccs.map((tu) =>
|
||||||
|
scrambleNames ? scramble(tu.id) : tu.id,
|
||||||
)}
|
)}
|
||||||
entities={$topAccsQuery.data.topUser.map((tu) => scrambleNames ? scramble(tu.id) : tu.id)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -499,11 +511,17 @@
|
|||||||
<th style="padding-left: 0.5rem;">User</th>
|
<th style="padding-left: 0.5rem;">User</th>
|
||||||
<th>GPUs</th>
|
<th>GPUs</th>
|
||||||
</tr>
|
</tr>
|
||||||
{#each $topAccsQuery.data.topUser as tu, i}
|
{#each $topStatsQuery.data.topUserAccs as tu, i}
|
||||||
<tr>
|
<tr>
|
||||||
<td><Icon name="circle-fill" style="color: {legendColors(i)};" /></td>
|
<td
|
||||||
|
><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
|
||||||
|
>
|
||||||
<td id="{canvasPrefix}-topName-accs-{tu.id}">
|
<td id="{canvasPrefix}-topName-accs-{tu.id}">
|
||||||
<a target="_blank" href="/monitoring/user/{tu.id}?cluster={presetCluster}{presetSubCluster ? '&partition='+presetSubCluster : ''}&state=running"
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="/monitoring/user/{tu.id}?cluster={presetCluster}{presetSubCluster
|
||||||
|
? '&partition=' + presetSubCluster
|
||||||
|
: ''}&state=running"
|
||||||
>{scrambleNames ? scramble(tu.id) : tu.id}
|
>{scrambleNames ? scramble(tu.id) : tu.id}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@@ -514,25 +532,25 @@
|
|||||||
>{scrambleNames ? scramble(tu.name) : tu.name}</Tooltip
|
>{scrambleNames ? scramble(tu.name) : tu.name}</Tooltip
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
<td>{tu['totalAccs']}</td>
|
<td>{tu["totalAccs"]}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</Table>
|
</Table>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col xs="6" md="3" lg="2" class="p-2">
|
<Col xs="6" md="3" lg="2" class="p-2">
|
||||||
<h4 class="text-center">
|
<h4 class="text-center">Top Projects: GPUs</h4>
|
||||||
Top Projects: GPUs
|
|
||||||
</h4>
|
|
||||||
<Pie
|
<Pie
|
||||||
{useAltColors}
|
{useAltColors}
|
||||||
canvasId="{canvasPrefix}-hpcpie-accs-projects"
|
canvasId="{canvasPrefix}-hpcpie-accs-projects"
|
||||||
size={colWidthAccs * 0.75}
|
size={colWidthAccs * 0.75}
|
||||||
sliceLabel={'GPUs'}
|
sliceLabel={"GPUs"}
|
||||||
quantities={$topAccsQuery.data.topProjects.map(
|
quantities={$topStatsQuery.data.topProjectAccs.map(
|
||||||
(tp) => tp['totalAccs'],
|
(tp) => tp["totalAccs"],
|
||||||
|
)}
|
||||||
|
entities={$topStatsQuery.data.topProjectAccs.map((tp) =>
|
||||||
|
scrambleNames ? scramble(tp.id) : tp.id,
|
||||||
)}
|
)}
|
||||||
entities={$topAccsQuery.data.topProjects.map((tp) => scrambleNames ? scramble(tp.id) : tp.id)}
|
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs="6" md="3" lg="2" class="p-2">
|
<Col xs="6" md="3" lg="2" class="p-2">
|
||||||
@@ -542,20 +560,29 @@
|
|||||||
<th style="padding-left: 0.5rem;">Project</th>
|
<th style="padding-left: 0.5rem;">Project</th>
|
||||||
<th>GPUs</th>
|
<th>GPUs</th>
|
||||||
</tr>
|
</tr>
|
||||||
{#each $topAccsQuery.data.topProjects as tp, i}
|
{#each $topStatsQuery.data.topProjectAccs as tp, i}
|
||||||
<tr>
|
<tr>
|
||||||
<td><Icon name="circle-fill" style="color: {legendColors(i)};" /></td>
|
<td
|
||||||
|
><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
|
||||||
|
>
|
||||||
<td>
|
<td>
|
||||||
<a target="_blank" href="/monitoring/jobs/?cluster={presetCluster}{presetSubCluster ? '&partition='+presetSubCluster : ''}&state=running&project={tp.id}&projectMatch=eq"
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="/monitoring/jobs/?cluster={presetCluster}{presetSubCluster
|
||||||
|
? '&partition=' + presetSubCluster
|
||||||
|
: ''}&state=running&project={tp.id}&projectMatch=eq"
|
||||||
>{scrambleNames ? scramble(tp.id) : tp.id}
|
>{scrambleNames ? scramble(tp.id) : tp.id}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{tp['totalAccs']}</td>
|
<td>{tp["totalAccs"]}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</Table>
|
</Table>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{:else}
|
{:else}
|
||||||
<Card class="mx-4" body color="warning">Cannot render accelerator status charts: No data!</Card>
|
<Card class="mx-4" body color="warning"
|
||||||
|
>Cannot render accelerator status charts: No data!</Card
|
||||||
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user