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.

257 lines
6.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<ul class="preview">
<li class="preview-item" style="width: 20%; flex: none" v-if="!hidePie">
<block-box :title="`${type === 'node' ? '节点显卡厂商分布' : '显卡类型分布'}`" class="nodeCard">
<div class="pie">
<echarts-plus :options="getPreviewBarPie(pieData, props)" :onClick="handlePieClick" ref="echartsRef" />
</div>
<ul class="nodeCard-legend">
<li v-for="{ name, value, color } in pieData" :style="{
fontWeight: currentName === name ? 'bold' : 'normal',
}">
<div class="left">
<span class="color-box" :style="{
'background-color': color,
}"></span>
<span> {{ name }}</span>
</div>
<span>{{ value }}</span>
</li>
</ul>
</block-box>
</li>
<li class="preview-item">
<TabTop v-bind="totalTop" :onClick="handleClick" class="node-top" />
</li>
<li class="preview-item">
<TabTop v-bind="usedTop" :onClick="handleClick" class="node-top" />
</li>
</ul>
</template>
<script setup>
import BlockBox from '@/components/BlockBox.vue';
import EchartsPlus from '@/components/Echarts-plus.vue';
import { getPreviewBarPie, getTopOptions } from '~/vgpu/components/config';
import { onMounted, reactive, ref } from 'vue';
import cardApi from '~/vgpu/api/card';
import TabTop from '~/vgpu/components/TabTop.vue';
const props = defineProps({
title: {
default: '节点',
},
type: {
default: 'node',
},
hidePie: Boolean,
handleClick: Function,
handlePieClick: Function,
currentName: String,
});
const echartsRef = ref();
const totalTop = {
title: `${props.title}资源分配率 Top5`,
config: [
// props.type === 'node' && {
// tab: 'CPU',
// key: 'cpu',
// nameKey: 'instance',
// data: [],
// query: `topk(5, sum(hami_container_vcore_allocated) by (instance) / sum(hami_core_size) by (instance) * 100)`,
// },
// props.type === 'node' && {
// 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: props.type,
data: [],
query: `topk(5, sum(hami_container_vgpu_allocated{}) by (${props.type}) / sum(hami_vgpu_count{}) by (${props.type}) * 100)`,
},
{
tab: '算力',
key: 'core',
nameKey: props.type,
data: [],
query: `topk(5, sum(hami_container_vcore_allocated{}) by (${props.type}) / sum(hami_core_size{}) by (${props.type}) * 100)`,
},
{
tab: '显存',
key: 'memory',
data: [],
nameKey: props.type,
query: `topk(5, sum(hami_container_vmemory_allocated{}) by (${props.type}) / sum(hami_memory_size{}) by (${props.type}) * 100)`,
},
].filter(Boolean),
};
const usedTop = {
title: `${props.title}资源使用率 Top5`,
config: [
props.type === 'node' && {
tab: 'CPU',
key: 'cpu',
nameKey: 'instance',
data: [],
query: `topk(5, (1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)) * 100)`,
},
props.type === 'node' && {
tab: '内存',
key: 'internal',
nameKey: 'instance',
data: [],
query: `topk(5, ((sum(node_memory_MemTotal_bytes) by (instance)) - sum(node_memory_MemAvailable_bytes) by (instance)) / sum(node_memory_MemTotal_bytes) by (instance) * 100)`,
},
{
tab: '算力',
key: 'core',
nameKey: props.type,
data: [],
query: `topk(5, avg(hami_core_util_avg) by (${props.type}))`,
},
{
tab: '显存',
key: 'memory',
data: [],
nameKey: props.type,
query: `topk(5, sum(hami_memory_used) by (${props.type}) / sum(hami_memory_size) by (${props.type}) * 100)`,
},
].filter(Boolean),
};
const pieConfig = {
deviceuuid: {
query:
'count by (devicetype) (sum by (deviceuuid, devicetype) (hami_vgpu_count))',
key: 'devicetype',
},
node: {
query: 'count by (provider) (sum by (node,provider) (hami_vgpu_count))',
key: 'provider',
},
};
const pieData = ref([]);
onMounted(async () => {
const thisPieConfig = pieConfig[props.type];
const { data } = await cardApi.getInstantVector({
query: thisPieConfig.query,
});
const colors = [
'#5470C6',
'#91CC75',
'#FAC858',
'#EE6666',
'#73C0DE',
'#3BA272',
'#FC8452',
'#9A60B4',
'#EA7CCC',
];
pieData.value = data.map((item, index) => {
return {
name: item.metric[thisPieConfig.key],
value: item.value,
color: colors[index],
};
});
});
</script>
<style scoped lang="scss">
ul {
margin: 0;
padding: 0;
list-style: none;
}
.preview {
width: 100%;
display: flex;
gap: 20px;
margin-bottom: 20px;
.preview-item {
flex: 1;
}
.nodeCard {
height: 100%;
.pie {
width: 200px;
height: 200px;
margin: 0 auto;
}
.nodeCard-legend {
width: 100%;
display: flex;
flex-direction: column;
gap: 15px;
max-height: calc(3 * (12px + 15px));
overflow-y: auto;
padding-right: 10px;
/* */
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
li {
display: flex;
justify-content: space-between;
font-size: 12px;
align-items: center;
.left {
display: flex;
align-items: center;
gap: 5px;
}
.color-box {
width: 10px;
height: 10px;
display: inline-block;
}
}
}
}
.node-top {
display: flex;
flex-direction: column;
min-height: 350px;
height: 100%;
&> :nth-child(2) {
flex: 1;
max-height: 280px;
}
.node-top-echarts {
//flex: 1;
}
}
}
</style>