From 41810f214d11d14e67b26ceb1352a94704f6a130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=8D=9A=E6=96=87?= <1179111926@qq.com> Date: Fri, 1 Aug 2025 14:47:54 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web/projects/vgpu/api/poll.js | 36 ++ .../projects/vgpu/hooks/useParentAction.js | 22 + packages/web/projects/vgpu/router.js | 6 + .../vgpu/views/monitor/overview/config.js | 193 ++++++-- .../vgpu/views/monitor/overview/index.vue | 163 +++++-- .../projects/vgpu/views/poll/admin/Detail.vue | 438 ++++++++++++++++++ .../vgpu/views/poll/admin/getOptions.js | 120 +++++ .../projects/vgpu/views/poll/admin/index.vue | 233 ++++++++++ .../vgpu/views/poll/admin/searchSchema.js | 40 ++ .../web/projects/vgpu/views/poll/index.vue | 3 + packages/web/src/icons/svg/vgpu-chip.svg | 27 ++ packages/web/src/icons/svg/vgpu-disk.svg | 16 + packages/web/src/icons/svg/vgpu-memory.svg | 25 + packages/web/src/icons/svg/vgpu-pool-tab.svg | 17 + .../web/src/layout/components/AppMain.vue | 6 +- packages/web/src/layout/index.vue | 51 +- packages/web/vue.config.js | 7 + 17 files changed, 1326 insertions(+), 77 deletions(-) create mode 100644 packages/web/projects/vgpu/api/poll.js create mode 100644 packages/web/projects/vgpu/hooks/useParentAction.js create mode 100644 packages/web/projects/vgpu/views/poll/admin/Detail.vue create mode 100644 packages/web/projects/vgpu/views/poll/admin/getOptions.js create mode 100644 packages/web/projects/vgpu/views/poll/admin/index.vue create mode 100644 packages/web/projects/vgpu/views/poll/admin/searchSchema.js create mode 100644 packages/web/projects/vgpu/views/poll/index.vue create mode 100644 packages/web/src/icons/svg/vgpu-chip.svg create mode 100644 packages/web/src/icons/svg/vgpu-disk.svg create mode 100644 packages/web/src/icons/svg/vgpu-memory.svg create mode 100644 packages/web/src/icons/svg/vgpu-pool-tab.svg diff --git a/packages/web/projects/vgpu/api/poll.js b/packages/web/projects/vgpu/api/poll.js new file mode 100644 index 0000000..155737e --- /dev/null +++ b/packages/web/projects/vgpu/api/poll.js @@ -0,0 +1,36 @@ +import request from '@/utils/request'; + +const apiPrefix = '/api/vgpu'; + + +class pollApi { + getPollList(params) { + return request({ + url: apiPrefix + '/v1/resource/pool/list', + method: 'GET', + params, + }); + } + + // getNodes(data) { + // return request({ + // url: apiPrefix + '/v1/nodes', + // method: 'POST', + // data, + // }); + // } + + // getNodeDetail(params) { + // return request({ + // url: apiPrefix + '/v1/node', + // method: 'GET', + // params, + // }); + // } + + // getNodeListReq(data) { + // return request(this.getNodeList(data)); + // } +} + +export default new pollApi(); diff --git a/packages/web/projects/vgpu/hooks/useParentAction.js b/packages/web/projects/vgpu/hooks/useParentAction.js new file mode 100644 index 0000000..dbd9828 --- /dev/null +++ b/packages/web/projects/vgpu/hooks/useParentAction.js @@ -0,0 +1,22 @@ +import { useRouter } from 'vue-router'; + +export default function useParentAction() { + const router = useRouter(); + + const sendRouteChange = (url) => { + if (window.parent !== window) { + // 在 iframe 中,通知父窗口 + const message = { + type: 'ChangeThePath', + data: url + }; + window.parent.postMessage(JSON.stringify(message), '*'); + } else { + // 不在 iframe 中,直接使用 router 跳转 + router.push(url); + } + }; + const hasParentWindow = window.parent !== window; + + return { sendRouteChange, hasParentWindow }; +} \ No newline at end of file diff --git a/packages/web/projects/vgpu/router.js b/packages/web/projects/vgpu/router.js index d5d29e3..ca7f48a 100644 --- a/packages/web/projects/vgpu/router.js +++ b/packages/web/projects/vgpu/router.js @@ -20,6 +20,12 @@ export default (Layout) => ({ name: 'overview', meta: { title: '资源总览', icon: 'dashboard', noCache: true }, }, + { + path: '/admin/vgpu/poll/admin', + component: () => import('~/vgpu/views/poll/admin/index.vue'), + name: 'poll-admin', + meta: { title: '资源池管理', icon: 'vgpu-pool-tab', noCache: true }, + }, { path: '/admin/vgpu/node/admin', component: () => import('~/vgpu/views/node/admin/index.vue'), diff --git a/packages/web/projects/vgpu/views/monitor/overview/config.js b/packages/web/projects/vgpu/views/monitor/overview/config.js index f61321c..5f078ec 100644 --- a/packages/web/projects/vgpu/views/monitor/overview/config.js +++ b/packages/web/projects/vgpu/views/monitor/overview/config.js @@ -11,29 +11,29 @@ export const rangeConfigInit = [ normal: { color: { type: 'linear', - x: 0, // 渐变起始点 0% - y: 0, // 渐变起始点 0% - x2: 0, // 渐变结束点 100% - y2: 1, // 渐变结束点 100% + x: 0, + y: 0, + x2: 0, + y2: 1, colorStops: [ { offset: 0, - color: 'rgba(250, 200, 88, 0.16)', // 渐变起始颜色 + color: 'rgba(250, 200, 88, 0.16)', }, { offset: 1, - color: 'rgba(250, 200, 88, 0.00)', // 渐变结束颜色 + color: 'rgba(250, 200, 88, 0.00)', }, ], - global: false, // 缺省为 false + global: false, }, }, }, itemStyle: { - color: 'rgb(250, 200, 88)', // 设置线条颜色为橙色 + color: 'rgb(250, 200, 88)', }, lineStyle: { - color: 'rgb(250, 200, 88)', // 设置线条颜色为橙色 + color: 'rgb(250, 200, 88)', }, }, { @@ -45,65 +45,134 @@ export const rangeConfigInit = [ normal: { color: { type: 'linear', - x: 0, // 渐变起始点 0% - y: 0, // 渐变起始点 0% - x2: 0, // 渐变结束点 100% - y2: 1, // 渐变结束点 100% + x: 0, + y: 0, + x2: 0, + y2: 1, colorStops: [ { offset: 0, - color: 'rgba(84, 112, 198, 0.16)', // 渐变起始颜色 + color: 'rgba(84, 112, 198, 0.16)', }, { offset: 1, - color: 'rgba(84, 112, 198, 0.00)', // 渐变结束颜色 + color: 'rgba(84, 112, 198, 0.00)', }, ], - global: false, // 缺省为 false + global: false, }, }, }, itemStyle: { - color: 'rgb(84, 112, 198)', // 设置线条颜色为橙色 + color: 'rgb(84, 112, 198)', }, lineStyle: { - color: 'rgb(84, 112, 198)', // 设置线条颜色为橙色 + color: 'rgb(84, 112, 198)', }, }, { name: '显存', query: `sum(hami_container_vmemory_allocated) / sum(hami_memory_size) * 100`, data: [], + type: 'line', areaStyle: { normal: { color: { type: 'linear', - x: 0, // 渐变起始点 0% - y: 0, // 渐变起始点 0% - x2: 0, // 渐变结束点 100% - y2: 1, // 渐变结束点 100% + x: 0, + y: 0, + x2: 0, + y2: 1, colorStops: [ { offset: 0, - color: 'rgba(34, 139, 34, 0.16)', // 渐变起始颜色 + color: 'rgba(34, 139, 34, 0.16)', }, { offset: 1, - color: 'rgba(34, 139, 34, 0.00)', // 渐变结束颜色 + color: 'rgba(34, 139, 34, 0.00)', }, ], - global: false, // 缺省为 false + global: false, }, }, }, itemStyle: { - color: 'rgb(145, 204, 117)', // 设置线条颜色为橙色 + color: 'rgb(145, 204, 117)', }, lineStyle: { - color: 'rgb(145, 204, 117)', // 设置线条颜色为橙色 + color: 'rgb(145, 204, 117)', }, }, - ], + { + name: 'CPU', + query: `sum(hami_container_cpu_allocated) / sum(hami_cpu_count) * 100`, + data: [], + type: 'line', + areaStyle: { + normal: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0, + color: 'rgba(255, 99, 71, 0.16)', + }, + { + offset: 1, + color: 'rgba(255, 99, 71, 0.00)', + }, + ], + global: false, + }, + }, + }, + itemStyle: { + color: 'rgb(255, 99, 71)', + }, + lineStyle: { + color: 'rgb(255, 99, 71)', + }, + }, + { + name: '内存', + query: `sum(hami_container_memory_allocated) / sum(hami_memory_capacity) * 100`, + data: [], + type: 'line', + areaStyle: { + normal: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0, + color: 'rgba(138, 43, 226, 0.16)', + }, + { + offset: 1, + color: 'rgba(138, 43, 226, 0.00)', + }, + ], + global: false, + }, + }, + }, + itemStyle: { + color: 'rgb(138, 43, 226)', + }, + lineStyle: { + color: 'rgb(138, 43, 226)', + }, + } + ] }, { title: '资源使用趋势', @@ -174,6 +243,74 @@ export const rangeConfigInit = [ color: 'rgb(145, 204, 117)', // 设置线条颜色为橙色 }, }, + { + name: 'CPU', + query: `sum(hami_container_cpu_allocated) / sum(hami_cpu_count) * 100`, + data: [], + type: 'line', + areaStyle: { + normal: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0, + color: 'rgba(255, 99, 71, 0.16)', + }, + { + offset: 1, + color: 'rgba(255, 99, 71, 0.00)', + }, + ], + global: false, + }, + }, + }, + itemStyle: { + color: 'rgb(255, 99, 71)', + }, + lineStyle: { + color: 'rgb(255, 99, 71)', + }, + }, + { + name: '内存', + query: `sum(hami_container_memory_allocated) / sum(hami_memory_capacity) * 100`, + data: [], + type: 'line', + areaStyle: { + normal: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0, + color: 'rgba(138, 43, 226, 0.16)', + }, + { + offset: 1, + color: 'rgba(138, 43, 226, 0.00)', + }, + ], + global: false, + }, + }, + }, + itemStyle: { + color: 'rgb(138, 43, 226)', + }, + lineStyle: { + color: 'rgb(138, 43, 226)', + }, + } ], }, ]; diff --git a/packages/web/projects/vgpu/views/monitor/overview/index.vue b/packages/web/projects/vgpu/views/monitor/overview/index.vue index 5e54652..8ee6f77 100644 --- a/packages/web/projects/vgpu/views/monitor/overview/index.vue +++ b/packages/web/projects/vgpu/views/monitor/overview/index.vue @@ -3,28 +3,30 @@
-
+
+ +
+
+
+
    -
  • +
  • @@ -44,28 +46,21 @@ - +
    -
  • +
  • {{ title }}
    {{ count }} @@ -75,7 +70,7 @@ @@ -84,15 +79,9 @@
    - - + +
@@ -114,6 +103,7 @@ 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'; @@ -128,10 +118,12 @@ 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) => { - router.push(`/admin/vgpu/card/admin?type=${params.data.name}`); + sendRouteChange(`/admin/vgpu/card/admin?type=${params.data.name}`); }; @@ -139,6 +131,36 @@ const alarmData = ref([]) const chartWidth = ref(200); const cardGaugeConfig = useInstantVector([ + { + title: 'CPU 使用率', + 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_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: 'GiB', + }, + { + title: '磁盘 使用率', + 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: 'vGPU 分配率', percent: 0, @@ -197,12 +219,39 @@ const resourceOverview = ref([ count: 0, icon: 'vgpu-node', unit: '个', + to: '/admin/vgpu/node/admin' + }, + { + title: '资源池', + count: 0, + icon: 'vgpu-pool-tab', + unit: '个', + to: '/admin/vgpu/resource/admin' + }, + { + title: 'CPU', + count: 0, + icon: 'vgpu-chip', + unit: '核', + }, + { + title: '内存', + count: 0, + icon: 'vgpu-memory', + unit: 'GIB', + }, + { + title: '磁盘', + count: 0, + icon: 'vgpu-disk', + unit: '个', }, { title: '显卡', count: 0, icon: 'vgpu-gpu-d', unit: '张', + to: '/admin/vgpu/card/admin' }, { title: 'vGPU', @@ -327,6 +376,20 @@ const nodeUsedTop = { title: '节点资源使用率 Top5', key: 'used', config: [ + { + tab: 'GPU', + key: 'gpu', + nameKey: 'node', + data: [], + query: 'topk(5, avg(hami_core_util_avg) by (node))', + }, + { + tab: '内存', + key: 'internal', + nameKey: 'node', + data: [], + query: 'topk(5, avg(hami_core_util_avg) by (node))', + }, { tab: '算力', key: 'core', @@ -349,6 +412,20 @@ const nodeTotalTop = { title: '节点资源分配率 Top5', key: 'used', config: [ + { + tab: 'GPU', + key: 'gpu', + nameKey: 'node', + data: [], + query: `topk(5, avg(hami_container_vgpu_allocated{}) by (node) / avg(hami_vgpu_count{}) by (node) * 100)`, + }, + { + tab: '内存', + key: 'internal', + nameKey: 'node', + data: [], + query: `topk(5, avg(hami_container_vgpu_allocated{}) by (node) / avg(hami_vgpu_count{}) by (node) * 100)`, + }, { tab: 'vGPU', key: 'vgpu', @@ -402,22 +479,23 @@ const fetchRangeData = () => { cardApi - .getRangeVector({ - ...params, - query: `sum({__name__=~"alert:.*:count"})`, - }) - .then((res) => { - alarmData.value = res.data[0].values; - }); + .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 = cardData.value.length; - resourceOverview.value[2].count = cardGaugeConfig.value[0].total; - resourceOverview.value[3].count = cardGaugeConfig.value[1].total; - resourceOverview.value[4].count = cardGaugeConfig.value[2].total.toFixed(0); + + 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 () => { @@ -479,6 +557,7 @@ watch( height: 190px; display: grid; grid-template-columns: repeat(5, 1fr); + .gauge-info { margin-top: 10px; } diff --git a/packages/web/projects/vgpu/views/poll/admin/Detail.vue b/packages/web/projects/vgpu/views/poll/admin/Detail.vue new file mode 100644 index 0000000..bfc5488 --- /dev/null +++ b/packages/web/projects/vgpu/views/poll/admin/Detail.vue @@ -0,0 +1,438 @@ + + + + + diff --git a/packages/web/projects/vgpu/views/poll/admin/getOptions.js b/packages/web/projects/vgpu/views/poll/admin/getOptions.js new file mode 100644 index 0000000..1bce542 --- /dev/null +++ b/packages/web/projects/vgpu/views/poll/admin/getOptions.js @@ -0,0 +1,120 @@ +import { timeParse } from '@/utils'; + +export const getRangeOptions = ({ core = [], memory = [] }) => { + return { + legend: { + // data: [], + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross', + }, + formatter: function (params) { + var res = params[0].name + '
'; + for (var i = 0; i < params.length; i++) { + res += + params[i].marker + + params[i].seriesName + + ' : ' + + (+params[i].value).toFixed(0) + + `%
`; + } + + return res; + }, + }, + grid: { + top: 37, // 上边距 + bottom: 20, // 下边距 + left: '7%', // 左边距 + right: 10, // 右边距 + }, + xAxis: { + type: 'category', + data: core.map((item) => timeParse(+item.timestamp)), + axisLabel: { + formatter: function (value) { + return timeParse(value, 'HH:mm'); + }, + }, + }, + yAxis: { + type: 'value', + // max: 100, + axisLabel: { + formatter: function (value) { + return `${value} %`; + }, + }, + }, + series: [ + { + name: '算力', + data: core, + type: 'line', + areaStyle: { + normal: { + color: { + type: 'linear', + x: 0, // 渐变起始点 0% + y: 0, // 渐变起始点 0% + x2: 0, // 渐变结束点 100% + y2: 1, // 渐变结束点 100% + colorStops: [ + { + offset: 0, + color: 'rgba(84, 112, 198, 0.16)', // 渐变起始颜色 + }, + { + offset: 1, + color: 'rgba(84, 112, 198, 0.00)', // 渐变结束颜色 + }, + ], + global: false, // 缺省为 false + }, + }, + }, + itemStyle: { + color: 'rgb(84, 112, 198)', // 设置线条颜色为橙色 + }, + lineStyle: { + color: 'rgb(84, 112, 198)', // 设置线条颜色为橙色 + }, + }, + { + name: '显存', + data: memory, + type: 'line', + areaStyle: { + normal: { + color: { + type: 'linear', + x: 0, // 渐变起始点 0% + y: 0, // 渐变起始点 0% + x2: 0, // 渐变结束点 100% + y2: 1, // 渐变结束点 100% + colorStops: [ + { + offset: 0, + color: 'rgba(34, 139, 34, 0.16)', // 渐变起始颜色 + }, + { + offset: 1, + color: 'rgba(34, 139, 34, 0.00)', // 渐变结束颜色 + }, + ], + global: false, // 缺省为 false + }, + }, + }, + itemStyle: { + color: 'rgb(145, 204, 117)', // 设置线条颜色为橙色 + }, + lineStyle: { + color: 'rgb(145, 204, 117)', // 设置线条颜色为橙色 + }, + }, + ], + }; +}; diff --git a/packages/web/projects/vgpu/views/poll/admin/index.vue b/packages/web/projects/vgpu/views/poll/admin/index.vue new file mode 100644 index 0000000..7ebd122 --- /dev/null +++ b/packages/web/projects/vgpu/views/poll/admin/index.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/packages/web/projects/vgpu/views/poll/admin/searchSchema.js b/packages/web/projects/vgpu/views/poll/admin/searchSchema.js new file mode 100644 index 0000000..e19bbbb --- /dev/null +++ b/packages/web/projects/vgpu/views/poll/admin/searchSchema.js @@ -0,0 +1,40 @@ +import api from '~/vgpu/api/card'; + +export default { + items: [ + { + label: 'IP', + name: 'ip', + component: 'input', + }, + { + label: '节点状态', + name: 'isSchedulable', + component: 'select', + props: { + mode: 'static', + options: [ + { + label: '可调度', + value: 'true', + }, + { + label: '禁止调度', + value: 'false', + }, + ], + }, + }, + { + label: '显卡型号', + name: 'type', + component: 'select', + props: { + mode: 'remote', + api: api.getCardType(), + labelKey: 'type', + valueKey: 'type', + }, + }, + ], +}; diff --git a/packages/web/projects/vgpu/views/poll/index.vue b/packages/web/projects/vgpu/views/poll/index.vue new file mode 100644 index 0000000..a44ab87 --- /dev/null +++ b/packages/web/projects/vgpu/views/poll/index.vue @@ -0,0 +1,3 @@ + diff --git a/packages/web/src/icons/svg/vgpu-chip.svg b/packages/web/src/icons/svg/vgpu-chip.svg new file mode 100644 index 0000000..39e9e6c --- /dev/null +++ b/packages/web/src/icons/svg/vgpu-chip.svg @@ -0,0 +1,27 @@ + + + CPU + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/web/src/icons/svg/vgpu-disk.svg b/packages/web/src/icons/svg/vgpu-disk.svg new file mode 100644 index 0000000..7808cbd --- /dev/null +++ b/packages/web/src/icons/svg/vgpu-disk.svg @@ -0,0 +1,16 @@ + + + 磁盘 + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/web/src/icons/svg/vgpu-memory.svg b/packages/web/src/icons/svg/vgpu-memory.svg new file mode 100644 index 0000000..368afe8 --- /dev/null +++ b/packages/web/src/icons/svg/vgpu-memory.svg @@ -0,0 +1,25 @@ + + + 内存 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/web/src/icons/svg/vgpu-pool-tab.svg b/packages/web/src/icons/svg/vgpu-pool-tab.svg new file mode 100644 index 0000000..890a876 --- /dev/null +++ b/packages/web/src/icons/svg/vgpu-pool-tab.svg @@ -0,0 +1,17 @@ + + + 资源池 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/web/src/layout/components/AppMain.vue b/packages/web/src/layout/components/AppMain.vue index ed76992..da29c01 100644 --- a/packages/web/src/layout/components/AppMain.vue +++ b/packages/web/src/layout/components/AppMain.vue @@ -1,5 +1,5 @@