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 @@
-
-
-
+
+
@@ -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 @@
+
+
+
+ 节点管理 > {{ detail.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
详细信息
+
+ -
+ {{ label }}
+
+ {{ detail[value] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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..023b088
--- /dev/null
+++ b/packages/web/projects/vgpu/views/poll/admin/index.vue
@@ -0,0 +1,212 @@
+
+
+
+ 创建资源池
+
+
+
+
+
+
+
{{ poolName }}
+
+ 节点数量 {{ nodeNum }}
+ CPU数 {{ cpuCores }}核
+ 显卡数量 {{ gpuNum }}张
+ 可用/总内存 {{ bytesToGB(availableMemory) }}GB / {{ bytesToGB(totalMemory) }}GB
+ 磁盘大小 {{ bytesToGB(diskSize) }}GB
+
+
+
+ 查看详情
+
+ 配置
+
+
+ 编辑
+ 删除
+
+
+
+
+
+
+
+ 资源池名称
+
+
+
+ 选择节点
+ 已选{{ nodeSelect.length
+ }}个节点
+
+
+
+
+
+
+ {{ nodeIp }}
+
+
+
显卡数量{{ gpuNum }}张
+
显卡大小{{ bytesToGB(gpuMemory) }}GB
+
内存大小{{ bytesToGB(totalMemory) }}GB
+
磁盘大小{{ bytesToGB(diskSize) }}GB
+
CPU{{ cpuCores }}核
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ nodeIp }}
+
+
+
显卡数量{{ gpuNum }}张
+
显卡大小{{ bytesToGB(gpuMemory) }}GB
+
内存大小{{ bytesToGB(totalMemory) }}GB
+
磁盘大小{{ bytesToGB(diskSize) }}GB
+
CPU{{ cpuCores }}核
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@
+
+
\ 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 @@
-