-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(front): resources/jobs charts in dashboard
Display charts of resources (nodes/cores) status and jobs queue in dashboard page based on metrics from Prometheus, when metrics feature is enabled. fix #275
- Loading branch information
Showing
11 changed files
with
585 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<!-- | ||
Copyright (c) 2024 Rackslab | ||
|
||
This file is part of Slurm-web. | ||
|
||
SPDX-License-Identifier: GPL-3.0-or-later | ||
--> | ||
|
||
<script setup lang="ts"> | ||
import { ref } from 'vue' | ||
import { useDashboardLiveChart } from '@/composables/dashboard/LiveChart' | ||
import type { MetricJobState } from '@/composables/GatewayAPI' | ||
import ErrorAlert from '@/components/ErrorAlert.vue' | ||
|
||
const chartCanvas = ref<HTMLCanvasElement | null>(null) | ||
|
||
const statesColors: Record<MetricJobState, string> = { | ||
running: 'rgb(51, 204, 51, 0.7)', // green | ||
pending: 'rgba(255, 204, 0, 0.7)', // yellow | ||
completing: 'rgba(204, 153, 0, 0.7)', // dark yellow | ||
completed: 'rgb(192, 191, 188, 0.7)', // grey | ||
cancelled: 'rgb(204, 0, 153, 0.7)', // purple | ||
unknown: 'rgb(30, 30, 30, 0.7)' // dark grey | ||
} | ||
|
||
const liveChart = useDashboardLiveChart<MetricJobState>('metrics_jobs', chartCanvas, statesColors, [ | ||
'unknown', | ||
'cancelled', | ||
'completed', | ||
'completing', | ||
'running', | ||
'pending' | ||
]) | ||
</script> | ||
|
||
<template> | ||
<div class="border-gray-200p border-b pb-5 pt-16 sm:flex sm:items-center sm:justify-between"> | ||
<h3 class="text-base font-semibold text-gray-900">Jobs Queue</h3> | ||
</div> | ||
<ErrorAlert v-if="liveChart.metrics.unable.value" class="mt-4" | ||
>Unable to retrieve jobs metrics.</ErrorAlert | ||
> | ||
<div v-else class="h-96 w-full"> | ||
<img | ||
v-show="!liveChart.metrics.loaded.value" | ||
class="h-full object-fill" | ||
src="/chart_placeholder.svg" | ||
alt="Loading chart" | ||
/> | ||
<canvas v-show="liveChart.metrics.loaded.value" ref="chartCanvas"></canvas> | ||
</div> | ||
</template> |
97 changes: 97 additions & 0 deletions
97
frontend/src/components/dashboard/ChartResourcesHistogram.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
<!-- | ||
Copyright (c) 2024 Rackslab | ||
|
||
This file is part of Slurm-web. | ||
|
||
SPDX-License-Identifier: GPL-3.0-or-later | ||
--> | ||
|
||
<script setup lang="ts"> | ||
import { onBeforeMount, ref, watch } from 'vue' | ||
import { useRouter, useRoute } from 'vue-router' | ||
import type { LocationQueryRaw } from 'vue-router' | ||
import { useRuntimeStore } from '@/stores/runtime' | ||
import { useDashboardLiveChart } from '@/composables/dashboard/LiveChart' | ||
import type { MetricResourceState } from '@/composables/GatewayAPI' | ||
import ErrorAlert from '@/components/ErrorAlert.vue' | ||
import { Switch } from '@headlessui/vue' | ||
|
||
const router = useRouter() | ||
const route = useRoute() | ||
const runtimeStore = useRuntimeStore() | ||
const chartCanvas = ref<HTMLCanvasElement | null>(null) | ||
|
||
const statesColors: Record<MetricResourceState, string> = { | ||
idle: 'rgb(51, 204, 51, 0.7)', // green | ||
down: 'rgb(204, 0, 0, 0.7)', // red | ||
mixed: 'rgba(255, 204, 0, 0.7)', // yellow | ||
allocated: 'rgba(204, 153, 0, 0.7)', // dark yellow | ||
drain: 'rgb(204, 0, 153, 0.7)', // purple | ||
unknown: 'rgb(192, 191, 188, 0.7)' // grey | ||
} | ||
|
||
const liveChart = useDashboardLiveChart<MetricResourceState>( | ||
runtimeStore.dashboard.coresToggle ? 'metrics_cores' : 'metrics_nodes', | ||
chartCanvas, | ||
statesColors, | ||
['unknown', 'down', 'drain', 'allocated', 'mixed', 'idle'] | ||
) | ||
|
||
/* Clear chart datasets and set new poller callback when dashboard range is | ||
* modified. */ | ||
watch( | ||
() => runtimeStore.dashboard.coresToggle, | ||
() => { | ||
router.push({ name: 'dashboard', query: runtimeStore.dashboard.query() as LocationQueryRaw }) | ||
if (runtimeStore.dashboard.coresToggle) { | ||
liveChart.setCallback('metrics_cores') | ||
} else { | ||
liveChart.setCallback('metrics_nodes') | ||
} | ||
} | ||
) | ||
|
||
onBeforeMount(() => { | ||
if (route.query.cores && typeof route.query.cores === 'string' && route.query.cores === 'true') { | ||
/* Retrieve the range criteria from query and update the store */ | ||
runtimeStore.dashboard.coresToggle = true | ||
} | ||
}) | ||
</script> | ||
|
||
<template> | ||
<div class="border-gray-200p border-b pb-5 pt-16 sm:flex sm:items-center sm:justify-between"> | ||
<h3 class="text-base font-semibold text-gray-900">Resources Status</h3> | ||
<div class="mt-3 inline-flex items-center text-sm sm:ml-4 sm:mt-0"> | ||
<span class="pr-2">Nodes</span> | ||
<Switch | ||
v-model="runtimeStore.dashboard.coresToggle" | ||
:class="[ | ||
runtimeStore.dashboard.coresToggle ? 'bg-slurmweb' : 'bg-gray-200', | ||
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:bg-slurmweb focus:outline-none focus:ring-2 focus:ring-offset-2' | ||
]" | ||
> | ||
<span | ||
aria-hidden="true" | ||
:class="[ | ||
runtimeStore.dashboard.coresToggle ? 'translate-x-5' : 'translate-x-0', | ||
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out' | ||
]" | ||
/> | ||
</Switch> | ||
<span class="pl-2">Cores</span> | ||
</div> | ||
</div> | ||
<ErrorAlert v-if="liveChart.metrics.unable.value" class="mt-4" | ||
>Unable to retrieve resource metrics.</ErrorAlert | ||
> | ||
<div v-else class="h-96 w-full"> | ||
<img | ||
v-show="!liveChart.metrics.loaded.value" | ||
class="h-full object-fill" | ||
src="/chart_placeholder.svg" | ||
alt="Loading chart" | ||
/> | ||
<canvas v-show="liveChart.metrics.loaded.value" ref="chartCanvas"></canvas> | ||
</div> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<!-- | ||
Copyright (c) 2024 Rackslab | ||
|
||
This file is part of Slurm-web. | ||
|
||
SPDX-License-Identifier: GPL-3.0-or-later | ||
--> | ||
|
||
<script setup lang="ts"> | ||
import { onBeforeMount } from 'vue' | ||
import { useRouter, useRoute } from 'vue-router' | ||
import type { LocationQueryRaw } from 'vue-router' | ||
import { useRuntimeStore } from '@/stores/runtime' | ||
import ChartResourcesHistogram from '@/components/dashboard/ChartResourcesHistogram.vue' | ||
import ChartJobsHistogram from '@/components/dashboard/ChartJobsHistogram.vue' | ||
import { isMetricRange, type MetricRange } from '@/composables/GatewayAPI' | ||
|
||
const router = useRouter() | ||
const route = useRoute() | ||
const runtimeStore = useRuntimeStore() | ||
|
||
function setRange(range: MetricRange) { | ||
runtimeStore.dashboard.range = range | ||
router.push({ name: 'dashboard', query: runtimeStore.dashboard.query() as LocationQueryRaw }) | ||
} | ||
|
||
onBeforeMount(() => { | ||
if (route.query.range && isMetricRange(route.query.range)) { | ||
/* Retrieve the range criteria from query and update the store */ | ||
runtimeStore.dashboard.range = route.query.range | ||
} else { | ||
runtimeStore.dashboard.range = 'hour' | ||
} | ||
}) | ||
</script> | ||
|
||
<template> | ||
<div class="border-gray-200p pt-16"> | ||
<div class="mt-3 text-right sm:mt-0"> | ||
<span class="isolate inline-flex rounded-md shadow-sm"> | ||
<button | ||
type="button" | ||
:class="[ | ||
runtimeStore.dashboard.range == 'week' | ||
? 'bg-slurmweb text-white' | ||
: 'bg-white text-gray-900 hover:bg-gray-50', | ||
'relative inline-flex items-center rounded-l-md px-3 py-2 text-xs font-semibold ring-1 ring-inset ring-gray-300 focus:z-10' | ||
]" | ||
@click="setRange('week')" | ||
> | ||
week | ||
</button> | ||
<button | ||
type="button" | ||
:class="[ | ||
runtimeStore.dashboard.range == 'day' | ||
? 'bg-slurmweb text-white' | ||
: 'bg-white text-gray-900 hover:bg-gray-50', | ||
'relative inline-flex items-center px-3 py-2 text-xs font-semibold ring-1 ring-inset ring-gray-300 focus:z-10' | ||
]" | ||
@click="setRange('day')" | ||
> | ||
day | ||
</button> | ||
<button | ||
type="button" | ||
:class="[ | ||
runtimeStore.dashboard.range == 'hour' | ||
? 'bg-slurmweb text-white' | ||
: 'bg-white text-gray-900 hover:bg-gray-50', | ||
'relative inline-flex items-center rounded-r-md px-3 py-2 text-xs font-semibold ring-1 ring-inset ring-gray-300 focus:z-10' | ||
]" | ||
@click="setRange('hour')" | ||
> | ||
hour | ||
</button> | ||
</span> | ||
</div> | ||
</div> | ||
<ChartResourcesHistogram v-if="runtimeStore.hasPermission('view-nodes')" /> | ||
<ChartJobsHistogram v-if="runtimeStore.hasPermission('view-jobs')" /> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.