You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

572 lines
15 KiB

<template>
<div class="home">
<div class="home-left">
<Block title="资源使用率">
<!-- <template #extra>
<div class="all-btn" @click="sendRouteChange('/admin/vgpu/card/admin')">
<svg-icon icon="more" style="margin-left: 4px" />
</div>
</template> -->
<div class="card-overview">
<div v-for="item in cardGaugeConfig.slice(0, 5)" :key="item.title">
<Gauge v-bind="item" />
</div>
</div>
<div class="card-overview">
<div v-for="item in cardGaugeConfig.slice(-3)" :key="item.title">
<Gauge v-bind="item" />
</div>
</div>
</Block>
<Block title="资源总览">
<!-- <template #extra>
<div class="all-btn" @click="sendRouteChange('/admin/vgpu/card/admin')">
全部<svg-icon icon="more" style="margin-left: 4px" />
</div>
</template> -->
<ul class="resourceOverview">
<li v-for="{ title, count, icon, to, unit } in resourceOverview" :key="title"
:style="{ cursor: to ? 'pointer' : 'default' }" @click="sendRouteChange(to)">
<div class="avatar">
<svg-icon :icon="icon" />
</div>
<div class="main">
<div>
{{ title }}
</div>
<div class="count">
{{ count }} <span style="font-size: 12px">{{ unit }}</span>
</div>
</div>
</li>
</ul>
</Block>
<Block v-for="{ title, dataSource } in rangeConfig" :title="title" :key="title">
<template #extra>
<time-picker v-model="times" type="datetimerange" size="small" />
</template>
<echarts-plus :options="getRangeOptions(dataSource)" style="height: 250px" />
</Block>
</div>
<div class="home-right">
<Block title="节点总览" style="margin-bottom: 16px">
<template #extra>
<div class="all-btn" @click="sendRouteChange('/admin/vgpu/node/admin')">
全部<svg-icon icon="more" style="margin-left: 4px" />
</div>
</template>
<ul class="node-all">
<li v-for="{ title, status, count, color } in nodes" :key="title" @click="
sendRouteChange(`/admin/vgpu/node/admin?isSchedulable=${status}`)
">
<div class="title">{{ title }}</div>
<div class="count" :style="{ color }">
{{ count }}
</div>
</li>
</ul>
</Block>
<Block title="显卡类型分布" style="margin-bottom: 16px">
<template #extra>
<div class="all-btn" @click="sendRouteChange('/admin/vgpu/card/admin')">
全部<svg-icon icon="more" style="margin-left: 4px" />
</div>
</template>
<div style="height: 218px">
<echarts-plus :options="getCardOptions(cardData, chartWidth)" :onClick="handlePieClick" />
</div>
</Block>
<TabTop v-bind="nodeTotalTop" :onClick="(params) => handleChartClick(params, router)"
style="margin-bottom: 16px" />
<TabTop v-bind="nodeUsedTop" :onClick="(params) => handleChartClick(params, router)" />
</div>
</div>
</template>
<script setup>
import { onMounted, ref, computed, reactive, watch, watchEffect } from 'vue';
import {
getCardOptions,
handleChartClick,
getRangeOptions,
} from './getOptions';
import Block from './Block.vue';
import './style.scss';
import { timeParse, getDaysInRange, getRandom, bytesToGB } from '@/utils';
import { useRouter } from 'vue-router';
import UserCard from '@/components/UserCard.vue';
import nodeApi from '~/vgpu/api/node';
import pollApi from '~/vgpu/api/poll';
import taskApi from '~/vgpu/api/task';
import monitorApi from '~/vgpu/api/monitor';
import cardApi from '~/vgpu/api/card';
import useInstantVector from '~/vgpu/hooks/useInstantVector';
import useParentAction from '~/vgpu/hooks/useParentAction';
import useFetchList from '@/hooks/useFetchList';
import { getTopOptions } from '~/vgpu/components/config';
import EchartsPlus from '@/components/Echarts-plus.vue';
import TabTop from '~/vgpu/components/TabTop.vue';
import TimeSelect from '~/vgpu/components/timeSelect.vue';
import Gauge from '~/vgpu/components/gauge.vue';
import { rangeConfigInit } from './config';
const router = useRouter();
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000);
const { sendRouteChange } = useParentAction();
const times = ref([start, end]);
const handlePieClick = (params) => {
sendRouteChange(`/admin/vgpu/card/admin?type=${params.data.name}`);
};
const alarmData = ref([])
const chartWidth = ref(200);
const cardGaugeConfig = useInstantVector([
{
title: 'CPU 使用率',
percent: 0,
query: `sum(count by(instance) (node_cpu_seconds_total{mode="idle"})*(1 - avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[1m]))))`,
totalQuery: `sum(count(node_cpu_seconds_total{mode="system"}) by (instance))`,
percentQuery: ``,
total: 0,
used: 0,
unit: '核',
},
{
title: '内存 使用率',
percent: 0,
query: `(sum(node_memory_MemTotal_bytes) - sum(node_memory_MemAvailable_bytes)) / 1024 / 1024 / 1024`,
totalQuery: `sum(node_memory_MemTotal_bytes) / 1024 / 1024 / 1024`,
percentQuery: ``,
total: 0,
used: 0,
unit: 'GiB',
},
{
title: '磁盘 使用率',
percent: 0,
query: `(sum(node_filesystem_size_bytes{fstype!~"tmpfs|overlay"})-sum(node_filesystem_free_bytes{fstype!~"tmpfs|overlay"})) / 1024 / 1024 / 1024`,
totalQuery: `sum(node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) / 1024 / 1024 / 1024`,
percentQuery: ``,
total: 0,
used: 0,
unit: 'GiB',
},
{
title: 'vGPU 分配率',
percent: 0,
query: `avg(sum (hami_container_vgpu_allocated) by (instance))`,
totalQuery: `avg(sum (hami_vgpu_count) by (instance))`,
percentQuery: `avg(sum (hami_container_vgpu_allocated) by (instance))/avg(sum (hami_vgpu_count) by (instance)) *100`,
total: 0,
used: 0,
unit: '个',
},
{
title: '算力分配率',
percent: 0,
query: `avg(sum(hami_container_vcore_allocated) by (instance))`,
totalQuery: `avg(sum(hami_core_size) by (instance))`,
percentQuery: `avg(sum(hami_container_vcore_allocated) by (instance))/avg(sum(hami_core_size) by (instance)) *100`,
total: 0,
used: 0,
unit: ' ',
},
{
title: '显存分配率',
percent: 0,
query: `avg(sum(hami_container_vmemory_allocated) by (instance)) / 1024`,
totalQuery: `avg(sum(hami_memory_size) by (instance)) / 1024`,
percentQuery: `(avg(sum(hami_container_vmemory_allocated) by (instance)) / 1024 )/(avg(sum(hami_memory_size) by (instance)) / 1024) *100 `,
total: 0,
used: 0,
unit: 'GiB',
},
{
title: '算力使用率',
percent: 0,
query: `avg(sum(hami_core_util) by (instance))`,
percentQuery: `avg(sum(hami_core_util_avg) by (instance))`,
totalQuery: `avg(sum(hami_core_size) by (instance))`,
total: 100,
used: 0,
unit: ' ',
},
{
title: '显存使用率',
percent: 0,
query: `avg(sum(hami_memory_used) by (instance)) / 1024`,
totalQuery: `avg(sum(hami_memory_size) by (instance))/1024`,
percentQuery: `(avg(sum(hami_memory_used) by (instance)) / 1024)/(avg(sum(hami_memory_size) by (instance))/1024)*100`,
total: 0,
used: 0,
unit: 'GiB',
},
]);
const resourceOverview = ref([
{
title: '节点',
count: 0,
icon: 'vgpu-node',
unit: '个',
to: '/admin/vgpu/node/admin'
},
{
title: '资源池',
count: 0,
icon: 'vgpu-pool-tab',
unit: '个',
to: '/admin/vgpu/poll/admin'
},
{
title: 'CPU',
count: 0,
icon: 'vgpu-chip',
unit: '核',
},
{
title: '内存',
count: 0,
icon: 'vgpu-memory',
unit: 'GIB',
},
{
title: '磁盘',
count: 0,
icon: 'vgpu-disk',
unit: 'GIB',
},
{
title: '显卡',
count: 0,
icon: 'vgpu-gpu-d',
unit: '张',
to: '/admin/vgpu/card/admin'
},
{
title: 'vGPU',
count: 0,
icon: 'vgpu-card',
unit: '个',
},
{
title: '算力',
count: 12,
icon: 'vgpu-core',
unit: ' ',
},
{
title: '显存',
count: 31,
icon: 'vgpu-mem',
unit: 'GiB',
},
]);
const nodeConfig = reactive({
instance: [
{ title: '可调度', key: 'yes', color: '#2563EB', value: 0 },
{ title: '禁止调度', key: 'no', color: '#DC2626', value: 0 },
],
core: [
{ title: '已分配', key: 'used', color: '#2563EB', value: 0 },
{ title: '闲置', key: 'free', color: '#B6C2CD', value: 0 },
{ title: '分配率', key: 'percent', color: '#B6C2CD', value: 0, unit: '%' },
],
memory: [
{
title: '已分配',
key: 'used',
color: '#2563EB',
value: 0,
unit: 'GiB',
},
{
title: '闲置',
key: 'free',
color: '#B6C2CD',
value: 0,
unit: 'GiB',
},
{
title: '分配率',
key: 'percent',
color: '#B6C2CD',
value: 0,
unit: '%',
},
],
});
const nodeData = useFetchList(nodeApi.getNodeListReq({ filters: {} }));
const cardData = useFetchList(cardApi.getCardListReq({ filters: {} }));
const pollData = useFetchList(pollApi.getPollList(), 'data');
const cardDetail = useInstantVector([
{
title: 'vGPU',
count: 0,
query: 'avg(hami_vgpu_count)',
unit: '个',
icon: 'gpu2',
},
{
title: '算力',
count: 0,
unit: ' ',
icon: 'account',
query: 'avg(hami_core_size)',
},
{
title: '显存',
count: 0,
unit: 'GiB',
icon: 'volume',
query: 'avg(hami_memory_size) / 1024',
},
]);
const nodes = computed(() => [
{
title: '可调度',
count: nodeData.value.filter((item) => !item.isExternal && item.isSchedulable).length,
isSchedulable: true,
isExternal: false,
status: 'true',
color: '#16A34A',
},
{
title: '禁止调度',
count: nodeData.value.filter((item) => !item.isExternal && !item.isSchedulable).length,
isSchedulable: false,
isExternal: false,
status: 'false',
color: '#1D2B3A',
},
]);
const exceed = useInstantVector([
{ title: 'vGPU 超配', count: 0, type: 'vgpu', query: 'avg(hami_vgpu_count)' },
{
title: '算力超配',
count: 0,
type: 'core',
query: 'avg(hami_vcore_scaling)',
},
{
title: '显存超配',
count: 0,
type: 'memory',
query: 'avg(hami_vmemory_scaling)',
},
]);
const nodeUsedTop = {
title: '节点资源使用率 Top5',
key: 'used',
config: [
// {
// tab: 'CPU',
// key: 'cpu',
// nameKey: 'instance',
// data: [],
// query: 'topk(5, avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])))',
// },
// {
// tab: '内存',
// key: 'internal',
// nameKey: 'instance',
// data: [],
// query: 'topk(5, ((node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes))',
// },
{
tab: '算力',
key: 'core',
nameKey: 'node',
data: [],
query: 'topk(5, avg(hami_core_util_avg) by (node))',
},
{
tab: '显存',
key: 'memory',
data: [],
nameKey: 'node',
query:
'topk(5, avg(hami_memory_used) by (node) / avg(hami_memory_size) by (node) * 100)',
},
],
};
const nodeTotalTop = {
title: '节点资源分配率 Top5',
key: 'used',
config: [
// {
// tab: 'CPU',
// key: 'cpu',
// nameKey: 'instance',
// data: [],
// query: `topk(5, sum(hami_container_vcore_allocated) by (instance) / sum(hami_core_size) by (instance) * 100)`,
// },
// {
// tab: '内存',
// key: 'internal',
// nameKey: 'instance',
// data: [],
// query: `topk(5, sum(hami_container_vmemory_allocated) by (instance) / sum(hami_memory_size) by (instance) * 100)`,
// },
{
tab: 'vGPU',
key: 'vgpu',
nameKey: 'node',
data: [],
query: `topk(5, avg(hami_container_vgpu_allocated{}) by (node) / avg(hami_vgpu_count{}) by (node) * 100)`,
},
{
tab: '算力',
key: 'core',
nameKey: 'node',
data: [],
query:
'topk(5, avg(hami_container_vcore_allocated{}) by (node) / avg(hami_core_size{}) by (node) * 100)',
},
{
tab: '显存',
key: 'memory',
data: [],
nameKey: 'node',
query:
'topk(5, avg(hami_container_vmemory_allocated{}) by (node) / avg(hami_memory_size{}) by (node) * 100)',
},
],
};
const rangeConfig = ref(rangeConfigInit);
const fetchRangeData = () => {
const params = {
range: {
start: timeParse(times.value[0]),
end: timeParse(times.value[1]),
step: '1m',
},
};
for (const item of rangeConfig.value) {
for (const v of item.dataSource) {
cardApi
.getRangeVector({
...params,
query: v.query,
})
.then((res) => {
v.data = res.data[0].values;
});
}
}
// cardApi
// .getRangeVector({
// ...params,
// query: `sum({__name__=~"alert:.*:count"})`,
// })
// .then((res) => {
// alarmData.value = res.data[0].values;
// });
};
watchEffect(() => {
resourceOverview.value[0].count = nodeData.value.length;
resourceOverview.value[1].count = pollData.value.length;
resourceOverview.value[2].count = cardGaugeConfig.value[0].total;
resourceOverview.value[3].count = Math.round(cardGaugeConfig.value[1].total);
resourceOverview.value[4].count = Math.round(cardGaugeConfig.value[2].total);
resourceOverview.value[5].count = cardData.value.length;
resourceOverview.value[6].count = cardGaugeConfig.value[3].total;
resourceOverview.value[7].count = cardGaugeConfig.value[4].total;
resourceOverview.value[8].count = cardGaugeConfig.value[5].total.toFixed(0);
});
onMounted(async () => {
const summary = await monitorApi.summary({
filters: {},
});
const nodeDataRes = {
yes: nodeData.value.filter((item) => item.isSchedulable).length,
no: nodeData.value.filter((item) => !item.isSchedulable).length,
};
nodeConfig.instance = nodeConfig.instance.map((item) => {
return { ...item, value: nodeDataRes[item.key] };
});
nodeConfig.core = nodeConfig.core.map((item) => {
const core_total = cardDetail.value[1].percent;
const coreData = {
percent: ((summary.coreUsed / core_total).toFixed(2) * 100).toFixed(0),
used: summary.coreUsed,
free: summary.coreTotal - summary.coreUsed,
};
return { ...item, value: coreData[item.key] };
});
nodeConfig.memory = nodeConfig.memory.map((item) => {
const memory_total = cardDetail.value[2].percent;
const coreData = {
percent: (
(summary.memoryUsed / 1024 / memory_total).toFixed(2) * 100
).toFixed(0),
used: summary.memoryUsed,
free: summary.memoryTotal - summary.memoryUsed,
};
return { ...item, value: coreData[item.key] };
});
});
watch(
times,
() => {
// fetchLineData();
fetchRangeData();
},
{ immediate: true },
);
</script>
<style>
.el-progress-bar__outer {
background-color: #b6c2cd;
}
.card-overview {
padding-bottom: 10px;
height: 190px;
display: grid;
grid-template-columns: repeat(5, 1fr);
.gauge-info {
margin-top: 10px;
}
}
</style>