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.

255 lines
6.3 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>
<div>
<back-header> {{ title }}管理 > {{ props.name }} </back-header>
<block-box class="node-block">
<div class="node-detail">
<div class="node-detail-left">
<div class="title">详细信息</div>
<ul class="node-detail-info">
<li v-for="{ label, value, render } in detailColumns">
<span class="label">{{ label }}</span>
<component v-if="render" :is="render(detail)" />
<span v-else class="value">{{ detail[value] }}</span>
</li>
<li class="cp" v-if="!hideCp">
<span v-for="{ label, count } in cp">
<span class="label">{{ label }}</span>
<span class="value">{{ count }} 倍</span>
</span>
</li>
</ul>
</div>
<ul class="gauges">
<li v-for="item in gaugeConfig">
<Gauge v-bind="item" />
</li>
</ul>
</div>
</block-box>
<div class="line-box">
<block-box
:title="title.replace('率', '趋势(%')"
v-for="{ title, data } in gaugeConfig"
>
<template #extra>
<TimeSelect v-model="time" />
</template>
<div style="height: 200px">
<echarts-plus :options="getLineOptions({ data })" />
</div>
</block-box>
</div>
<block-box title="显卡列表" v-if="type !== 'deviceuuid'">
<CardList :hideTitle="true" :filters="filters" />
</block-box>
<block-box title="任务列表">
<TaskList :hideTitle="true" :filters="filters" />
</block-box>
</div>
</template>
<script setup lang="jsx">
import BackHeader from '@/components/BackHeader.vue';
import { useRoute } from 'vue-router';
import BlockBox from '@/components/BlockBox.vue';
import { onMounted, ref, watch } from 'vue';
import CardList from '~/vgpu/views/card/admin/index.vue';
import TaskList from '~/vgpu/views/task/admin/index.vue';
import Gauge from '~/vgpu/components/gauge.vue';
import useInstantVector from '~/vgpu/hooks/useInstantVector';
import EchartsPlus from '@/components/Echarts-plus.vue';
import cardApi from '~/vgpu/api/card';
import { timeParse } from '@/utils';
import { getLineOptions } from '~/vgpu/components/config';
import TimeSelect from '~/vgpu/components/timeSelect.vue';
const props = defineProps([
'title',
'detailColumns',
'type',
'detail',
'name',
'filters',
'hideCp',
]);
const route = useRoute();
const time = ref(1 / 24);
const cp = useInstantVector(
[
{
label: 'vGPU 超配',
count: '0',
query: `avg(hami_vgpu_count{node=~"$node"})`,
},
{
label: '算力超配',
count: '0',
query: `avg(hami_vcore_scaling{node=~"$node"})`,
},
{
label: '显存超配',
count: '1.5',
query: `avg(hami_vmemory_scaling{node=~"$node"})`,
},
],
(query) => query.replaceAll('$node', props.detail.name),
);
const gaugeConfig = useInstantVector(
[
{
title: '算力分配率',
percent: 0,
query: `sum(hami_container_vcore_allocated{${props.type}=~"$${props.type}"})`,
totalQuery: `sum(hami_core_size{${props.type}=~"$${props.type}"})`,
percentQuery: `sum(hami_container_vcore_allocated{${props.type}=~"$${props.type}"}) / sum(hami_core_size{${props.type}=~"$${props.type}"}) *100`,
total: 0,
used: 0,
unit: ' ',
},
{
title: '显存分配率',
percent: 0,
query: `sum(hami_container_vmemory_allocated{${props.type}=~"$${props.type}"}) / 1024`,
totalQuery: `sum(hami_memory_size{${props.type}=~"$${props.type}"}) / 1024`,
percentQuery: `(sum(hami_container_vmemory_allocated{${props.type}=~"$${props.type}"}) / 1024) /(sum(hami_memory_size{${props.type}=~"$${props.type}"}) / 1024) *100`,
total: 0,
used: 0,
unit: 'GiB',
},
{
title: '算力使用率',
percent: 0,
query: `avg(hami_core_util{${props.type}=~"$${props.type}"})`,
percentQuery: `avg(hami_core_util_avg{${props.type}=~"$${props.type}"})`,
totalQuery: `sum(hami_core_size{${props.type}=~"$${props.type}"})`,
total: 100,
used: 0,
unit: ' ',
},
{
title: '显存使用率',
percent: 0,
query: `sum(hami_memory_used{${props.type}=~"$${props.type}"}) / 1024`,
totalQuery: `sum(hami_memory_size{${props.type}=~"$${props.type}"})/1024`,
percentQuery: `(sum(hami_memory_used{${props.type}=~"$${props.type}"}) / 1024)/(sum(hami_memory_size{${props.type}=~"$${props.type}"})/1024)*100`,
total: 0,
used: 0,
unit: 'GiB',
},
],
(query) => query.replaceAll(`$${props.type}`, props.name),
time,
);
// const fetchLineData = async () => {
// const start = new Date();
// start.setTime(start.getTime() - 3600 * 1000 * 24 * time.value);
//
// const lineReqs = gaugeConfig.value.map((item) =>
// cardApi.getRangeVector({
// range: {
// start: timeParse(start),
// end: timeParse(new Date()),
// step: '1m',
// },
// query: item.percentQuery.replaceAll(`$${props.type}`, props.name),
// }),
// );
//
// const res = await Promise.all(lineReqs);
//
// gaugeConfig.value = gaugeConfig.value.map((item, index) => ({
// ...item,
// data: res[index].data[0]?.values || [],
// }));
// };
// watch(
// () => props.detail,
// async () => {
// fetchLineData();
// },
// );
// watch(time, () => {
// fetchLineData();
// });
</script>
<style lang="scss">
.node-detail {
display: flex;
height: 100%;
gap: 50px;
ul {
margin: 0;
padding: 0;
list-style: none;
}
.title {
color: #1d2b3a;
font-family: 'PingFang SC';
font-size: 14px;
font-style: normal;
font-weight: 500;
//line-height: 20px;
margin-bottom: 20px;
}
.node-detail-left {
min-width: 500px;
}
.node-detail-info {
display: flex;
flex-direction: column;
gap: 15px;
font-size: 12px;
.label {
display: inline-block;
width: 80px;
color: #939ea9;
}
.cp {
display: flex;
gap: 25px;
margin-top: 10px;
}
}
.gauges {
flex: 1;
display: flex;
li {
flex: 1;
}
}
}
.line-box {
display: grid;
grid-template-columns: repeat(2, 1fr);
column-gap: 20px;
}
.node-block {
display: flex;
flex-direction: column;
.home-block-content {
flex: 1;
}
}
</style>