#781 资源规格编辑

Merged
yangxzh1 merged 14 commits from openioctopus/octopus:master into master 3 months ago
  1. +12
    -0
      admin-portal/src/api/resourceManager.js
  2. +8
    -0
      admin-portal/src/views/devManager/components/notebook/notebookProfile.vue
  3. +147
    -9
      admin-portal/src/views/resourceManager/components/ResourceSpec.vue
  4. +2
    -2
      admin-portal/vue.config.js
  5. +18
    -9
      openai-portal/src/views/modelDev/components/notebook/notebookCreation.vue
  6. +8
    -0
      openai-portal/src/views/modelDev/components/notebook/notebookProfile.vue
  7. +17
    -0
      server/admin-server/api/v1/resourcespec.proto
  8. +33
    -0
      server/admin-server/internal/service/resourcespec.go
  9. +12
    -0
      server/base-server/api/v1/resourcespec.proto
  10. +2
    -0
      server/base-server/internal/data/dao/model/resources/resourcespec.go
  11. +11
    -0
      server/base-server/internal/data/dao/resourcespec.go
  12. +5
    -5
      server/base-server/internal/service/develop/develop.go
  13. +69
    -0
      server/base-server/internal/service/resources/resourcespec.go
  14. +1
    -0
      server/common/errors/codes.go

+ 12
- 0
admin-portal/src/api/resourceManager.js View File

@@ -82,6 +82,18 @@ export function deleteSpecification(params) {

})
}
// 更新资源规格
export function updateSpecification(params) {
return request({
url: `/v1/resourcemanage/resourcespec/${params.id}`,
method: 'put',
data: {
name: params.name,
price: params.price,
resourceQuantity: params.resourceQuantity
}
})
}
// 获取资源列表
export function getResourceList() {
return request({


+ 8
- 0
admin-portal/src/views/devManager/components/notebook/notebookProfile.vue View File

@@ -56,6 +56,14 @@
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<div>
自定义启动命令:
<span>{{ profileInfo.command }}</span>
</div>
</el-col>
</el-row>
</div>
</template>
<script>


+ 147
- 9
admin-portal/src/views/resourceManager/components/ResourceSpec.vue View File

@@ -28,11 +28,12 @@
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="text" @click="open(scope.row)">删除</el-button>
<el-button type="text" @click="edit(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 操作对话框 -->
<el-dialog title="添加规格" :visible.sync="addDialog" :close-on-click-modal="false">
<el-dialog title="添加规格" :visible.sync="addDialog" @close="closeDialog">
<el-form ref="ruleForm" :model="ruleForm" :rules="rules" label-width="100px" class="demo-ruleForm">
<el-form-item label="规格名称" prop="name">
<el-input v-model="ruleForm.name" />
@@ -54,10 +55,10 @@
</el-select>
<span style="margin:0 10px 0 10px">=</span>
<el-input v-model="item.value" style="width: 20%;" />
<i class="el-icon-delete" @click="deleteItem(item, index)"></i>
<i class="el-icon-delete" @click="deleteItem(item, index, 'add')"></i>
</el-form-item>
</div>
<el-button type="primary" @click="addItem">增加</el-button>
<el-button type="primary" @click="addItem('add')">增加</el-button>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -66,19 +67,61 @@
</div>
</el-dialog>

<!-- 编辑对话框 -->
<el-dialog title="编辑规格" :visible.sync="editDialog" @close="closeDialog">
<el-form ref="eidtRuleForm" :model="eidtRuleForm" :rules="rules" label-width="100px" class="demo-ruleForm">
<el-form-item label="规格名称" prop="name">
<el-input v-model="eidtRuleForm.name" />
</el-form-item>
<el-form-item label="机时价格" prop="price">
<el-input-number v-model="eidtRuleForm.price" :min="0" label="描述文字" :precision="2" :step="0.01"/>
<span class="red">支持两位小数</span>
</el-form-item>
<el-form-item label="资源信息" prop="resourceQuantity">
<div v-for="(item, index) in eidtRuleForm.resourceQuantity" :key="index">
<el-form-item style="margin-bottom:10px">
<el-select v-model="item.name" style="width: 20%;">
<el-option
v-for="item in options"
:key="item.name"
:label="item.name"
:value="item.name"
/>
</el-select>
<span style="margin:0 10px 0 10px">=</span>
<el-input v-model="item.value" style="width: 20%;" />
<i class="el-icon-delete" @click="deleteItem(item, index, 'edit')"></i>
</el-form-item>
</div>
<el-button type="primary" @click="addItem('edit')">增加</el-button>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="update" v-preventReClick>确认</el-button>
<el-button @click="cancel">取消</el-button>
</div>
</el-dialog>

</div>
</template>

<script>
import { getResource, deleteSpecification, createResource, getResourceList } from '@/api/resourceManager.js'
import { getResource, deleteSpecification, createResource, getResourceList, updateSpecification } from '@/api/resourceManager.js'
export default {
name: "Resource",
components: {},
data() {
return {
addDialog: false,
editDialog: false,
tableData: [],
msg: '',
eidtRuleForm: {
id: '',
name: '',
price: undefined,
resourceQuantity: []
},
ruleForm: {
name: '',
price: undefined,
@@ -119,14 +162,25 @@
}
})
},
addItem() {
this.ruleForm.resourceQuantity.push({
addItem(name) {
if(name == 'add') {
this.ruleForm.resourceQuantity.push({
key: '',
value: ''
})
return
}
this.eidtRuleForm.resourceQuantity.push({
key: '',
value: ''
})
},
deleteItem(item, index) {
this.ruleForm.resourceQuantity.splice(index, 1)
deleteItem(item, index, name) {
if(name == 'add') {
this.ruleForm.resourceQuantity.splice(index, 1)
return
}
this.eidtRuleForm.resourceQuantity.splice(index, 1)
},
confirm() {
this.$refs['ruleForm'].validate((valid) => {
@@ -179,7 +233,21 @@
}
});
},
cancel() { this.addDialog = false },
cancel() {
this.addDialog = false
this.editDialog = false
this.eidtRuleForm = {
id: '',
name: '',
price: undefined,
resourceQuantity: []
}
this.ruleForm = {
name: '',
price: undefined,
resourceQuantity: []
}
},
getResource() {
getResource().then(response => {
if (response.success) {
@@ -237,6 +305,76 @@
message: '已取消删除'
});
});
},
update() {
this.$refs['eidtRuleForm'].validate((valid) => {
if (valid) {
let obj = {}
let param = {}
obj.name = this.eidtRuleForm.name
obj.id = this.eidtRuleForm.id
obj.price = this.eidtRuleForm.price
this.eidtRuleForm.resourceQuantity.forEach(
item => {
param[item.name] = item.value
}
)
obj.resourceQuantity = param
updateSpecification(obj).then(response => {
if (response.success) {
this.$message({
message: '更新规格成功',
type: 'success'
})
this.eidtRuleForm = {
id: '',
name: '',
price: undefined,
resourceQuantity: []
}
this.getResource()
this.editDialog = false
} else {
this.$message({
message: this.getErrorMsg(response.error.subcode),
type: 'warning'
});
}
})
} else {
console.log("update error")
return false;
}
})
},
edit(val) {
let tempArr = val.resourceQuantity.split(",")
tempArr.forEach((item) => {
let arr = item.split(":")
let obj = {}
obj.name = arr[0]
obj.value = arr[1]
this.eidtRuleForm.resourceQuantity.push(obj)
})
this.editDialog = true
this.eidtRuleForm.id = val.id
this.eidtRuleForm.name = val.name
this.eidtRuleForm.price = val.price
},
closeDialog() {
this.addDialog = false
this.editDialog = false
this.eidtRuleForm = {
name: '',
price: undefined,
resourceQuantity: []
}
this.ruleForm = {
name: '',
price: undefined,
resourceQuantity: []
}

}

}


+ 2
- 2
admin-portal/vue.config.js View File

@@ -38,14 +38,14 @@ module.exports = {
},
proxy: {
[process.env.VUE_APP_BASE_API]: {
target: 'http://192.168.202.73',
target: 'http://192.168.242.41',
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: '/adminserver'
}
},
[process.env.VUE_APP_BASE_API2]: {
target: 'http://192.168.202.73',
target: 'http://192.168.242.41',
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API2]: ''


+ 18
- 9
openai-portal/src/views/modelDev/components/notebook/notebookCreation.vue View File

@@ -10,7 +10,6 @@
<div class="tip"><i
class="el-alert__icon el-icon-warning"></i>算法存储在<span>/code</span>中,数据集存储在<span>/dataset</span>中,用户目录在<span>/userhome</span>中
</div>
</el-alert>
<el-form-item label="描述" :label-width="formLabelWidth" prop="desc">
<el-input v-model="ruleForm.desc" :autosize="{ minRows: 2, maxRows: 4}" placeholder="请输入NoteBook描述"
maxlength="300" show-word-limit />
@@ -102,12 +101,20 @@
<el-form-item>
<el-button type="text" @click="showMultitask">高级设置</el-button>
</el-form-item>
<el-form-item v-if="isShowMultitask" label="任务数" prop="taskNumber">
<el-select v-model.number="ruleForm.taskNumber" placeholder="请选择">
<el-option label="1" value="1" />
<el-option label="2" value="2" />
</el-select>
</el-form-item>
<div v-if="isShowMultitask">
<el-form-item label="任务数" prop="taskNumber">
<el-select v-model.number="ruleForm.taskNumber" placeholder="请选择">
<el-option label="1" value="1" />
<el-option label="2" value="2" />
</el-select>
</el-form-item>
<el-form-item label="自定义启动命令" prop="command">
<el-input v-model="ruleForm.command"></el-input>
</el-form-item>
<div class="tip"><i
class="el-alert__icon el-icon-warning"></i>服务端口环境变量为<span>OCTOPUS_NOTEBOOK_PORT</span>,基础URL环境变量为<span>OCTOPUS_NOTEBOOK_BASE_URL</span>
</div>
</div>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancel">取 消</el-button>
@@ -163,7 +170,8 @@
dataSetVersion: "",
taskNumber: 1,
resourcePool: "",
specification: ""
specification: "",
command: ""
},
rules: {
name: [
@@ -349,7 +357,8 @@
datasetId: this.ruleForm.dataSetId || "",
datasetVersion: this.ruleForm.dataSetVersion || "",
taskNumber: this.ruleForm.taskNumber,
resourcePool: this.ruleForm.resourcePool
resourcePool: this.ruleForm.resourcePool,
command: this.ruleForm.command
};
const confirmInfo = this.$createElement
this.$confirm(


+ 8
- 0
openai-portal/src/views/modelDev/components/notebook/notebookProfile.vue View File

@@ -56,6 +56,14 @@
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<div>
自定义启动命令:
<span>{{ profileInfo.command }}</span>
</div>
</el-col>
</el-row>
</div>
</template>
<script>


+ 17
- 0
server/admin-server/api/v1/resourcespec.proto View File

@@ -18,6 +18,12 @@ service ResourceSpecService {
body: "*"
};
};
rpc UpdateResourceSpec(UpdateResourceSpecRequest) returns (UpdateResourceSpecReply){
option (google.api.http) = {
put: "/v1/resourcemanage/resourcespec/{id}",
body: "*"
};
};
rpc DeleteResourceSpec(DeleteResourceSpecRequest) returns (DeleteResourceSpecReply){
option (google.api.http) = {
delete: "/v1/resourcemanage/resourcespec/{id}",
@@ -52,6 +58,17 @@ message CreateResourceSpecReply {
string id = 1;
}

message UpdateResourceSpecRequest {
string id = 1;
string name = 2;
double price = 3;
map<string,string> resourceQuantity = 4;
}

message UpdateResourceSpecReply {
string id = 1;
}

message DeleteResourceSpecRequest {
string id = 1;
}


+ 33
- 0
server/admin-server/internal/service/resourcespec.go View File

@@ -81,6 +81,39 @@ func (rsvc *ResourceSpecService) CreateResourceSpec(ctx context.Context, req *ap
return apiReply, nil
}

func (rsvc *ResourceSpecService) UpdateResourceSpec(
ctx context.Context,
req *api.UpdateResourceSpecRequest) (*api.UpdateResourceSpecReply, error) {

reply, err := rsvc.data.ResourceSpecClient.UpdateResourceSpec(
ctx,
&innerapi.UpdateResourceSpecRequest{
Id: req.Id,
Name: req.Name,
Price: req.Price,
ResourceQuantity: req.ResourceQuantity,
})

if err != nil {
return nil, err
}

replyBytes, err := json.Marshal(reply)

if err != nil {
return nil, errors.Errorf(err, errors.ErrorUpdateResourceSpec)
}

apiReply := &api.UpdateResourceSpecReply{}
err = json.Unmarshal(replyBytes, apiReply)

if err != nil {
return nil, errors.Errorf(err, errors.ErrorUpdateResourceSpec)
}

return apiReply, nil
}

func (rsvc *ResourceSpecService) DeleteResourceSpec(ctx context.Context, req *api.DeleteResourceSpecRequest) (*api.DeleteResourceSpecReply, error) {

reply, err := rsvc.data.ResourceSpecClient.DeleteResourceSpec(ctx, &innerapi.DeleteResourceSpecRequest{


+ 12
- 0
server/base-server/api/v1/resourcespec.proto View File

@@ -11,6 +11,7 @@ service ResourceSpecService {
rpc CreateResourceSpec(CreateResourceSpecRequest) returns (CreateResourceSpecReply);
rpc DeleteResourceSpec(DeleteResourceSpecRequest) returns (DeleteResourceSpecReply);
rpc GetResourceSpec(GetResourceSpecRequest) returns (GetResourceSpecReply);
rpc UpdateResourceSpec(UpdateResourceSpecRequest) returns (UpdateResourceSpecReply);
rpc GetResourceSpecIgnore(GetResourceSpecRequest) returns (GetResourceSpecReply);
}

@@ -54,4 +55,15 @@ message DeleteResourceSpecRequest {

message DeleteResourceSpecReply {
string id = 1;
}

message UpdateResourceSpecRequest {
string id = 1;
string name = 2 [(validate.rules).string = {min_len: 1, max_len: 100}];
double price = 3;
map<string,string> resourceQuantity = 4;
}

message UpdateResourceSpecReply {
string id = 1;
}

+ 2
- 0
server/base-server/internal/data/dao/model/resources/resourcespec.go View File

@@ -23,6 +23,8 @@ type CreateResourceSpecRequest struct {
}

type UpdateResourceSpecRequest struct {
Id string
Name string
Price float64
ResourceQuantity string
}

+ 11
- 0
server/base-server/internal/data/dao/resourcespec.go View File

@@ -13,6 +13,7 @@ type ResourceSpecDao interface {
CreateResourceSpec(request *resources.CreateResourceSpecRequest) (string, error)
DeleteResourceSpec(id string) (string, error)
GetResourceSpec(id string) (*resources.ResourceSpec, error)
UpdateResourceSpec(resource *resources.ResourceSpec) (string, error)
GetResourceSpecIgnore(id string) (*resources.ResourceSpec, error)
}

@@ -92,6 +93,16 @@ func (d *resourceSepcDao) GetResourceSpec(id string) (*resources.ResourceSpec, e
return resourceSpec, nil
}

func (d *resourceSepcDao) UpdateResourceSpec(resource *resources.ResourceSpec) (string, error) {
db := d.db

if err := db.Save(resource).Error; err != nil {
return "", err
}

return resource.Id, nil
}

func (d *resourceSepcDao) GetResourceSpecIgnore(id string) (*resources.ResourceSpec, error) {
db := d.db



+ 5
- 5
server/base-server/internal/service/develop/develop.go View File

@@ -93,8 +93,8 @@ func buildIngressName(jobId string, idx int) string {
return fmt.Sprintf("%s-%s", jobId, buildTaskName(idx))
}

func buildNotebookUrl(jobId string, idx int) string {
return fmt.Sprintf("/notebook_%s_%s", jobId, buildTaskName(idx))
func buildNotebookUrl(id string, idx int) string {
return fmt.Sprintf("/notebook_%s_%s", id, buildTaskName(idx))
}

func NewDevelopService(conf *conf.Bootstrap, logger log.Logger, data *data.Data,
@@ -637,7 +637,7 @@ func (s *developService) submitJob(ctx context.Context, nb *model.Notebook, nbJo
task.Replicas = 1
envs := []v1.EnvVar{{
Name: envNotebookBaseUrl,
Value: buildNotebookUrl(nbJob.Id, i),
Value: buildNotebookUrl(nb.Id, i),
}, {
Name: envNotebookPort,
Value: strconv.Itoa(servicePort),
@@ -838,7 +838,7 @@ func (s *developService) createIngress(ctx context.Context, nb *model.Notebook,
HTTP: &v1beta1.HTTPIngressRuleValue{
Paths: []v1beta1.HTTPIngressPath{
{
Path: buildNotebookUrl(nbJob.Id, i),
Path: buildNotebookUrl(nb.Id, i),
Backend: v1beta1.IngressBackend{
ServiceName: buildServiceName(nbJob.Id, i),
ServicePort: intstr.FromInt(servicePort),
@@ -944,7 +944,7 @@ func (s *developService) convertNotebook(ctx context.Context, notebooksTbl []*mo
notebook.UpdatedAt = n.UpdatedAt.Unix()
notebook.ResourceSpecPrice = priceMap[n.NotebookJobId]
for i := 0; i < n.TaskNumber; i++ {
notebook.Tasks = append(notebook.Tasks, &api.Notebook_Task{Name: buildTaskName(i), Url: buildNotebookUrl(n.NotebookJobId, i)})
notebook.Tasks = append(notebook.Tasks, &api.Notebook_Task{Name: buildTaskName(i), Url: buildNotebookUrl(n.Id, i)})
}
notebook.ExitMsg = s.getExitMsg(ctx, jobMap[n.NotebookJobId])
notebooks = append(notebooks, notebook)


+ 69
- 0
server/base-server/internal/service/resources/resourcespec.go View File

@@ -168,6 +168,75 @@ func (rsvc *ResourceSpecService) GetResourceSpec(ctx context.Context, req *api.G
return &api.GetResourceSpecReply{ResourceSpec: rspec}, nil
}

func (rsvc *ResourceSpecService) UpdateResourceSpec(
ctx context.Context,
req *api.UpdateResourceSpecRequest) (*api.UpdateResourceSpecReply, error) {

allResources, err := rsvc.data.ResourceDao.ListResource()
if err != nil {
return &api.UpdateResourceSpecReply{}, errors.Errorf(err, errors.ErrorUpdateResourceSpec)
}

resourceMap := make(map[string]*resources.Resource)

for _, r := range allResources {
resourceMap[r.Name] = r
}

for rName := range req.ResourceQuantity {
if _, ok := resourceMap[rName]; !ok {
return &api.UpdateResourceSpecReply{}, errors.Errorf(nil, errors.ErrorUpdateResourceSpec)
}
}

for resName, quantity := range req.ResourceQuantity {
resQuant, err := resource.ParseQuantity(quantity)
if err != nil {
return &api.UpdateResourceSpecReply{}, errors.Errorf(err, errors.ErrorUpdateResourceSpec)
}

if resName == "shm" {
if _, ok := req.ResourceQuantity["memory"]; !ok {
return &api.UpdateResourceSpecReply{}, errors.Errorf(err, errors.ErrorUpdateResourceSpec)
}

memQuant, err := resource.ParseQuantity(req.ResourceQuantity["memory"])
if err != nil {
return &api.UpdateResourceSpecReply{}, errors.Errorf(err, errors.ErrorUpdateResourceSpec)
}
resQuant.Add(resQuant.DeepCopy())
if memQuant.Cmp(resQuant.DeepCopy()) < 0 {
//shm > 1/2 memory is a error
return &api.UpdateResourceSpecReply{}, errors.Errorf(err, errors.ErrorUpdateResourceSpec)
}
}
}

resQuantityBytes, err := json.Marshal(req.ResourceQuantity)

if err != nil {
return &api.UpdateResourceSpecReply{}, errors.Errorf(err, errors.ErrorUpdateResourceSpec)
}

resQuantityStr := string(resQuantityBytes)

resourceSpec, err := rsvc.data.ResourceSpecDao.GetResourceSpec(req.Id)
resourceSpec.Name = req.Name
resourceSpec.Price = req.Price
resourceSpec.ResourceQuantity = resQuantityStr
if err != nil {
return &api.UpdateResourceSpecReply{}, errors.Errorf(err, errors.ErrorUpdateResourceSpec)
}

id, err := rsvc.data.ResourceSpecDao.UpdateResourceSpec(resourceSpec)

if err != nil {
return &api.UpdateResourceSpecReply{}, errors.Errorf(err, errors.ErrorUpdateResourceSpec)
}

return &api.UpdateResourceSpecReply{Id: id}, nil
}

func (rsvc *ResourceSpecService) GetResourceSpecIgnore(ctx context.Context, req *api.GetResourceSpecRequest) (*api.GetResourceSpecReply, error) {
dbr, err := rsvc.data.ResourceSpecDao.GetResourceSpecIgnore(req.Id)



+ 1
- 0
server/common/errors/codes.go View File

@@ -98,6 +98,7 @@ const (
ErrorResourceSpecNotExist = 11017 // 资源规格不存在
ErrorResourceExist = 11018 // 资源名已存在(创建自定义资源)
ErrorListNode = 11019 // 获取节点列表失败
ErrorUpdateResourceSpec = 11020 // 更新资源规格失败

/* 12001~13000 算法管理错误*/
ErrorFindAlgorithmVersionAccessMaxIdFailed = 12001 // 查找最新公共算法版本失败


Loading…
Cancel
Save