Browse Source

Resources Management

tags/v1.22.8.2^2
chenshihai 3 years ago
parent
commit
3523cc0aa2
6 changed files with 321 additions and 78 deletions
  1. +50
    -7
      web_src/vuepages/apis/modules/resources.js
  2. +1
    -0
      web_src/vuepages/const/index.js
  3. +120
    -27
      web_src/vuepages/pages/resources/components/SpecificationDialog.vue
  4. +1
    -1
      web_src/vuepages/pages/resources/queue/index.vue
  5. +32
    -3
      web_src/vuepages/pages/resources/scene/index.vue
  6. +117
    -40
      web_src/vuepages/pages/resources/specification/index.vue

+ 50
- 7
web_src/vuepages/apis/modules/resources.js View File

@@ -1,12 +1,5 @@
import service from '../service';

// 查询资源队列列表
// page 当前页数,从1开始
// cluster 所属集群 :OpenI 启智集群,C2Net 智算集群
// center 智算中心:OpenIOne 云脑一,OpenITwo 云脑二, chendu 成都人工智能计算中心, pclcci 鹏城云计算所 ,hefei 合肥类脑类脑智能开放平台, xuchang 中原人工智能计算中心
// resource 计算资源: GPU NPU
// card XPU类型: T4、A100、V100、Ascend 910

export const getQueueList = (params) => {
return service({
url: '/admin/resources/queue',
@@ -15,6 +8,12 @@ export const getQueueList = (params) => {
});
}

// 查询资源队列列表
// page 当前页数,从1开始
// cluster 所属集群 :OpenI 启智集群,C2Net 智算集群
// center 智算中心:OpenIOne 云脑一,OpenITwo 云脑二, chendu 成都人工智能计算中心, pclcci 鹏城云计算所 ,hefei 合肥类脑类脑智能开放平台, xuchang 中原人工智能计算中心
// resource 计算资源: GPU NPU
// card XPU类型: T4、A100、V100、Ascend 910
export const getResQueueList = (params) => {
return service({
url: '/admin/resources/queue/list',
@@ -43,3 +42,47 @@ export const updateResQueue = (data) => { // CardsTotalNum,Remark
});
}

// 查询所有资源队列名称列表
export const getResQueueCode = () => {
return service({
url: '/admin/resources/queue/codes',
method: 'get',
params: {},
data: {},
});
}

// 新增资源规格
export const addResSpecification = (data) => {
return service({
url: '/admin/resources/specification/add',
method: 'post',
params: {},
data,
});
}

// 更新资源规格
// params: action edit-编辑 on-shelf 上架 off-shelf 下架
// data: UnitPrice
export const updateResSpecification = (data) => { // data => { ID: 1, action: 'edit|on-shelf|off-shelf', UnitPrice: 1 | undefined }
return service({
url: `/admin/resources/specification/update/${data.ID}`,
method: 'post',
params: { action: data.action },
data: { UnitPrice: data.action === 'edit' ? data.UnitPrice : undefined }
});
}

// 查询资源队列列表
// page
// queue 所属队列id
// status 状态 : 1 待审核 2已上架 3已下架
export const getResSpecificationList = (params) => {
return service({
url: '/admin/resources/specification/list',
method: 'get',
params,
data: {},
});
}

+ 1
- 0
web_src/vuepages/const/index.js View File

@@ -13,3 +13,4 @@ export const CLUSTERS = [{ k: 'OpenI', v: '启智集群' }, { k: 'C2Net', v: '
export const AI_CENTER = [{ k: 'OpenIOne', v: '云脑一' }, { k: 'OpenITwo', v: '云脑二' }, { k: 'chendu', v: '成都人工智能计算中心' }, { k: 'pclcci', v: '鹏城云计算所' }, { k: 'hefei', v: '合肥类脑类脑智能开放平台' }, { k: 'xuchang', v: '中原人工智能计算中心' }];
export const COMPUTER_RESOURCES = [{ k: 'GPU', v: 'GPU' }, { k: 'NPU', v: 'NPU' }];
export const ACC_CARD_TYPE = [{ k: 'T4', v: 'T4' }, { k: 'A100', v: 'A100' }, { k: 'V100', v: 'V100' }, { k: 'Ascend910', v: 'Ascend 910' }];
export const SPECIFICATION_STATUS = [{ k: '1', v: '待上架' }, { k: '2', v: '已上架' }, { k: '3', v: '已下架' }];

+ 120
- 27
web_src/vuepages/pages/resources/components/SpecificationDialog.vue View File

@@ -1,7 +1,7 @@
<template>
<div class="base-dlg">
<BaseDialog :visible.sync="dialogShow" :width="`700px`" :title="`新增资源规格和单价信息`" @open="open" @opened="opened"
@close="close" @closed="closed">
<BaseDialog :visible.sync="dialogShow" :width="`700px`" :title="type === 'add' ? '新增资源规格和单价信息' : '编辑资源规格和单价信息'"
@open="open" @opened="opened" @close="close" @closed="closed">
<div class="dlg-content">
<div class="form">
<div class="form-row">
@@ -9,15 +9,27 @@
<span>资源池(队列)</span>
</div>
<div class="content">
<el-select v-model="dataInfo.queue"></el-select>
<el-select v-model="dataInfo.QueueId" :disabled="type === 'edit'">
<el-option v-for="item in this.queueList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row">
<div class="title">
<span>对应资源编码</span>
</div>
<div class="content">
<el-input v-model="dataInfo.SourceSpecId" placeholder="云脑II需要填写对应的资源编码" :disabled="type === 'edit'">
</el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>XPU数</span>
<span>数</span>
</div>
<div class="content">
<el-input v-model="dataInfo.xpu" type="number" placeholder=""></el-input>
<el-input v-model="dataInfo.AccCardsNum" type="number" placeholder="" :disabled="type === 'edit'">
</el-input>
</div>
</div>
<div class="form-row">
@@ -25,7 +37,16 @@
<span>CPU数</span>
</div>
<div class="content">
<el-input v-model="dataInfo.cpu" type="number" placeholder=""></el-input>
<el-input v-model="dataInfo.CpuCores" type="number" placeholder="" :disabled="type === 'edit'"></el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>显存(GB)</span>
</div>
<div class="content">
<el-input v-model="dataInfo.GPUMemGiB" type="number" placeholder="" :disabled="type === 'edit'">
</el-input>
</div>
</div>
<div class="form-row">
@@ -33,7 +54,7 @@
<span>内存(GB)</span>
</div>
<div class="content">
<el-input v-model="dataInfo.mem" type="number" placeholder=""></el-input>
<el-input v-model="dataInfo.MemGiB" type="number" placeholder="" :disabled="type === 'edit'"></el-input>
</div>
</div>
<div class="form-row">
@@ -41,15 +62,16 @@
<span>共享内存(GB)</span>
</div>
<div class="content">
<el-input v-model="dataInfo.shareMem" type="number" placeholder=""></el-input>
<el-input v-model="dataInfo.ShareMemGiB" type="number" placeholder="" :disabled="type === 'edit'">
</el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>单价</span>
<span>单价(积分/时)</span>
</div>
<div class="content">
<el-input v-model="dataInfo.price" type="number" placeholder=""></el-input>
<el-input v-model="dataInfo.UnitPrice" type="number" placeholder=""></el-input>
</div>
</div>
<div class="form-row">
@@ -57,7 +79,9 @@
<span>状态</span>
</div>
<div class="content">
<el-select v-model="dataInfo.status"></el-select>
<el-select v-model="dataInfo.Status" :disabled="type === 'edit'">
<el-option v-for="item in this.statusList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row" style="margin-top: 20px">
@@ -74,7 +98,9 @@
</template>
<script>
import BaseDialog from '~/components/BaseDialog.vue';
import { getQueueList } from '~/apis/modules/resources';
import { getResQueueCode, addResSpecification, updateResSpecification } from '~/apis/modules/resources';
import { SPECIFICATION_STATUS, CLUSTERS, AI_CENTER } from '~/const';
import { getListValueWithKey } from '~/utils';

export default {
name: "SpecificationDialog",
@@ -90,9 +116,11 @@ export default {
data() {
return {
dialogShow: false,
dataInfo: {

},
dataInfo: {},
queueList: [],
statusList: [...SPECIFICATION_STATUS],
clusterList: [...CLUSTERS],
aiCenterList: [...AI_CENTER],
};
},
watch: {
@@ -103,25 +131,43 @@ export default {
methods: {
resetDataInfo() {
this.dataInfo = {
queue: '',
queueList: [],
xpu: '',
cpu: '',
mem: '',
shareMem: '',
price: '',
status: '',
statusList: [],
QueueId: '',
AccCardsNum: '',
CpuCores: '',
MemGiB: '',
ShareMemGiB: '',
GPUMemGiB: '',
UnitPrice: '',
Status: '1',
}
},
getQueueList() {
getResQueueCode().then(res => {
res = res.data;
if (res.Code === 0) {
const data = res.Data;
const list = [];
for (let i = 0, iLen = data.length; i < iLen; i++) {
const item = data[i];
list.push({
k: item.ID,
v: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${getListValueWithKey(this.aiCenterList, item.AiCenterCode)})`,
});
}
this.queueList.splice(0, Infinity, ...list);
}
}).catch(err => {
console.log(err);
});
},
open() {
this.resetDataInfo();
this.getQueueList();
if (this.type === 'add') {
//
} else if (this.type === 'edit') {
this.dataInfo = Object.assign(this.dataInfo, { ...this.data });
this.dataInfo = Object.assign(this.dataInfo, { ...this.data, Status: '2' });
}
console.log('open', this.type, this.data);
this.$emit("open");
},
opened() {
@@ -135,7 +181,54 @@ export default {
this.$emit("update:visible", false);
},
confirm() {

if (!this.dataInfo.QueueId || !this.dataInfo.AccCardsNum || !this.dataInfo.CpuCores || !this.dataInfo.MemGiB || !this.dataInfo.ShareMemGiB || !this.dataInfo.GPUMemGiB
|| !this.dataInfo.UnitPrice || !this.dataInfo.Status
) {
this.$message({
type: 'info',
message: '请先完善信息!'
});
return;
}
if (parseInt(this.dataInfo.AccCardsNum) != Number(this.dataInfo.AccCardsNum)) {
this.$message({
type: 'info',
message: '请输入正整数的卡数!'
});
return;
}
const setApi = this.type === 'add' ? addResSpecification : updateResSpecification;
setApi({
...this.dataInfo,
action: this.type === 'edit' ? 'edit' : undefined,
AccCardsNum: Number(this.dataInfo.AccCardsNum),
CpuCores: Number(this.dataInfo.CpuCores),
MemGiB: Number(this.dataInfo.MemGiB),
ShareMemGiB: Number(this.dataInfo.ShareMemGiB),
GPUMemGiB: Number(this.dataInfo.GPUMemGiB),
UnitPrice: Number(this.dataInfo.UnitPrice),
Status: Number(this.dataInfo.Status),
}).then(res => {
res = res.data;
if (res.Code === 0) {
this.$message({
type: 'success',
message: '提交成功!'
});
this.$emit("confirm");
} else {
this.$message({
type: 'error',
message: '提交失败!'
});
}
}).catch(err => {
console.log(err);
this.$message({
type: 'error',
message: '提交失败!'
});
})
},
cancel() {
this.dialogShow = false;


+ 1
- 1
web_src/vuepages/pages/resources/queue/index.vue View File

@@ -108,7 +108,7 @@ export default {
center: this.selComputingCenter,
resource: this.selComputingType,
card: this.selCardType,
curpage: this.pageInfo.curpage,
page: this.pageInfo.curpage,
pagesize: this.pageInfo.pageSize,
};
this.loading = true;


+ 32
- 3
web_src/vuepages/pages/resources/scene/index.vue View File

@@ -9,6 +9,9 @@
<el-select class="select" size="medium" v-model="selIsExclusive" @change="selectChange">
<el-option v-for="item in isExclusiveList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selAiCenter" @change="selectChange">
<el-option v-for="item in aiCenterList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selQueue" @change="selectChange">
<el-option v-for="item in queueList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
@@ -62,7 +65,10 @@

<script>
import SceneDialog from '../components/SceneDialog.vue';
import { getQueueList } from '~/apis/modules/resources';
import { getQueueList, getResQueueCode } from '~/apis/modules/resources';
import { CLUSTERS, AI_CENTER, ACC_CARD_TYPE } from '~/const';
import { getListValueWithKey } from '~/utils';
import { formatDate } from 'element-ui/lib/utils/date-util';

export default {
data() {
@@ -72,7 +78,10 @@ export default {
selIsExclusive: '',
isExclusiveList: [{ k: '', v: '全部专属和通用' }, { k: '1', v: '专属' }, { k: '2', v: '通用' }],
selQueue: '',
queueList: [{ k: '', v: '全部资源池队列' }, { k: '1', v: '资源池1' }, { k: '2', v: '资源池2' }],
queueList: [{ k: '', v: '全部资源池队列' }],
clusterList: [...CLUSTERS],
selAiCenter: '',
aiCenterList: [{ k: '', v: '全部智算中心' }, ...AI_CENTER],
loading: false,
tableData: [],
pageInfo: {
@@ -88,12 +97,31 @@ export default {
},
components: { SceneDialog },
methods: {
getQueueList() {
getResQueueCode().then(res => {
res = res.data;
if (res.Code === 0) {
const data = res.Data;
const list = [];
for (let i = 0, iLen = data.length; i < iLen; i++) {
const item = data[i];
list.push({
k: item.ID,
v: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${getListValueWithKey(this.aiCenterList, item.AiCenterCode)})`,
});
}
this.queueList.push(...list);
}
}).catch(err => {
console.log(err);
});
},
getTableData() {
const params = {
taskType: this.selTaskType,
isExclusive: this.selIsExclusive,
queue: this.selQueue,
curpage: this.pageInfo.curpage,
page: this.pageInfo.curpage,
pagesize: this.pageInfo.pageSize,
};
console.log('params', params);
@@ -157,6 +185,7 @@ export default {
}
},
mounted() {
this.getQueueList();
this.getTableData();
},
beforeDestroy() {


+ 117
- 40
web_src/vuepages/pages/resources/specification/index.vue View File

@@ -19,25 +19,43 @@
<div class="table-container">
<div style="min-height:600px;">
<el-table border :data="tableData" style="width: 100%" v-loading="loading" stripe>
<el-table-column prop="id" label="ID" align="center" header-align="center" width="100"></el-table-column>
<el-table-column prop="name" label="资源规格" align="left" header-align="center"></el-table-column>
<el-table-column prop="queue" label="资源池(队列)" align="center" header-align="center"></el-table-column>
<el-table-column prop="specID" label="智算网络资源规格ID" align="center" header-align="center"></el-table-column>
<el-table-column prop="cardNum" label="卡数 " align="center" header-align="center"></el-table-column>
<el-table-column prop="cpu" label="CPU " align="center" header-align="center"></el-table-column>
<el-table-column prop="mem" label="内存(GB) " align="center" header-align="center"></el-table-column>
<el-table-column prop="shareMem" label="共享内存(GB)" align="center" header-align="center"></el-table-column>
<el-table-column prop="updateTime" label="最后更新时间" align="center" header-align="center"></el-table-column>
<el-table-column prop="price" label="单价(积分/时)" align="center" header-align="center"></el-table-column>
<el-table-column prop="status" label="状态" align="center" header-align="center">
<el-table-column prop="ID" label="ID" align="center" header-align="center" width="60"></el-table-column>
<el-table-column prop="SpecStr" label="资源规格" align="left" header-align="center" min-width="160">
</el-table-column>
<el-table-column prop="QueueInfo" label="资源池(队列)" align="center" header-align="center" min-width="100">
</el-table-column>
<el-table-column prop="SourceSpecId" label="智算网络资源规格ID" align="center" header-align="center">
</el-table-column>
<el-table-column prop="AccCardsNum" label="卡数 " align="center" header-align="center"></el-table-column>
<el-table-column prop="CpuCores" label="CPU " align="center" header-align="center"></el-table-column>
<el-table-column prop="GPUMemGiB" label="显存(GB)" align="center" header-align="center"></el-table-column>
<el-table-column prop="MemGiB" label="内存(GB) " align="center" header-align="center"></el-table-column>
<el-table-column prop="ShareMemGiB" label="共享内存(GB)" align="center" header-align="center"></el-table-column>
<el-table-column prop="UpdatedTimeStr" label="最后更新时间" align="center" header-align="center"></el-table-column>
<el-table-column prop="UnitPrice" label="单价(积分/时)" align="center" header-align="center">
<template slot-scope="scope">
{{ scope.row.status }}
<span style="font-weight:600;font-size:14px;">{{ scope.row.UnitPrice }}</span>
</template>
</el-table-column>
<el-table-column prop="StatusStr" label="状态" align="center" header-align="center" width="100">
<template slot-scope="scope">
<span :style="{ color: scope.row.Status == '2' ? 'rgb(82, 196, 26)' : 'rgb(245, 34, 45)' }">{{
scope.row.StatusStr
}}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" header-align="center" width="100">
<template slot-scope="scope">
<span class="op-btn" @click="showDialog('edit', scope.row)">定价</span>
<span class="op-btn" @click="showDialog('edit', scope.row)">上架</span>
<span v-if="scope.row.Status == '1'">
<span class="op-btn" @click="showDialog('edit', scope.row)">定价上架</span>
</span>
<span v-if="scope.row.Status == '2'">
<span class="op-btn" @click="showDialog('edit', scope.row)">修改</span>
<span class="op-btn" @click="onOrOffShelf('off-shelf', scope.row)">下架</span>
</span>
<span v-if="scope.row.Status == '3'">
<span class="op-btn" @click="onOrOffShelf('on-shelf', scope.row)">上架</span>
</span>
</template>
</el-table-column>
<template slot="empty">
@@ -65,15 +83,21 @@

<script>
import SpecificationDialog from '../components/SpecificationDialog.vue';
import { getQueueList } from '~/apis/modules/resources';
import { getResQueueCode, getResSpecificationList, updateResSpecification } from '~/apis/modules/resources';
import { SPECIFICATION_STATUS, CLUSTERS, AI_CENTER, ACC_CARD_TYPE } from '~/const';
import { getListValueWithKey } from '~/utils';
import { formatDate } from 'element-ui/lib/utils/date-util';

export default {
data() {
return {
selQueue: '',
queueList: [{ k: '', v: '全部资源池' }, { k: '1', v: '资源池1' }, { k: '2', v: '资源池2' }],
queueList: [{ k: '', v: '全部资源池' }],
selStatus: '',
statusList: [{ k: '', v: '全部状态' }, { k: '1', v: '待上架' }, { k: '2', v: '已上架' }, { k: '3', v: '已下架' }],
statusList: [{ k: '', v: '全部状态' }, ...SPECIFICATION_STATUS],
clusterList: [...CLUSTERS],
aiCenterList: [...AI_CENTER],
accCardTypeList: [...ACC_CARD_TYPE],
syncLoading: false,
loading: false,
tableData: [],
@@ -90,36 +114,55 @@ export default {
},
components: { SpecificationDialog },
methods: {
getQueueList() {
getResQueueCode().then(res => {
res = res.data;
if (res.Code === 0) {
const data = res.Data;
const list = [];
for (let i = 0, iLen = data.length; i < iLen; i++) {
const item = data[i];
list.push({
k: item.ID,
v: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${getListValueWithKey(this.aiCenterList, item.AiCenterCode)})`,
});
}
this.queueList.push(...list);
}
}).catch(err => {
console.log(err);
});
},
getTableData() {
const params = {
queue: this.selQueue,
status: this.selStatus,
curpage: this.pageInfo.curpage,
page: this.pageInfo.curpage,
pagesize: this.pageInfo.pageSize,
};
console.log('params', params);
this.loading = true;
getQueueList(params).then(res => {
getResSpecificationList(params).then(res => {
this.loading = false;
// const data = res.data.
const data = new Array(20).fill(0).map((_, index) => {
return {
index: index,
id: `id-${index}-${Math.random().toFixed(2)}`,
name: `name-${index}-${Math.random().toFixed(2)}`,
queue: `queue-${index}-${Math.random().toFixed(2)}`,
specID: `specID-${index}-${Math.random().toFixed(2)}`,
cardNum: `cardNum-${index}-${Math.random().toFixed(2)}`,
cpu: `cpu-${index}-${Math.random().toFixed(2)}`,
mem: `mem-${index}-${Math.random().toFixed(2)}`,
shareMem: `shareMem-${index}-${Math.random().toFixed(2)}`,
updateTime: `updateTime-${index}-${Math.random().toFixed(2)}`,
price: `price-${index}-${Math.random().toFixed(2)}`,
status: `status-${index}-${Math.random().toFixed(2)}`,
};
});
this.tableData = data;
this.pageInfo.total = 99;
res = res.data;
if (res.Code === 0) {
const list = res.Data.List;
const data = list.map((item) => {
const Queue = item.Queue;
const Spec = item.Spec;
const NGPU = `${Queue.ComputeResource}:${Spec.AccCardsNum === 0 ? '0' : Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Queue.AccCardType)}`;
return {
...Spec,
SpecStr: `${NGPU}, CPU:${Spec.CpuCores}, 显存:${Spec.GPUMemGiB}GB, 内存:${Spec.MemGiB}GB, 共享内存:${Spec.ShareMemGiB}GB`,
QueueId: Queue.ID,
QueueInfo: `${Queue.QueueCode}(${getListValueWithKey(this.clusterList, Queue.Cluster)} - ${getListValueWithKey(this.aiCenterList, Queue.AiCenterCode)})`,
UpdatedTimeStr: formatDate(new Date(Spec.UpdatedTime * 1000), 'yyyy-MM-dd HH:mm:ss'),
Status: Spec.Status.toString(),
StatusStr: getListValueWithKey(this.statusList, Spec.Status.toString()),
}
});
this.tableData = data;
this.pageInfo.total = res.Data.TotalSize;
}
}).catch(err => {
console.log(err);
this.loading = false;
@@ -145,6 +188,39 @@ export default {
this.specificationDialogShow = false;
this.getTableData();
},
onOrOffShelf(type, data) {
this.$confirm(type === 'on-shelf' ? '请确认是否上架该规格?' : '请确认是否下架该规格?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
lockScroll: false,
}).then(() => {
updateResSpecification({
ID: data.ID,
action: type
}).then(res => {
res = res.data;
if (res.Code === 0) {
this.$message({
type: 'success',
message: '操作成功!'
});
this.getTableData();
} else {
this.$message({
type: 'error',
message: '操作失败!'
});
}
}).catch(err => {
console.log(err);
this.$message({
type: 'error',
message: '操作失败!'
});
});
}).catch(() => { });
},
confirmOperate() {
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
@@ -165,6 +241,7 @@ export default {
}
},
mounted: function () {
this.getQueueList();
this.getTableData();
},
beforeDestroy: function () {
@@ -222,7 +299,7 @@ export default {
cursor: pointer;
font-size: 12px;
color: rgb(25, 103, 252);
margin: 0 5px;
margin-right: 4px;
}
}



Loading…
Cancel
Save