#4621 V20230808

Merged
ychao_1983 merged 108 commits from V20230808 into develop 9 months ago
  1. +32
    -25
      entity/ai_task.go
  2. +1
    -0
      entity/container.go
  3. +12
    -12
      entity/creation.go
  4. +4
    -2
      models/ai_model_manage.go
  5. +6
    -5
      models/cloudbrain.go
  6. +4
    -0
      models/cloudbrain_spec.go
  7. +8
    -0
      models/repo.go
  8. +14
    -0
      models/repo_unit.go
  9. +13
    -2
      models/resource_queue.go
  10. +8
    -0
      models/resource_scene.go
  11. +51
    -6
      models/resource_specification.go
  12. +3
    -0
      models/unit.go
  13. +3
    -0
      models/user.go
  14. +1
    -0
      modules/auth/repo_form.go
  15. +1
    -0
      modules/context/repo.go
  16. +1
    -0
      modules/convert/cloudbrain.go
  17. +13
    -7
      modules/grampus/grampus.go
  18. +1
    -1
      modules/modelappservice/modelsevice.go
  19. +11
    -3
      modules/setting/screen_map.go
  20. +1
    -1
      modules/storage/minio.go
  21. +3
    -3
      modules/storage/obs.go
  22. +5
    -1
      modules/storage/storage.go
  23. +1
    -0
      modules/structs/cloudbrain.go
  24. +3
    -0
      options/locale/locale_en-US.ini
  25. +3
    -0
      options/locale/locale_zh-CN.ini
  26. +1
    -1
      routers/admin/cloudbrains.go
  27. +6
    -1
      routers/admin/resources.go
  28. +17
    -1
      routers/api/v1/repo/cloudbrain_dashboard.go
  29. +23
    -4
      routers/repo/ai_model_manage.go
  30. +101
    -22
      routers/repo/ai_model_square.go
  31. +8
    -0
      routers/repo/attachment.go
  32. +12
    -0
      routers/repo/setting.go
  33. +7
    -4
      routers/routes/routes.go
  34. +9
    -3
      routers/user/auth.go
  35. +15
    -9
      services/ai_task_service/cluster/c2net.go
  36. +9
    -8
      services/ai_task_service/cluster/cloudbrain_two.go
  37. +1
    -2
      services/ai_task_service/container_builder/code_builder.go
  38. +36
    -2
      services/ai_task_service/container_builder/dataset_builder.go
  39. +71
    -61
      services/ai_task_service/schedule/model_schedule.go
  40. +6
    -2
      services/ai_task_service/task/cloudbrain_one_notebook_task.go
  41. +21
    -13
      services/ai_task_service/task/cloudbrain_one_train_task.go
  42. +4
    -3
      services/ai_task_service/task/cloudbrain_two_notebook_task.go
  43. +12
    -8
      services/ai_task_service/task/cloudbrain_two_train_task.go
  44. +28
    -2
      services/ai_task_service/task/grampus_notebook_task.go
  45. +8
    -3
      services/ai_task_service/task/grampus_online_infer_task.go
  46. +21
    -13
      services/ai_task_service/task/grampus_train_task.go
  47. +1
    -0
      services/ai_task_service/task/opt_handler.go
  48. +5
    -4
      services/ai_task_service/task/task_base.go
  49. +25
    -3
      services/ai_task_service/task/task_creation_info.go
  50. +10
    -1
      services/cloudbrain/resource/resource_queue.go
  51. +3
    -0
      services/cloudbrain/resource/resource_specification.go
  52. +1
    -0
      templates/base/footer_content.tmpl
  53. +1
    -0
      templates/base/footer_content_fluid.tmpl
  54. +6
    -4
      templates/repo/datasets/index.tmpl
  55. +2
    -2
      templates/repo/header.tmpl
  56. +41
    -0
      templates/repo/modelmanage/create_online.tmpl
  57. +8
    -0
      templates/repo/settings/options.tmpl
  58. +9
    -1
      web_src/vuepages/apis/modules/modelmanage.js
  59. +692
    -0
      web_src/vuepages/components/cloudbrain/ModelSelectV2.vue
  60. +71
    -0
      web_src/vuepages/components/cloudbrain/NetworkType.vue
  61. +20
    -6
      web_src/vuepages/components/cloudbrain/SpecSelect.vue
  62. +53
    -0
      web_src/vuepages/components/cloudbrain/details/ExportModel.vue
  63. +3
    -1
      web_src/vuepages/const/index.js
  64. +8
    -0
      web_src/vuepages/langs/config/en-US.js
  65. +8
    -0
      web_src/vuepages/langs/config/zh-CN.js
  66. +17
    -7
      web_src/vuepages/pages/cloudbrain/configs.js
  67. +63
    -19
      web_src/vuepages/pages/cloudbrain/create/index.vue
  68. +78
    -47
      web_src/vuepages/pages/modelmanage/intro/index.vue
  69. +55
    -5
      web_src/vuepages/pages/modelmanage/local/index.vue
  70. +54
    -5
      web_src/vuepages/pages/modelmanage/settings/index.vue
  71. +15
    -3
      web_src/vuepages/pages/resources/components/QueueDialog.vue
  72. +4
    -2
      web_src/vuepages/pages/resources/components/SceneDialog.vue
  73. +7
    -3
      web_src/vuepages/pages/resources/components/SpecSelect.vue
  74. +14
    -5
      web_src/vuepages/pages/resources/queue/index.vue
  75. +12
    -4
      web_src/vuepages/pages/resources/scene/index.vue
  76. +10
    -1
      web_src/vuepages/pages/resources/specification/index.vue
  77. +34
    -13
      web_src/vuepages/pages/supercompute/create/index.vue

+ 32
- 25
entity/ai_task.go View File

@@ -20,31 +20,32 @@ import (

// todo 暂时保留之前各种云脑属性的定义
type CreateReq struct {
JobType models.JobType `json:"job_type" binding:"Required"`
DisplayJobName string `json:"display_job_name" binding:"Required"`
JobName string `json:"job_name"`
SpecId int64 `json:"spec_id" binding:"Required"`
ComputeSourceStr string `json:"compute_source" binding:"Required"`
Cluster ClusterType `json:"cluster" binding:"Required"`
WorkServerNumber int `json:"work_server_number"`
BranchName string `json:"branch_name"`
PreTrainModelUrl string `json:"pretrain_model_url"`
PretrainModelCkptName string `json:"pretrain_model_ckpt_name"`
ImageUrl string `json:"image_url"`
ImageID string `json:"image_id"`
ImageName string `json:"image_name"`
PretrainModelName string `json:"pretrain_model_name"`
PretrainModelVersion string `json:"pretrain_model_version"`
PretrainModelId string `json:"pretrain_model_id"`
Description string `json:"description"`
LabelName string `json:"label_names"`
DatasetUUIDStr string `json:"dataset_uuid_str"`
Params string `json:"params"`
BootFile string `json:"boot_file"`
PoolId string `json:"pool_id"`
IsContinueRequest bool `json:"is_continue"`
SourceCloudbrainId int64 `json:"source_cloudbrain_id"`
AppName string `json:"app_name"`
JobType models.JobType `json:"job_type" binding:"Required"`
DisplayJobName string `json:"display_job_name" binding:"Required"`
JobName string `json:"job_name"`
SpecId int64 `json:"spec_id" binding:"Required"`
ComputeSourceStr string `json:"compute_source" binding:"Required"`
Cluster ClusterType `json:"cluster" binding:"Required"`
WorkServerNumber int `json:"work_server_number"`
BranchName string `json:"branch_name"`
PreTrainModelUrl string `json:"pretrain_model_url"`
PretrainModelCkptName string `json:"pretrain_model_ckpt_name"`
ImageUrl string `json:"image_url"`
ImageID string `json:"image_id"`
ImageName string `json:"image_name"`
PretrainModelName string `json:"pretrain_model_name"`
PretrainModelVersion string `json:"pretrain_model_version"`
PretrainModelId string `json:"pretrain_model_id"`
Description string `json:"description"`
LabelName string `json:"label_names"`
DatasetUUIDStr string `json:"dataset_uuid_str"`
Params string `json:"params"`
BootFile string `json:"boot_file"`
PoolId string `json:"pool_id"`
IsContinueRequest bool `json:"is_continue"`
SourceCloudbrainId int64 `json:"source_cloudbrain_id"`
AppName string `json:"app_name"`
HasInternet models.SpecInternetQuery `json:"has_internet"` //0 all;1 no internet;2 has internet
ParamArray models.Parameters
ComputeSource *models.ComputeSource
ReqCommitID string
@@ -451,6 +452,12 @@ type GetResourceUsageOpts struct {
NodeId int
LogFileName string
}
type GetSpecOpts struct {
UserId int64
ComputeSource models.ComputeSource
JobType models.JobType
HasInternet models.SpecInternetQuery //0 all;1 no internet;2 has internet
}

type AITaskNodeInfo struct {
ID int `json:"id"`


+ 1
- 0
entity/container.go View File

@@ -43,6 +43,7 @@ type ContainerBuildOpts struct {
AcceptStorageType []StorageType
Uncompressed bool
MKDIR bool
VolumeFolder bool
}

func (opts ContainerBuildOpts) IsStorageTypeIn(storageType StorageType) bool {


+ 12
- 12
entity/creation.go View File

@@ -7,18 +7,18 @@ import (

type CreationRequiredInfo struct {
//排队信息、代码分支信息、查询是否有正在运行的任务、查询镜像列表、查询资源规格(积分余额,开关)
Specs []*structs.SpecificationShow `json:"specs"`
Images []ClusterImage `json:"images"`
CanUseAllImages bool `json:"can_use_all_images"`
Branches []string `json:"branches"`
DefaultBranch string `json:"default_branch"`
WaitCount int64 `json:"wait_count"`
NotStopTaskCount int `json:"not_stop_task_count"`
DisplayJobName string `json:"display_job_name"`
PointAccount *PointAccountInfo `json:"point_account"`
PaySwitch bool `json:"pay_switch"`
Config AITaskCreationConfig `json:"config"`
AllowedWorkerNum []int `json:"allowed_worker_num"`
Specs map[string][]*structs.SpecificationShow `json:"specs"`
Images []ClusterImage `json:"images"`
CanUseAllImages bool `json:"can_use_all_images"`
Branches []string `json:"branches"`
DefaultBranch string `json:"default_branch"`
WaitCount int64 `json:"wait_count"`
NotStopTaskCount int `json:"not_stop_task_count"`
DisplayJobName string `json:"display_job_name"`
PointAccount *PointAccountInfo `json:"point_account"`
PaySwitch bool `json:"pay_switch"`
Config AITaskCreationConfig `json:"config"`
AllowedWorkerNum []int `json:"allowed_worker_num"`
}

type AITaskCreationConfig struct {


+ 4
- 2
models/ai_model_manage.go View File

@@ -56,6 +56,7 @@ type AiModelManage struct {
OnlineInfo []map[string]interface{} `xorm:"-" json:"onlineInfo"`
UsedCloudbrain []map[string]interface{} `xorm:"-" json:"usedCloudbrain"`
HasOnlineUrl int `xorm:"NOT NULL DEFAULT 0" json:"hasOnlineUrl"`
License string `xorm:"NULL" json:"license"`
}

type AiModelFile struct {
@@ -395,16 +396,17 @@ func ModifyModelCollectedNum(id string, collectedNum int) error {
return nil
}

func ModifyLocalModel(id string, name, label, description string, engine int, isPrivate bool) error {
func ModifyLocalModel(id string, name, label, description string, engine int, isPrivate bool, license string) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("name", "label", "description", "engine", "is_private").Update(&AiModelManage{
re, err := sess.Cols("name", "label", "description", "engine", "is_private", "license").Update(&AiModelManage{
Description: description,
Name: name,
Label: label,
Engine: int64(engine),
IsPrivate: isPrivate,
License: license,
})
if err != nil {
return err


+ 6
- 5
models/cloudbrain.go View File

@@ -303,7 +303,7 @@ type Cloudbrain struct {
FineTuneCategory int
Spec *Specification `xorm:"-"`
Config *CloudbrainConfig `xorm:"-"`
AppName string //超算任务的应用类型
AppName string //超算任务的应用类型
}

type CloudbrainShow struct {
@@ -1855,10 +1855,11 @@ type GrampusSpec struct {
}

type GrampusAiCenter struct {
AccDevices []GrampusAccDevice `json:"accDevices"`
Id string `json:"id"`
Name string `json:"name"`
Resource []GrampusCenterResource `json:"resource"`
AccDevices []GrampusAccDevice `json:"accDevices"`
Id string `json:"id"`
Name string `json:"name"`
Resource []GrampusCenterResource `json:"resource"`
IsNetAccess bool `json:"isNetAccess"`
}

type GrampusAccDevice struct {


+ 4
- 0
models/cloudbrain_spec.go View File

@@ -19,6 +19,7 @@ type CloudbrainSpec struct {
QueueId int64
QueueCode string
Cluster string
HasInternet int
AiCenterCode string `xorm:"index"`
AiCenterName string
IsExclusive bool
@@ -46,6 +47,7 @@ func (s CloudbrainSpec) ConvertToSpecification() *Specification {
AiCenterName: s.AiCenterName,
IsExclusive: s.IsExclusive,
ExclusiveOrg: s.ExclusiveOrg,
HasInternet: s.HasInternet,
}
}

@@ -69,6 +71,7 @@ func NewCloudBrainSpec(cloudbrainId int64, s Specification) CloudbrainSpec {
AiCenterName: s.AiCenterName,
IsExclusive: s.IsExclusive,
ExclusiveOrg: s.ExclusiveOrg,
HasInternet: s.HasInternet,
}
}

@@ -155,6 +158,7 @@ func UpdateCloudbrainSpec(cloudbrainId int64, s *Specification) (int64, error) {
AiCenterName: s.AiCenterName,
IsExclusive: s.IsExclusive,
ExclusiveOrg: s.ExclusiveOrg,
HasInternet: s.HasInternet,
}
return x.Where("cloudbrain_id = ?", cloudbrainId).Update(&new)
}

+ 8
- 0
models/repo.go View File

@@ -1301,6 +1301,14 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, opts ...Cr
Config: &ModelManageConfig{EnableModelManage: true},
})
}
} else if tp == UnitTypeHPC {
if !isCourse {
units = append(units, RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &HPCConfig{EnableHPC: true},
})
}
} else {
units = append(units, RepoUnit{
RepoID: repo.ID,


+ 14
- 0
models/repo_unit.go View File

@@ -135,6 +135,18 @@ type ModelManageConfig struct {
EnableModelManage bool
}

type HPCConfig struct {
EnableHPC bool
}

func (cfg *HPCConfig) FromDB(bs []byte) error {
return json.Unmarshal(bs, &cfg)
}

func (cfg *HPCConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}

// FromDB fills up a CloudBrainConfig from serialized format.
func (cfg *ModelManageConfig) FromDB(bs []byte) error {
return json.Unmarshal(bs, &cfg)
@@ -188,6 +200,8 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
r.Config = new(DatasetConfig)
case UnitTypeCloudBrain:
r.Config = new(CloudBrainConfig)
case UnitTypeHPC:
r.Config = new(HPCConfig)
case UnitTypeBlockChain:
r.Config = new(BlockChainConfig)
case UnitTypeModelManage:


+ 13
- 2
models/resource_queue.go View File

@@ -17,6 +17,7 @@ type ResourceQueue struct {
ComputeResource string
AccCardType string
CardsTotalNum int
HasInternet int //0 unknown;1 no internet;2 has internet
IsAutomaticSync bool
Remark string
DeletedTime timeutil.TimeStamp `xorm:"deleted"`
@@ -38,6 +39,7 @@ func (r ResourceQueue) ConvertToRes() *ResourceQueueRes {
CardsTotalNum: r.CardsTotalNum,
UpdatedTime: r.UpdatedTime,
Remark: r.Remark,
HasInternet: AICenterInternetStatus(r.HasInternet),
}
}

@@ -48,6 +50,7 @@ type ResourceQueueReq struct {
ComputeResource string `binding:"Required"`
AccCardType string `binding:"Required"`
CardsTotalNum int
HasInternet int
CreatorId int64
IsAutomaticSync bool
Remark string
@@ -61,6 +64,7 @@ func (r ResourceQueueReq) ToDTO() ResourceQueue {
ComputeResource: strings.ToUpper(r.ComputeResource),
AccCardType: strings.ToUpper(r.AccCardType),
CardsTotalNum: r.CardsTotalNum,
HasInternet: r.HasInternet,
IsAutomaticSync: r.IsAutomaticSync,
Remark: r.Remark,
CreatedBy: r.CreatorId,
@@ -84,6 +88,7 @@ type SearchResourceQueueOptions struct {
AiCenterCode string
ComputeResource string
AccCardType string
HasInternet SpecInternetQuery
}

type ResourceQueueListRes struct {
@@ -134,6 +139,7 @@ type ResourceQueueRes struct {
CardsTotalNum int
UpdatedTime timeutil.TimeStamp
Remark string
HasInternet AICenterInternetStatus
}

func InsertResourceQueue(queue ResourceQueue) (int64, error) {
@@ -143,8 +149,8 @@ func InsertResourceQueue(queue ResourceQueue) (int64, error) {
func UpdateResourceQueueById(queueId int64, queue ResourceQueue) (int64, error) {
return x.ID(queueId).Update(&queue)
}
func UpdateResourceCardsTotalNum(queueId int64, queue ResourceQueue) (int64, error) {
return x.ID(queueId).Cols("cards_total_num", "remark").Update(&queue)
func UpdateResourceCardsTotalNumAndInternetStatus(queueId int64, queue ResourceQueue) (int64, error) {
return x.ID(queueId).Cols("cards_total_num", "remark", "has_internet").Update(&queue)
}

func SearchResourceQueue(opts SearchResourceQueueOptions) (int64, []ResourceQueue, error) {
@@ -164,6 +170,11 @@ func SearchResourceQueue(opts SearchResourceQueueOptions) (int64, []ResourceQueu
if opts.AccCardType != "" {
cond = cond.And(builder.Eq{"acc_card_type": opts.AccCardType})
}
if opts.HasInternet == QueryNoInternetSpecs {
cond = cond.And(builder.Eq{"has_internet": NoInternet})
} else if opts.HasInternet == QueryHasInternetSpecs {
cond = cond.And(builder.Eq{"has_internet": HasInternet})
}
n, err := x.Where(cond).Unscoped().Count(&ResourceQueue{})
if err != nil {
return 0, nil, err


+ 8
- 0
models/resource_scene.go View File

@@ -51,6 +51,7 @@ type SearchResourceSceneOptions struct {
ComputeResource string
AccCardType string
Cluster string
HasInternet SpecInternetQuery
}

type ResourceSceneListRes struct {
@@ -107,6 +108,7 @@ type ResourceSpecInfo struct {
QueueId int64
ComputeResource string
AccCardType string
HasInternet int
}

func (ResourceSpecInfo) TableName() string {
@@ -268,6 +270,11 @@ func SearchResourceScene(opts SearchResourceSceneOptions) (int64, []ResourceScen
if opts.Cluster != "" {
cond = cond.And(builder.Eq{"resource_queue.cluster": opts.Cluster})
}
if opts.HasInternet == QueryHasInternetSpecs {
cond = cond.And(builder.Eq{"resource_queue.has_internet": HasInternet})
} else if opts.HasInternet == QueryNoInternetSpecs {
cond = cond.And(builder.Eq{"resource_queue.has_internet": NoInternet})
}
cond = cond.And(builder.NewCond().Or(builder.Eq{"resource_scene.delete_time": 0}).Or(builder.IsNull{"resource_scene.delete_time"}))
cols := []string{"resource_scene.id", "resource_scene.scene_name", "resource_scene.job_type", "resource_scene.is_exclusive",
"resource_scene.exclusive_org"}
@@ -312,6 +319,7 @@ func SearchResourceScene(opts SearchResourceSceneOptions) (int64, []ResourceScen
"resource_queue.ai_center_code", "resource_queue.acc_card_type",
"resource_queue.id as queue_id", "resource_queue.compute_resource",
"resource_queue.queue_code", "resource_queue.ai_center_name",
"resource_queue.has_internet",
).In("resource_scene_spec.scene_id", sceneIds).
Join("INNER", "resource_scene_spec", "resource_scene_spec.spec_id = resource_specification.id").
Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id").


+ 51
- 6
models/resource_specification.go View File

@@ -20,6 +20,22 @@ const (
SearchSpecOrder4Standard
)

type AICenterInternetStatus int

const (
Unknown AICenterInternetStatus = -1
NoInternet AICenterInternetStatus = 1
HasInternet AICenterInternetStatus = 2
)

type SpecInternetQuery int

const (
QueryAllSpecs SpecInternetQuery = 0
QueryNoInternetSpecs SpecInternetQuery = 1
QueryHasInternetSpecs SpecInternetQuery = 2
)

type ResourceSpecification struct {
ID int64 `xorm:"pk autoincr"`
QueueId int64 `xorm:"INDEX"`
@@ -97,6 +113,7 @@ type SearchResourceSpecificationOptions struct {
AccCardsNum int
ComputeResource string
AccCardType string
HasInternet SpecInternetQuery
}

type SearchResourceBriefSpecificationOptions struct {
@@ -178,6 +195,7 @@ func (r ResourceSpecAndQueue) ConvertToResourceSpecInfo() *ResourceSpecInfo {
QueueId: r.QueueId,
ComputeResource: r.ComputeResource,
AccCardType: r.AccCardType,
HasInternet: r.HasInternet,
}
}

@@ -201,8 +219,9 @@ type FindSpecsOptions struct {
ShareMemGiB float32
UseShareMemGiB bool
//if true,find specs no matter used or not used in scene. if false,only find specs used in scene
RequestAll bool
SpecStatus int
RequestAll bool
SpecStatus int
HasInternet SpecInternetQuery //0 all,1 no internet,2 has internet
}

type Specification struct {
@@ -218,6 +237,7 @@ type Specification struct {
UnitPrice int
QueueId int64
QueueCode string
HasInternet int
Cluster string
AiCenterCode string
AiCenterName string
@@ -231,7 +251,7 @@ func (Specification) TableName() string {
return "resource_specification"
}

func (s *Specification) loadRelatedSpecs(jobType JobType) {
func (s *Specification) loadRelatedSpecs(jobType JobType, hasInternet SpecInternetQuery) {
if s.RelatedSpecs != nil {
return
}
@@ -240,6 +260,11 @@ func (s *Specification) loadRelatedSpecs(jobType JobType) {
s.RelatedSpecs = defaultSpecs
return
}
//是否需要网络的调度策略如下:
//需要联网时只能调度到有网的分中心;不需要联网时可以调度到所有的分中心
if hasInternet == QueryNoInternetSpecs {
hasInternet = QueryAllSpecs
}
r, err := FindSpecs(FindSpecsOptions{
ComputeResource: s.ComputeResource,
Cluster: s.Cluster,
@@ -247,6 +272,7 @@ func (s *Specification) loadRelatedSpecs(jobType JobType) {
RequestAll: false,
SpecStatus: SpecOnShelf,
JobType: jobType,
HasInternet: hasInternet,
})
if err != nil {
s.RelatedSpecs = defaultSpecs
@@ -254,15 +280,22 @@ func (s *Specification) loadRelatedSpecs(jobType JobType) {
}
s.RelatedSpecs = r
}
func (s *Specification) GetAvailableCenterIds(userId int64, jobType JobType) []string {
s.loadRelatedSpecs(jobType)

type GetAvailableCenterIdOpts struct {
UserId int64
JobType JobType
HasInternet SpecInternetQuery
}

func (s *Specification) GetAvailableCenterIds(opts GetAvailableCenterIdOpts) []string {
s.loadRelatedSpecs(opts.JobType, opts.HasInternet)

if len(s.RelatedSpecs) == 0 {
return make([]string, 0)
}

//filter exclusive specs
specs := FilterExclusiveSpecs(s.RelatedSpecs, userId)
specs := FilterExclusiveSpecs(s.RelatedSpecs, opts.UserId)

centerIds := make([]string, len(specs))
for i, v := range specs {
@@ -358,6 +391,12 @@ func SearchResourceSpecification(opts SearchResourceSpecificationOptions) (int64
} else if opts.AvailableCode == 2 {
cond = cond.And(builder.Eq{"resource_specification.is_available": false})
}

if opts.HasInternet == QueryNoInternetSpecs {
cond = cond.And(builder.Eq{"resource_queue.has_internet": NoInternet})
} else if opts.HasInternet == QueryHasInternetSpecs {
cond = cond.And(builder.Eq{"resource_queue.has_internet": HasInternet})
}
//cond = cond.And(builder.Or(builder.Eq{"resource_queue.deleted_time": 0}).Or(builder.IsNull{"resource_queue.deleted_time"}))
n, err := x.Where(cond).Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id").
Unscoped().Count(&ResourceSpecAndQueue{})
@@ -530,6 +569,12 @@ func FindSpecs(opts FindSpecsOptions) ([]*Specification, error) {
if opts.SpecStatus > 0 {
cond = cond.And(builder.Eq{"resource_specification.status": opts.SpecStatus})
}
if opts.HasInternet == QueryNoInternetSpecs {
cond = cond.And(builder.Eq{"resource_queue.has_internet": NoInternet})
} else if opts.HasInternet == QueryHasInternetSpecs {
cond = cond.And(builder.Eq{"resource_queue.has_internet": HasInternet})
}

r := make([]*Specification, 0)
s := x.Where(cond).
Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id")


+ 3
- 0
models/unit.go View File

@@ -28,6 +28,7 @@ const (
UnitTypeCloudBrain UnitType = 11 // 11 CloudBrain
UnitTypeBlockChain UnitType = 12 // 12 BlockChain
UnitTypeModelManage UnitType = 13 // 13 ModelManage
UnitTypeHPC UnitType = 14 // 14 HPC
)

// Value returns integer value for unit type
@@ -84,6 +85,7 @@ var (
UnitTypeCloudBrain,
UnitTypeBlockChain,
UnitTypeModelManage,
UnitTypeHPC,
}

// DefaultRepoUnits contains the default unit types
@@ -97,6 +99,7 @@ var (
UnitTypeCloudBrain,
UnitTypeBlockChain,
UnitTypeModelManage,
UnitTypeHPC,
}

// NotAllowedDefaultRepoUnits contains units that can't be default


+ 3
- 0
models/user.go View File

@@ -328,6 +328,9 @@ func (u *User) IsLocal() bool {
func (u *User) IsOAuth2() bool {
return u.LoginType == LoginOAuth2
}
func (u *User) IsCloudBrain() bool {
return u.LoginType == LoginCloudBrain
}

// HasForkedRepo checks if user has already forked a repository with given ID.
func (u *User) HasForkedRepo(repoID int64) bool {


+ 1
- 0
modules/auth/repo_form.go View File

@@ -127,6 +127,7 @@ type RepoSettingForm struct {
EnableDataset bool
EnableCloudBrain bool
EnableModelManager bool
EnableHPC bool
EnableWiki bool
EnableExternalWiki bool
ExternalWikiURL string


+ 1
- 0
modules/context/repo.go View File

@@ -826,5 +826,6 @@ func UnitTypes() macaron.Handler {
ctx.Data["UnitTypeExternalTracker"] = models.UnitTypeExternalTracker
ctx.Data["UnitTypeBlockChain"] = models.UnitTypeBlockChain
ctx.Data["UnitTypeModelManage"] = models.UnitTypeModelManage
ctx.Data["UnitTypeHPC"] = models.UnitTypeHPC
}
}

+ 1
- 0
modules/convert/cloudbrain.go View File

@@ -102,6 +102,7 @@ func ToSpecification(s *models.Specification) *api.SpecificationShow {
ComputeResource: s.ComputeResource,
UnitPrice: s.UnitPrice,
SourceSpecId: s.SourceSpecId,
HasInternet: s.HasInternet,
}
}



+ 13
- 7
modules/grampus/grampus.go View File

@@ -279,7 +279,10 @@ func GenerateNotebookJob(ctx *context.Context, req *GenerateNotebookJobReq) (job
AutoStopDuration: autoStopDurationMs,
Capacity: setting.Capacity,
Command: req.Command,
CenterID: req.Spec.GetAvailableCenterIds(ctx.User.ID, models.JobTypeDebug),
CenterID: req.Spec.GetAvailableCenterIds(models.GetAvailableCenterIdOpts{
UserId: ctx.User.ID,
JobType: models.JobTypeDebug,
}),
},
},
})
@@ -438,12 +441,15 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId str
Name: req.JobName,
Tasks: []models.GrampusTasks{
{
Name: req.JobName,
Command: req.Command,
ResourceSpecId: req.Spec.SourceSpecId,
ImageId: req.ImageId,
ImageUrl: req.ImageUrl,
CenterID: req.Spec.GetAvailableCenterIds(ctx.User.ID, models.JobTypeTrain),
Name: req.JobName,
Command: req.Command,
ResourceSpecId: req.Spec.SourceSpecId,
ImageId: req.ImageId,
ImageUrl: req.ImageUrl,
CenterID: req.Spec.GetAvailableCenterIds(models.GetAvailableCenterIdOpts{
UserId: ctx.User.ID,
JobType: models.JobTypeTrain,
}),
ReplicaNum: 1,
Datasets: datasetGrampus,
Models: modelGrampus,


+ 1
- 1
modules/modelappservice/modelsevice.go View File

@@ -31,7 +31,7 @@ func ProducerOrder(modelApp *models.ModelApp) {

func GetWaitTime() int {
dvid := setting.BaiduWenXin.MODEL_SERVERS
return ((len(wenxinChannel) / dvid) + 1) * 15
return ((len(wenxinChannel) / dvid) + 1) * 30
}

func consumerOrder(in <-chan *models.ModelApp, url string) {


+ 11
- 3
modules/setting/screen_map.go View File

@@ -1,9 +1,12 @@
package setting

import "strings"

var ScreenMap = struct {
ShowData bool
MinValue int
MaxValue int
ShowData bool
MinValue int
MaxValue int
ExcludeCenter []string
}{}

var IPInfo = struct {
@@ -16,6 +19,11 @@ func NewScreenMapConfig() {
ScreenMap.ShowData = sec.Key("ShowData").MustBool(false)
ScreenMap.MinValue = sec.Key("MinValue").MustInt(130)
ScreenMap.MaxValue = sec.Key("MaxValue").MustInt(190)
ScreenMap.ExcludeCenter = []string{}
excludeCenterStr := sec.Key("ExcludeCenter").MustString("")
if excludeCenterStr != "" {
ScreenMap.ExcludeCenter = strings.Split(excludeCenterStr, ",")
}

sec = Cfg.Section("IPInfo")



+ 1
- 1
modules/storage/minio.go View File

@@ -22,7 +22,7 @@ var (
)

const (
PresignedGetUrlExpireTime = time.Hour * 24 * 7
PresignedGetUrlExpireTime = time.Hour * 24 * 1
PresignedPutUrlExpireTime = time.Hour * 24 * 7
)



+ 3
- 3
modules/storage/obs.go View File

@@ -599,7 +599,7 @@ func ObsGenMultiPartSignedUrl(objectName string, uploadId string, partNumber int
input.Bucket = setting.Bucket
input.Key = objectName
//strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid, fileName)), "/")
input.Expires = 60 * 60
input.Expires = 24 * 60 * 60
input.Method = obs.HttpMethodPut

input.QueryParams = map[string]string{
@@ -622,7 +622,7 @@ func GetObsCreateSignedUrlByBucketAndKey(bucket, key string) (string, error) {
input.Bucket = bucket
input.Key = key

input.Expires = 60 * 60
input.Expires = 24 * 60 * 60
input.Method = obs.HttpMethodGet
comma := strings.LastIndex(key, "/")
filename := key
@@ -652,7 +652,7 @@ func ObsGetPreSignedUrl(objectName, fileName string) (string, error) {
input.Key = objectName
//strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid, fileName)), "/")
input.Bucket = setting.Bucket
input.Expires = 60 * 60
input.Expires = 24 * 60 * 60

fileName = url.PathEscape(fileName)
reqParams := make(map[string]string)


+ 5
- 1
modules/storage/storage.go View File

@@ -70,7 +70,11 @@ func Init() error {
m.BasePath,
m.UseSSL,
)
log.Info("minio storage inited.")
if err != nil {
log.Info("minio storage inited failed." + err.Error())
} else {
log.Info("minio storage inited succeed.")
}
MinioCore, err = minio.NewCore(m.Endpoint, m.AccessKeyID, m.SecretAccessKey, m.UseSSL)
if err != nil {
log.Error("init ScheduleMinioCore err.%v", err)


+ 1
- 0
modules/structs/cloudbrain.go View File

@@ -144,6 +144,7 @@ type SpecificationShow struct {
ComputeResource string `json:"compute_resource"`
UnitPrice int `json:"unit_price"`
SourceSpecId string `json:"source_spec_id"`
HasInternet int `json:"has_internet"`
}
type PointAccountShow struct {
ID int64 `json:"id"`


+ 3
- 0
options/locale/locale_en-US.ini View File

@@ -1345,6 +1345,8 @@ model.manage.engine=Model engine
model.manage.select.engine=Select model engine
model.manage.modelfile=Model file
model.manage.modellabel=Model label
model.manage.modellicense=License
model.manage.select.modellicense=Select license
model.manage.modeldesc=Model brief introduction
model.manage.modelaccess=Model Access
model.manage.modelaccess.public=Public
@@ -2117,6 +2119,7 @@ settings.external_wiki_url_error = The external wiki URL is not a valid URL.
settings.external_wiki_url_desc = Visitors are redirected to the external wiki URL when clicking the wiki tab.
settings.dataset_desc = Enable Repository Dataset
settings.cloudbrain_desc = Enable Cloudbarin
settings.hpc_desc = Enable HPC
settings.model_desc = Enable Model Manage
settings.issues_desc = Enable Repository Issue Tracker
settings.use_internal_issue_tracker = Use Built-In Issue Tracker


+ 3
- 0
options/locale/locale_zh-CN.ini View File

@@ -1358,6 +1358,8 @@ model.manage.engine=模型框架
model.manage.select.engine=选择模型框架
model.manage.modelfile=模型文件
model.manage.modellabel=模型标签
model.manage.modellicense=许可证
model.manage.select.modellicense=选择许可证
model.manage.modeldesc=模型简介
model.manage.modelaccess=模型权限
model.manage.modelaccess.public=公开
@@ -2133,6 +2135,7 @@ settings.external_wiki_url_error=外部百科链接无效
settings.external_wiki_url_desc=当点击任务标签时,访问者将被重定向到外部任务系统的URL。
settings.dataset_desc=启用数据集
settings.cloudbrain_desc = 启用云脑
settings.hpc_desc = 启用超算
settings.model_desc = 启用模型管理
settings.issues_desc=启用任务系统
settings.use_internal_issue_tracker=使用内置的轻量级任务管理系统


+ 1
- 1
routers/admin/cloudbrains.go View File

@@ -233,7 +233,7 @@ func getRepoPathName(rs *models.CloudbrainInfo) string {
}

func getDurationTime(rs *models.CloudbrainInfo) string {
if rs.JobType == "TRAIN" || rs.JobType == "INFERENCE" {
if rs.JobType == "TRAIN" || rs.JobType == "INFERENCE" || rs.JobType == string(models.JobTypeSuperCompute) {
return rs.TrainJobDuration
} else {
return "-"


+ 6
- 1
routers/admin/resources.go View File

@@ -45,12 +45,14 @@ func GetResourceQueueList(ctx *context.Context) {
aiCenterCode := ctx.Query("center")
computeResource := ctx.Query("resource")
accCardType := ctx.Query("card")
hasInternet := ctx.QueryInt("hasInternet")
list, err := resource.GetResourceQueueList(models.SearchResourceQueueOptions{
ListOptions: models.ListOptions{Page: page, PageSize: 10},
Cluster: cluster,
AiCenterCode: aiCenterCode,
ComputeResource: computeResource,
AccCardType: accCardType,
HasInternet: models.SpecInternetQuery(hasInternet),
})
if err != nil {
log.Error("GetResourceQueueList error.%v", err)
@@ -95,7 +97,6 @@ func AddResourceQueue(ctx *context.Context, req models.ResourceQueueReq) {

func UpdateResourceQueue(ctx *context.Context, req models.ResourceQueueReq) {
queueId := ctx.ParamsInt64(":id")
//only CardsTotalNum permitted to change
err := resource.UpdateResourceQueue(queueId, req)
if err != nil {
log.Error("UpdateResourceQueue error. %v", err)
@@ -124,6 +125,7 @@ func GetResourceSpecificationList(ctx *context.Context) {
cardsNum := ctx.QueryInt("cardsNum")
computeResource := ctx.Query("resource")
cardType := ctx.Query("cardType")
hasInternet := ctx.QueryInt("hasInternet")
list, err := resource.GetResourceSpecificationList(models.SearchResourceSpecificationOptions{
ListOptions: models.ListOptions{Page: page, PageSize: 10},
QueueId: queue,
@@ -134,6 +136,7 @@ func GetResourceSpecificationList(ctx *context.Context) {
AccCardsNum: cardsNum,
ComputeResource: computeResource,
AccCardType: cardType,
HasInternet: models.SpecInternetQuery(hasInternet),
})
if err != nil {
log.Error("GetResourceSpecificationList error.%v", err)
@@ -235,6 +238,7 @@ func GetResourceSceneList(ctx *context.Context) {
computeResource := ctx.Query("resource")
cardType := ctx.Query("cardType")
cluster := ctx.Query("cluster")
hasInternet := ctx.QueryInt("hasInternet")
list, err := resource.GetResourceSceneList(models.SearchResourceSceneOptions{
ListOptions: models.ListOptions{Page: page, PageSize: 10},
JobType: jobType,
@@ -244,6 +248,7 @@ func GetResourceSceneList(ctx *context.Context) {
ComputeResource: computeResource,
AccCardType: cardType,
Cluster: cluster,
HasInternet: models.SpecInternetQuery(hasInternet),
})
if err != nil {
log.Error("GetResourceSceneList error.%v", err)


+ 17
- 1
routers/api/v1/repo/cloudbrain_dashboard.go View File

@@ -211,14 +211,21 @@ func GetCloubrainOverviewGroupByAiCenter(ctx *context.Context) {

if setting.ScreenMap.ShowData || ctx.IsUserSiteAdmin() {

var cloudbrainCardTimeAndCountFilterArray = make([]map[string]string, 0)

for _, cloudbrainCardTimeAndCountMap := range cloudbrainCardTimeAndCountArray {
centerId := cloudbrainCardTimeAndCountMap["ai_center"]
centerShow := cloudbrainService.GetAiCenterShowByAiCenterId(centerId, ctx)
cloudbrainCardTimeAndCountMap["ai_center"] = centerShow

if len(setting.ScreenMap.ExcludeCenter) > 0 && isExcludeCenter(centerId) {
continue

}
cloudbrainCardTimeAndCountFilterArray = append(cloudbrainCardTimeAndCountFilterArray, cloudbrainCardTimeAndCountMap)
}
ctx.JSON(http.StatusOK, map[string]interface{}{
"cardAndJobCount": cloudbrainCardTimeAndCountArray,
"cardAndJobCount": cloudbrainCardTimeAndCountFilterArray,
"locationInfo": aiCenterLocationInfos,
})
return
@@ -232,6 +239,15 @@ func GetCloubrainOverviewGroupByAiCenter(ctx *context.Context) {

}

func isExcludeCenter(id string) bool {
for _, centerId := range setting.ScreenMap.ExcludeCenter {
if id == centerId {
return true
}
}
return false
}

func getAiCenterSize(name string, timeMap map[string]int64, MaxCardTime int64, MinCardTime int64) int {
cardTime, _ := timeMap[name]
if cardTime == 0 {


+ 23
- 4
routers/repo/ai_model_manage.go View File

@@ -87,6 +87,7 @@ func saveModelByParameters(aiTask *models.Cloudbrain, name string, version strin
aiTask.ContainerIp = ""
aiTaskJson, _ := json.Marshal(aiTask)
isPrivate := ctx.QueryBool("isPrivate")
license := ctx.Query("license")
model := &models.AiModelManage{
ID: id,
Version: version,
@@ -109,6 +110,7 @@ func saveModelByParameters(aiTask *models.Cloudbrain, name string, version strin
Status: STATUS_COPY_MODEL,
IsPrivate: isPrivate,
ComputeResource: aiTask.ComputeResource,
License: license,
}

err = models.SaveModelToDb(model)
@@ -278,6 +280,7 @@ func SaveLocalModel(ctx *context.Context) {
}
}
}
license := ctx.Query("license")
model := &models.AiModelManage{
ID: id,
Version: version,
@@ -299,6 +302,7 @@ func SaveLocalModel(ctx *context.Context) {
Status: STATUS_FINISHED,
IsPrivate: isPrivate,
ComputeResource: computeResource,
License: license,
}

err := models.SaveModelToDb(model)
@@ -540,7 +544,11 @@ func DeleteModelFile(ctx *context.Context) {
return
} else {
log.Info("delete obs file size is:" + fmt.Sprint(totalSize))
models.ModifyModelSize(id, model.Size-totalSize)
newSize := model.Size - totalSize
if newSize < 0 {
newSize = 0
}
models.ModifyModelSize(id, newSize)
modelFile := &models.AiModelFile{
Name: fileName,
ModelID: id,
@@ -1285,6 +1293,7 @@ func ModifyModelInfo(ctx *context.Context) {
description := ctx.Query("description")
engine := ctx.QueryInt("engine")
isPrivate := ctx.QueryBool("isPrivate")
license := ctx.Query("license")
aimodels := models.QueryModelByName(name, task.RepoId)
if aimodels != nil && len(aimodels) > 0 {
if len(aimodels) == 1 {
@@ -1299,16 +1308,26 @@ func ModifyModelInfo(ctx *context.Context) {
return
}
}
err = models.ModifyLocalModel(id, name, label, description, engine, isPrivate)
err = models.ModifyLocalModel(id, name, label, description, engine, isPrivate, license)
if task.Name != name {
aimodels = models.QueryModelByName(task.Name, task.RepoId)
if aimodels != nil && len(aimodels) > 0 {
for _, model := range aimodels {
models.ModifyLocalModel(model.ID, name, model.Label, model.Description, int(model.Engine), model.IsPrivate)
models.ModifyLocalModel(model.ID, name, model.Label, model.Description, int(model.Engine), model.IsPrivate, model.License)
}
}
}

if license != task.License {
mdContent := getMDContent(task)
if mdContent != "" {
if task.License != "" {
log.Info("start to remove license.")
mdContent = removeLicenseFromMD(mdContent, task.License)
}
mdContent = addLicenseMDToReadme(mdContent, license)
updateReadMeMd(task, mdContent)
}
}
if err != nil {
re["msg"] = err.Error()
ctx.JSON(200, re)


+ 101
- 22
routers/repo/ai_model_square.go View File

@@ -452,6 +452,10 @@ func ModifyModelReadMe(ctx *context.Context) {
if err == nil {
content := ctx.Query("content")
hasOnlineUrl := hasOnlineUrlInReadme(content)
log.Info(" model.License =")
if model.License != "" {
content = addLicenseMDToReadme(content, model.License)
}
path := Model_prefix + models.AttachmentRelativePath(id) + "/"
if model.Type == models.TypeCloudBrainTwo {
err = storage.PutStringToObs(setting.Bucket, path+README_FILE_NAME, content)
@@ -476,6 +480,79 @@ func ModifyModelReadMe(ctx *context.Context) {
}
}

func updateReadMeMd(model *models.AiModelManage, content string) {
path := Model_prefix + models.AttachmentRelativePath(model.ID) + "/"
if model.Type == models.TypeCloudBrainTwo {
log.Info("put model md file to obs.")
err := storage.PutStringToObs(setting.Bucket, path+README_FILE_NAME, content)
if err != nil {
log.Info("Failed to update readme file. as:" + err.Error())
}
}
}

func removeLicenseFromMD(content string, licenseId string) string {
lines := strings.Split(content, "\n")
licenseMap := getLicenseMap(licenseId)
if licenseMap == nil {
return content
}
newLines := make([]string, 0, len(lines))
for _, line := range lines {
if line == "## 许可证" || strings.HasPrefix(line, "["+licenseMap["name"]+"]") {
log.Info("line is equal. + line=" + line)
continue
}
newLines = append(newLines, line)
}
return strings.Join(newLines, "\n")
}

func getLicenseMap(licenseId string) map[string]string {
url := setting.RecommentRepoAddr + "model/license.json"
result, err := repository.RecommendContentFromPromote(url)
log.Info("license result=" + result)
remap := make([]map[string]string, 0)
if err == nil {
err = json.Unmarshal([]byte(result), &remap)
if err != nil {
log.Info("error=" + err.Error())
return nil
}
for _, licenseMap := range remap {
if licenseMap["id"] == licenseId {
return licenseMap
}
}
} else {
log.Info("error=" + err.Error())
}
return nil
}

func addLicenseMDToReadme(content string, licenseId string) string {
if hasLicense(content) {
return content
}
log.Info("start to add license to md file.")
licenseMap := getLicenseMap(licenseId)
if licenseMap == nil {
return content
}
content += "\n## 许可证\n"
content += "[" + licenseMap["name"] + "](" + licenseMap["linkUrl"] + ")\n"
return content
}

func hasLicense(content string) bool {
re := regexp.MustCompile(`## 许可证\s+(.+)`)
match := re.FindStringSubmatch(content)
if len(match) > 1 {
return true
}
return false
}

func hasOnlineUrlInReadme(content string) bool {
re := regexp.MustCompile(`在线体验地址\s+(.+)`)
match := re.FindStringSubmatch(content)
@@ -491,6 +568,26 @@ func hasOnlineUrlInReadme(content string) bool {
return false
}

func getMDContent(model *models.AiModelManage) string {
files := queryOneLevelModelFile(model, "")
var content []byte
for _, file := range files {
if strings.ToLower(file.FileName) == strings.ToLower(README_FILE_NAME) {
path := Model_prefix + models.AttachmentRelativePath(model.ID) + "/"
body, err := storage.ObsDownloadAFile(setting.Bucket, path+file.FileName)
if err != nil {
log.Info("download file failed: %s\n", err.Error())
break
} else {
defer body.Close()
content, err = ioutil.ReadAll(body)
return string(content)
}
}
}
return ""
}

func QueryModelReadMe(ctx *context.Context) {
id := ctx.Query("id")
model, err := models.QueryModelById(id)
@@ -498,31 +595,13 @@ func QueryModelReadMe(ctx *context.Context) {
"code": "-1",
}
if err == nil {
files := queryOneLevelModelFile(model, "")
find := false
var content []byte
for _, file := range files {
if strings.ToLower(file.FileName) == strings.ToLower(README_FILE_NAME) {
find = true
path := Model_prefix + models.AttachmentRelativePath(id) + "/"
body, err := storage.ObsDownloadAFile(setting.Bucket, path+file.FileName)
if err != nil {
log.Info("download file failed: %s\n", err.Error())
break
} else {
defer body.Close()
content, err = ioutil.ReadAll(body)
}
}
}
mdContent := getMDContent(model)
metas := map[string]string{"include_toc": "true"}
if find {
if mdContent != "" {
re["isExistMDFile"] = "true"
re["fileName"] = README_FILE_NAME
strc := string(content)
re["content"] = strc

re["htmlcontent"] = string(markdown.RenderRaw([]byte(strc), "", false, metas))
re["content"] = mdContent
re["htmlcontent"] = string(markdown.RenderRaw([]byte(mdContent), "", false, metas))
} else {
re["isExistMDFile"] = "false"
re["fileName"] = README_FILE_NAME


+ 8
- 0
routers/repo/attachment.go View File

@@ -654,6 +654,12 @@ func GetSuccessChunks(ctx *context.Context) {
return
}

repo, err := models.GetRepositoryByID(dataset.RepoID)
if err != nil {
ctx.ServerError("GetRepositoryByID", err)
return
}

ctx.JSON(200, map[string]string{
"uuid": fileChunk.UUID,
"uploaded": strconv.Itoa(fileChunk.IsUploaded),
@@ -663,6 +669,8 @@ func GetSuccessChunks(ctx *context.Context) {
"datasetID": strconv.Itoa(int(attach.DatasetID)),
"fileName": attach.Name,
"datasetName": dataset.Title,
"repoName": repo.Name,
"repoOwner": repo.OwnerName,
})

}


+ 12
- 0
routers/repo/setting.go View File

@@ -272,6 +272,18 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeCloudBrain)
}

if form.EnableHPC && !models.UnitTypeHPC.UnitGlobalDisabled() {
units = append(units, models.RepoUnit{
RepoID: repo.ID,
Type: models.UnitTypeHPC,
Config: &models.HPCConfig{
EnableHPC: form.EnableHPC,
},
})
} else if !models.UnitTypeHPC.UnitGlobalDisabled() {
deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeHPC)
}

if form.EnableModelManager && !models.UnitTypeModelManage.UnitGlobalDisabled() {
units = append(units, models.RepoUnit{
RepoID: repo.ID,


+ 7
- 4
routers/routes/routes.go View File

@@ -831,6 +831,9 @@ func RegisterRoutes(m *macaron.Macaron) {

reqRepoCloudBrainReader := context.RequireRepoReader(models.UnitTypeCloudBrain)
reqRepoCloudBrainWriter := context.RequireRepoWriter(models.UnitTypeCloudBrain)
reqRepoHPCReader := context.RequireRepoReader(models.UnitTypeHPC)
reqRepoHPCWriter := context.RequireRepoWriter(models.UnitTypeHPC)

reqRepoModelManageReader := context.RequireRepoReader(models.UnitTypeModelManage)
reqRepoModelManageWriter := context.RequireRepoWriter(models.UnitTypeModelManage)
//reqRepoBlockChainReader := context.RequireRepoReader(models.UnitTypeBlockChain)
@@ -1306,10 +1309,10 @@ func RegisterRoutes(m *macaron.Macaron) {
})
}, context.RepoRef())
m.Group("/supercompute", func() {
m.Get("", reqRepoCloudBrainReader, super_compute.GetAPPList)
m.Get("/job", reqRepoCloudBrainReader, super_compute.GetJobList)
m.Get("/job/create", reqRepoCloudBrainWriter, super_compute.CreateUI)
m.Get("/job/:id", reqRepoCloudBrainReader, super_compute.GetDetailUI)
m.Get("", reqRepoHPCReader, super_compute.GetAPPList)
m.Get("/job", reqRepoHPCReader, super_compute.GetJobList)
m.Get("/job/create", reqRepoHPCWriter, super_compute.CreateUI)
m.Get("/job/:id", reqRepoHPCReader, super_compute.GetDetailUI)
})
m.Group("/grampus", func() {
m.Group("/notebook", func() {


+ 9
- 3
routers/user/auth.go View File

@@ -1691,7 +1691,7 @@ func ForgotPasswdPost(ctx *context.Context) {
return
}

if !u.IsLocal() && !u.IsOAuth2() {
if !u.IsLocal() && !u.IsOAuth2() && !u.IsCloudBrain() {
ctx.Data["Err_Email"] = true
ctx.RenderWithErr(ctx.Tr("auth.non_local_account"), tplForgotPassword, nil)
return
@@ -1841,7 +1841,10 @@ func ResetPasswdPost(ctx *context.Context) {
}
u.HashPassword(passwd)
u.MustChangePassword = false
if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt"); err != nil {
if u.LoginType == models.LoginCloudBrain {
u.LoginType = models.LoginNoType
}
if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt", "login_type"); err != nil {
ctx.ServerError("UpdateUser", err)
return
}
@@ -1911,7 +1914,10 @@ func ResetPasswdByPhonePost(ctx *context.Context, form auth.ResetPassWordByPhone
}
u.HashPassword(passwd)
u.MustChangePassword = false
if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt"); err != nil {
if u.LoginType == models.LoginCloudBrain {
u.LoginType = models.LoginNoType
}
if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt", "login_type"); err != nil {
ctx.ServerError("UpdateUser", err)
return
}


+ 15
- 9
services/ai_task_service/cluster/c2net.go View File

@@ -90,7 +90,7 @@ func convertNoteBookReq2Grampus(req entity.CreateNoteBookTaskRequest) models.Cre
codePath := "/code"
if len(req.Tasks[0].Code) > 0 {
codePath = req.Tasks[0].Code[0].ContainerPath
if strings.Contains(codePath, "/") {
if strings.HasSuffix(codePath, ".zip") {
codePath = codePath[0:strings.LastIndex(codePath, "/")]
}
}
@@ -445,20 +445,27 @@ func buildUnzipDatasetCommand(datasets []entity.ContainerData, datasetPath, comp
}
builder.Next(entity.NewCommand("cd", datasetPath)).
Next(entity.NewCommand("echo", "'start to unzip datasets'"))

fileDatasets := make([]entity.ContainerData, 0)
for _, dataset := range datasets {
if !dataset.IsDir {
fileDatasets = append(fileDatasets, dataset)
}
}
//单数据集
if len(datasets) == 1 {
if strings.HasSuffix(datasets[0].Name, ".tar.gz") {
builder.Next(entity.NewCommand("tar", "--strip-components=1", "-zxvf", "'"+datasets[0].Name+"'"))
if len(fileDatasets) == 1 {
if strings.HasSuffix(fileDatasets[0].Name, ".tar.gz") {
builder.Next(entity.NewCommand("tar", "--strip-components=1", "-zxvf", "'"+fileDatasets[0].Name+"'"))
} else {
builder.Next(entity.NewCommand("unzip", "-q", "'"+datasets[0].Name+"'"))
builder.Next(entity.NewCommand("unzip", "-q", "'"+fileDatasets[0].Name+"'"))
}
builder.Next(entity.NewCommand("ls", "-l"))
builder.Next(entity.NewCommand("echo", "'unzip datasets finished'"))
return builder
}
//多数据集
for i := 0; i < len(datasets); i++ {
name := datasets[i].Name
for i := 0; i < len(fileDatasets); i++ {
name := fileDatasets[i].Name
if strings.HasSuffix(name, ".tar.gz") {
builder.Next(entity.NewCommand("tar", "-zxvf", name))
} else {
@@ -492,9 +499,8 @@ func buildExecCodeCommand(codeDirPath, modelFilePath, bootFile, computeResource,
paramCode += " --'" + param.Label + "'='" + param.Value + "'"
}
if computeResource == models.NPU {
modelRemoteObsUrl := getNpuModelRemoteObsUrl(jobName)
builder.Next(entity.NewCommand("source", "/home/ma-user/.bashrc")).
Next(entity.NewCommand("python", "/home/ma-user/davinci/train/davincirun.py", "python", "/home/ma-user/openi.py", paramCode, "--model_url="+modelRemoteObsUrl))
Next(entity.NewCommand("python", "/home/ma-user/davinci/train/davincirun.py", "python", "/home/ma-user/grampus.py", paramCode))
} else if computeResource == models.GCU {
builder.Next(entity.NewCommand("cd", codeDirPath))
if modelFilePath != "" {


+ 9
- 8
services/ai_task_service/cluster/cloudbrain_two.go View File

@@ -2,6 +2,14 @@ package cluster

import "C"
import (
"encoding/json"
"errors"
"fmt"
"io"
"path"
"strconv"
"strings"

"code.gitea.io/gitea/entity"
"code.gitea.io/gitea/manager/client/cloudbrain_two"
"code.gitea.io/gitea/manager/client/cloudbrain_two_cd"
@@ -11,13 +19,6 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/services/ai_task_service/storage_helper"
"encoding/json"
"errors"
"fmt"
"io"
"path"
"strconv"
"strings"
)

type CloudbrainTwoClusterAdapter struct {
@@ -475,7 +476,7 @@ func handleCloudbrainTwoParameter(req entity.CreateTrainTaskRequest) models.Para
Value: multiModelUrl,
}, models.Parameter{
Label: modelarts.CkptUrl,
Value: t.Datasets[0].S3DownloadUrl,
Value: t.PreTrainModel[0].S3DownloadUrl,
})
}



+ 1
- 2
services/ai_task_service/container_builder/code_builder.go View File

@@ -59,8 +59,7 @@ func (b *CodeBuilder) Build(ctx *context.CreationContext) ([]entity.ContainerDat
}

var codeArchiveName, objectKey string
//如果代码是压缩包形式,挂载的是文件,以默认分支命名压缩包(继承原有逻辑)
if !b.Opts.Uncompressed {
if !b.Opts.Uncompressed && !b.Opts.VolumeFolder {
codeArchiveName = cloudbrain.DefaultBranchName + ".zip"
objectKey = path.Join(remoteDir, codeArchiveName)
} else {


+ 36
- 2
services/ai_task_service/container_builder/dataset_builder.go View File

@@ -1,13 +1,15 @@
package container_builder

import (
"path"
"strings"

"code.gitea.io/gitea/entity"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/routers/response"
"code.gitea.io/gitea/services/ai_task_service/context"
"path"
"strings"
"code.gitea.io/gitea/services/ai_task_service/storage_helper"
)

type DatasetBuilder struct {
@@ -41,6 +43,38 @@ func (b *DatasetBuilder) Build(ctx *context.CreationContext) ([]entity.Container
return nil, response.PARTIAL_DATASETS_NOT_AVAILABLE
}
var data []entity.ContainerData

//如果是智算GPU调试任务,需要把dataset文件夹也挂载,这样提交镜像时才不会把dataset下的文件提交到镜像中
if ctx.Request.Cluster == entity.C2Net && (ctx.Request.JobType == models.JobTypeDebug || ctx.Request.JobType == models.JobTypeTrain) && ctx.Request.ComputeSource.Name == models.GPU {
log.Info("mount dataset directory.")
jobName := ctx.Request.JobName
storageTypes := b.Opts.AcceptStorageType
uploader := storage_helper.SelectUploaderFromStorageType(storageTypes[0])
remoteDir := path.Join(uploader.GetJobDefaultObjectKeyPrefix(jobName), b.Opts.GetLocalPath())
err := uploader.MKDIR(remoteDir)
if err != nil {
log.Error("MKDIR err.displayJobName = %s err=%v", ctx.Request.DisplayJobName, err)
return nil, response.NewBizError(err)
}

datasetDirectoryObjectKey := remoteDir
if !strings.HasSuffix(remoteDir, "/") {
datasetDirectoryObjectKey = remoteDir + "/"
}
data = append(data, entity.ContainerData{
ContainerPath: b.Opts.ContainerPath,
Name: "dataset",
ReadOnly: false,
ObjectKey: datasetDirectoryObjectKey,
RealPath: uploader.GetRealPath(remoteDir),
Bucket: uploader.GetBucket(),
EndPoint: uploader.GetEndpoint(),
GetBackEndpoint: uploader.GetEndpoint(),
IsDir: true,
StorageType: storageTypes[0],
})
}

for _, datasetInfo := range datasetInfos {
var name, objectKey, s3DownloadUrl string
//如果不是压缩包,那么文件名是去掉后缀以后的数据集名称


+ 71
- 61
services/ai_task_service/schedule/model_schedule.go View File

@@ -10,8 +10,6 @@ import (
"strings"
"time"

"code.gitea.io/gitea/modules/modelarts"

"code.gitea.io/gitea/modules/obs"

"code.gitea.io/gitea/models"
@@ -167,12 +165,6 @@ func HandleUnfinishedMigrateRecord(r *models.ModelMigrateRecord) error {
}
}

if r.CurrentStep == models.BucketMoving {
//尝试查询NPU结果目录下是否有文件,有文件则认为已经解压成功
if cloudbrain.ComputeResource == models.NPUResource && IsNPUModelDirHasFile(cloudbrain.JobName, cloudbrain.VersionName) {
TryToUpdateNPUMoveBucketResult(r, cloudbrain.JobName, cloudbrain.VersionName)
}
}
return nil
}

@@ -203,66 +195,28 @@ func LocalMigrateOperate(jobName, computeSource string, r *models.ModelMigrateRe
log.Error("UpdateModelMigrateStatusByStep err. r.ID=%d step=%d err=%v", r.ID, models.BucketMoveFailed, err)
return err
}
//因为调度无法指定桶,所以调度成功后我们还需要移桶
if computeSource == models.NPUResource {
//因为NPU的输出会被压缩,因此需要解压+移桶
if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.OutputPath); err != nil {
log.Error("Failed to obsMkdir_output: %s (%v)", jobName, err)

return err
}
log.Info("DestObjectKey", r.DestObjectKey)
if strings.Contains(r.DestObjectKey, ".") {
isExists, _ := storage.IsObjectExist4Obs(r.DestBucket, r.DestObjectKey)
if !isExists {
//此时没有文件需要解压迁移,直接更新为成功
models.UpdateModelMigrateStatusByStep(r, models.BucketMoveSuccess)
targetObjectPrefix := strings.TrimSuffix(r.DestObjectKey, models.ModelSuffix)
if err := MoveBucketInOpenIOBS(r.DestObjectKey, targetObjectPrefix, r.DestBucket, setting.Bucket); err != nil {
log.Error("MoveBucketInOpenIMinio err.%v", err)
if tmpErr := models.UpdateModelMigrateStatusByStep(r, models.BucketMoveFailed); tmpErr != nil {
log.Error("UpdateModelMigrateStatusByStep error. r.ID=%d step=%d err=%v", r.ID, models.BucketMoveFailed, tmpErr)
}
decompress(r.DestBucket+"/"+r.DestObjectKey, setting.Bucket+"/"+strings.TrimSuffix(r.DestObjectKey, models.ModelSuffix))

} else { //如果是文件夹,遍历文件
fileInfos, err := storage.GetOneLevelObjectsUnderDir(r.DestBucket, "", r.DestObjectKey)
if err != nil {
log.Error("UpdateModelMigrateStatusByStep err. r.ID=%d step=%d err=%v", r.ID, models.BucketMoveFailed, err)
return err
}
if len(fileInfos) == 0 {
//此时没有文件需要解压迁移,直接更新为成功
models.UpdateModelMigrateStatusByStep(r, models.BucketMoveSuccess)
}
for _, fileInfo := range fileInfos {
log.Info("decompress file:", fileInfo.FileName)
sourceFilPath := r.DestBucket + "/" + r.DestObjectKey + fileInfo.FileName
if !strings.HasSuffix(r.DestObjectKey, "/") {
sourceFilPath = r.DestBucket + "/" + r.DestObjectKey + "/" + fileInfo.FileName
}
decompress(sourceFilPath, setting.Bucket+"/"+strings.TrimSuffix(r.DestObjectKey, models.ModelSuffix))
}

return err
}

} else {
//因为调度无法指定桶,所以调度成功后我们还需要移桶
if setting.UseLocalMinioMigrate {
if err := MoveBucketJust4LocalMinio(r.DestObjectKey, grampus.GetGPUModelObjectKey(jobName), r.DestBucket, setting.Attachment.Minio.Bucket); err != nil {
log.Error("MoveBucketJust4LocalMinio err.%v", err)
if tmpErr := models.UpdateModelMigrateStatusByStep(r, models.BucketMoveFailed); tmpErr != nil {
log.Error("UpdateModelMigrateStatusByStep error. r.ID=%d step=%d err=%v", r.ID, models.BucketMoveFailed, tmpErr)
}
return err
}
} else {
if err := MoveBucketInOpenIMinio(r.DestObjectKey, grampus.GetGPUModelObjectKey(jobName), r.DestBucket, setting.Attachment.Minio.Bucket); err != nil {
log.Error("MoveBucketInOpenIMinio err.%v", err)
if tmpErr := models.UpdateModelMigrateStatusByStep(r, models.BucketMoveFailed); tmpErr != nil {
log.Error("UpdateModelMigrateStatusByStep error. r.ID=%d step=%d err=%v", r.ID, models.BucketMoveFailed, tmpErr)
}
return err
if err := MoveBucketInOpenIMinio(r.DestObjectKey, grampus.GetGPUModelObjectKey(jobName), r.DestBucket, setting.Attachment.Minio.Bucket); err != nil {
log.Error("MoveBucketInOpenIMinio err.%v", err)
if tmpErr := models.UpdateModelMigrateStatusByStep(r, models.BucketMoveFailed); tmpErr != nil {
log.Error("UpdateModelMigrateStatusByStep error. r.ID=%d step=%d err=%v", r.ID, models.BucketMoveFailed, tmpErr)
}
return err
}

if err := models.UpdateModelMigrateStatusByStep(r, models.BucketMoveSuccess); err != nil {
log.Error("UpdateModelMigrateStatusByStep error. r.ID=%d step=%d err=%v", r.ID, models.BucketMoveSuccess, err)
}
}
if err := models.UpdateModelMigrateStatusByStep(r, models.BucketMoveSuccess); err != nil {
log.Error("UpdateModelMigrateStatusByStep error. r.ID=%d step=%d err=%v", r.ID, models.BucketMoveSuccess, err)
}
return nil
}
@@ -336,6 +290,40 @@ func MoveBucketInOpenIMinio(objectKeyPrefix, targetObjectPrefix, oldBucket, newB
return nil
}

func MoveBucketInOpenIOBS(objectKeyPrefix, targetObjectPrefix, oldBucket, newBucket string) error {
var client = storage.ObsCli

input := &obs.ListObjectsInput{}
input.Bucket = oldBucket
input.MaxKeys = 1000
input.Prefix = objectKeyPrefix
output, err := client.ListObjects(input)
if err != nil {
log.Error("MoveBucketInOpenIOBS list objects error.err=%v", err)
return err
}

log.Info("MoveBucketInOpenIOBS start.objectKeyPrefix=%s", objectKeyPrefix)
count := 0
for _, object := range output.Contents {
if object.Key == input.Prefix {
continue
}
if strings.HasSuffix(object.Key, "/") {
continue
}
log.Debug("MoveBucketInOpenIOBS object.Key=%s", object.Key)
newObjectKey := strings.Replace(object.Key, objectKeyPrefix, targetObjectPrefix, 1)
err := MoveOBSFileBucket(client, object.Key, newObjectKey, oldBucket, newBucket)
if err != nil {
log.Error("MoveBucketInOpenIOBS MoveOBSFileBucket object.Key=%s Err=%v", object.Key, err)
continue
}
}
log.Info("MoveBucketInOpenIOBS finished.objectKeyPrefix=%s ,total=%d", objectKeyPrefix, count)
return nil
}

func MoveBucketJust4LocalMinio(objectKeyPrefix, targetObjectPrefix, oldBucket, newBucket string) error {
oldPath := path.Join(setting.Attachment.Minio.RealPath, oldBucket, objectKeyPrefix)
newPath := path.Join(setting.Attachment.Minio.RealPath, newBucket, targetObjectPrefix)
@@ -387,6 +375,28 @@ func MoveMinioFileBucket(core *minio.Core, oldObjectKey, newObjectKey, oldBucket
return err
}

func MoveOBSFileBucket(client *obs.ObsClient, oldObjectKey, newObjectKey, oldBucket, newBucket string) error {
input := &obs.CopyObjectInput{}
input.Bucket = newBucket
input.Key = newObjectKey
input.CopySourceBucket = oldBucket
input.CopySourceKey = oldObjectKey
_, err := client.CopyObject(input)

if err != nil {
log.Error("MoveOBSFileBucket CopyObject err oldObjectKey=%s .%v", oldObjectKey, err)
return err
}
delObj := &obs.DeleteObjectInput{}
delObj.Bucket = oldBucket
delObj.Key = oldObjectKey
_, err = client.DeleteObject(delObj)
if err != nil {
log.Error("MoveOBSFileBucket DeleteObject err oldObjectKey=%s .%v", oldObjectKey, err)
}
return err
}

type DecompressReq struct {
SourceFile string `json:"source_file"`
DestPath string `json:"dest_path"`


+ 6
- 2
services/ai_task_service/task/cloudbrain_one_notebook_task.go View File

@@ -146,8 +146,12 @@ func (g CloudbrainOneNotebookTaskTemplate) CallCreationAPI(ctx *context.Creation
OutPut: ctx.GetContainerDataArray(entity.ContainerOutPutPath),
AutoStopDuration: autoStopDurationMs,
Capacity: setting.Capacity,
CenterID: ctx.Spec.GetAvailableCenterIds(ctx.User.ID, form.JobType),
Spec: ctx.Spec,
CenterID: ctx.Spec.GetAvailableCenterIds(models.GetAvailableCenterIdOpts{
UserId: ctx.User.ID,
JobType: g.JobType,
HasInternet: form.HasInternet,
}),
Spec: ctx.Spec,
},
},
}


+ 21
- 13
services/ai_task_service/task/cloudbrain_one_train_task.go View File

@@ -108,13 +108,17 @@ func (g CloudbrainOneTrainTaskTemplate) CallCreationAPI(ctx *context.CreationCon
ImageUrl: strings.TrimSpace(form.ImageUrl),
Datasets: ctx.GetContainerDataArray(entity.ContainerDataset),
Code: ctx.GetContainerDataArray(entity.ContainerCode),
CenterID: ctx.Spec.GetAvailableCenterIds(ctx.User.ID, form.JobType),
PreTrainModel: ctx.GetContainerDataArray(entity.ContainerPreTrainModel),
BootFile: form.BootFile,
OutPut: ctx.GetContainerDataArray(entity.ContainerOutPutPath),
LogPath: ctx.GetContainerDataArray(entity.ContainerLogPath),
Params: form.ParamArray,
Spec: ctx.Spec,
CenterID: ctx.Spec.GetAvailableCenterIds(models.GetAvailableCenterIdOpts{
UserId: ctx.User.ID,
JobType: g.JobType,
HasInternet: form.HasInternet,
}),
PreTrainModel: ctx.GetContainerDataArray(entity.ContainerPreTrainModel),
BootFile: form.BootFile,
OutPut: ctx.GetContainerDataArray(entity.ContainerOutPutPath),
LogPath: ctx.GetContainerDataArray(entity.ContainerLogPath),
Params: form.ParamArray,
Spec: ctx.Spec,
},
},
}
@@ -149,12 +153,16 @@ func (g CloudbrainOneTrainTaskTemplate) CallRestartAPI(ctx *context.CreationCont
ImageUrl: strings.TrimSpace(form.ImageUrl),
Datasets: ctx.GetContainerDataArray(entity.ContainerDataset),
Code: ctx.GetContainerDataArray(entity.ContainerCode),
CenterID: ctx.Spec.GetAvailableCenterIds(ctx.User.ID, form.JobType),
PreTrainModel: ctx.GetContainerDataArray(entity.ContainerPreTrainModel),
BootFile: form.BootFile,
OutPut: ctx.GetContainerDataArray(entity.ContainerOutPutPath),
Params: form.ParamArray,
Spec: ctx.Spec,
CenterID: ctx.Spec.GetAvailableCenterIds(models.GetAvailableCenterIdOpts{
UserId: ctx.User.ID,
JobType: g.JobType,
HasInternet: form.HasInternet,
}),
PreTrainModel: ctx.GetContainerDataArray(entity.ContainerPreTrainModel),
BootFile: form.BootFile,
OutPut: ctx.GetContainerDataArray(entity.ContainerOutPutPath),
Params: form.ParamArray,
Spec: ctx.Spec,
},
},
}


+ 4
- 3
services/ai_task_service/task/cloudbrain_two_notebook_task.go View File

@@ -168,18 +168,19 @@ func (g CloudbrainTwoNotebookTaskTemplate) CallRestartAPI(ctx *context.CreationC
return nil
}

func (g CloudbrainTwoNotebookTaskTemplate) GetSpecs(userId int64, computeSource models.ComputeSource) ([]*api.SpecificationShow, *response.BizError) {
func (g CloudbrainTwoNotebookTaskTemplate) GetSpecs(opts entity.GetSpecOpts) ([]*api.SpecificationShow, *response.BizError) {
var aiCenterCode = models.AICenterOfCloudBrainTwo
if setting.ModelartsCD.Enabled {
aiCenterCode = models.AICenterOfChengdu
}
var specs []*models.Specification
var err error
specs, err = resource.FindAvailableSpecs(userId, models.FindSpecsOptions{
specs, err = resource.FindAvailableSpecs(opts.UserId, models.FindSpecsOptions{
JobType: g.JobType,
ComputeResource: computeSource.Name,
ComputeResource: opts.ComputeSource.Name,
Cluster: g.ClusterType.GetParentCluster(),
AiCenterCode: aiCenterCode,
HasInternet: opts.HasInternet,
})

if err != nil {


+ 12
- 8
services/ai_task_service/task/cloudbrain_two_train_task.go View File

@@ -114,14 +114,18 @@ func (g CloudbrainTwoTrainTaskTemplate) CallCreationAPI(ctx *context.CreationCon
Description: form.Description,
Tasks: []entity.TrainTask{
{
Name: form.JobName,
ResourceSpecId: ctx.Spec.SourceSpecId,
ImageId: form.ImageID,
ImageUrl: strings.TrimSpace(form.ImageUrl),
Datasets: ctx.GetContainerDataArray(entity.ContainerDataset),
Code: ctx.GetContainerDataArray(entity.ContainerCode),
LogPath: ctx.GetContainerDataArray(entity.ContainerLogPath),
CenterID: ctx.Spec.GetAvailableCenterIds(ctx.User.ID, form.JobType),
Name: form.JobName,
ResourceSpecId: ctx.Spec.SourceSpecId,
ImageId: form.ImageID,
ImageUrl: strings.TrimSpace(form.ImageUrl),
Datasets: ctx.GetContainerDataArray(entity.ContainerDataset),
Code: ctx.GetContainerDataArray(entity.ContainerCode),
LogPath: ctx.GetContainerDataArray(entity.ContainerLogPath),
CenterID: ctx.Spec.GetAvailableCenterIds(models.GetAvailableCenterIdOpts{
UserId: ctx.User.ID,
JobType: g.JobType,
HasInternet: form.HasInternet,
}),
PreTrainModel: ctx.GetContainerDataArray(entity.ContainerPreTrainModel),
BootFile: form.BootFile,
OutPut: ctx.GetContainerDataArray(entity.ContainerOutPutPath),


+ 28
- 2
services/ai_task_service/task/grampus_notebook_task.go View File

@@ -39,6 +39,7 @@ func GetGrampusNoteBookConfig(opts entity.AITaskConfigKey) *entity.AITaskBaseCon
ContainerPath: codePath,
ReadOnly: false,
AcceptStorageType: []entity.StorageType{entity.MINIO, entity.OBS},
VolumeFolder: true,
},
entity.ContainerDataset: {
ContainerPath: datasetPath,
@@ -52,6 +53,27 @@ func GetGrampusNoteBookConfig(opts entity.AITaskConfigKey) *entity.AITaskBaseCon
},
},
}
if opts.ComputeSource == models.CPU {
config = &entity.AITaskBaseConfig{
ContainerSteps: map[entity.ContainerDataType]*entity.ContainerBuildOpts{
entity.ContainerCode: {
ContainerPath: codePath,
ReadOnly: false,
AcceptStorageType: []entity.StorageType{entity.MINIO},
},
entity.ContainerDataset: {
ContainerPath: datasetPath,
ReadOnly: true,
AcceptStorageType: []entity.StorageType{entity.MINIO, entity.OBS},
},
entity.ContainerPreTrainModel: {
ContainerPath: pretrainModelPath,
ReadOnly: true,
AcceptStorageType: []entity.StorageType{entity.MINIO, entity.OBS},
},
},
}
}

if opts.ComputeSource == models.NPU || opts.ComputeSource == models.DCU {
config = &entity.AITaskBaseConfig{
@@ -189,8 +211,12 @@ func (g GrampusNoteBookTaskTemplate) CallCreationAPI(ctx *context.CreationContex
Code: ctx.GetContainerDataArray(entity.ContainerCode),
AutoStopDuration: autoStopDurationMs,
Capacity: setting.Capacity,
CenterID: ctx.Spec.GetAvailableCenterIds(ctx.User.ID, form.JobType),
Spec: ctx.Spec,
CenterID: ctx.Spec.GetAvailableCenterIds(models.GetAvailableCenterIdOpts{
UserId: ctx.User.ID,
JobType: g.JobType,
HasInternet: form.HasInternet,
}),
Spec: ctx.Spec,
},
},
}


+ 8
- 3
services/ai_task_service/task/grampus_online_infer_task.go View File

@@ -119,9 +119,13 @@ func (g GrampusOnlineInferTaskTemplate) CallCreationAPI(ctx *context.CreationCon
OutPut: ctx.GetContainerDataArray(entity.ContainerOutPutPath),
AutoStopDuration: -1,
Capacity: setting.Capacity,
CenterID: ctx.Spec.GetAvailableCenterIds(ctx.User.ID, models.JobTypeOnlineInference),
Spec: ctx.Spec,
BootFile: ctx.Request.BootFile,
CenterID: ctx.Spec.GetAvailableCenterIds(models.GetAvailableCenterIdOpts{
UserId: ctx.User.ID,
JobType: g.JobType,
HasInternet: form.HasInternet,
}),
Spec: ctx.Spec,
BootFile: ctx.Request.BootFile,
},
},
}
@@ -150,6 +154,7 @@ func (g GrampusOnlineInferTaskTemplate) LoadSpec(ctx *context.CreationContext) *
JobType: models.JobTypeDebug,
ComputeResource: ctx.Request.ComputeSource.Name,
Cluster: ctx.Request.Cluster.GetParentCluster(),
HasInternet: ctx.Request.HasInternet,
})
if err != nil || spec == nil {
return response.SPEC_NOT_AVAILABLE


+ 21
- 13
services/ai_task_service/task/grampus_train_task.go View File

@@ -140,13 +140,17 @@ func (g GrampusTrainTaskTemplate) CallCreationAPI(ctx *context.CreationContext)
DisplayJobName: form.DisplayJobName,
Tasks: []entity.TrainTask{
{
Name: form.JobName,
ResourceSpecId: ctx.Spec.SourceSpecId,
ImageId: form.ImageID,
ImageUrl: imageUrl,
Datasets: ctx.GetContainerDataArray(entity.ContainerDataset),
Code: ctx.GetContainerDataArray(entity.ContainerCode),
CenterID: ctx.Spec.GetAvailableCenterIds(ctx.User.ID, form.JobType),
Name: form.JobName,
ResourceSpecId: ctx.Spec.SourceSpecId,
ImageId: form.ImageID,
ImageUrl: imageUrl,
Datasets: ctx.GetContainerDataArray(entity.ContainerDataset),
Code: ctx.GetContainerDataArray(entity.ContainerCode),
CenterID: ctx.Spec.GetAvailableCenterIds(models.GetAvailableCenterIdOpts{
UserId: ctx.User.ID,
JobType: g.JobType,
HasInternet: form.HasInternet,
}),
PreTrainModel: ctx.GetContainerDataArray(entity.ContainerPreTrainModel),
BootFile: form.BootFile,
OutPut: ctx.GetContainerDataArray(entity.ContainerOutPutPath),
@@ -189,12 +193,16 @@ func (g GrampusTrainTaskTemplate) CallRestartAPI(ctx *context.CreationContext) *
ImageUrl: strings.TrimSpace(form.ImageUrl),
Datasets: ctx.GetContainerDataArray(entity.ContainerDataset),
Code: ctx.GetContainerDataArray(entity.ContainerCode),
CenterID: ctx.Spec.GetAvailableCenterIds(ctx.User.ID, form.JobType),
PreTrainModel: ctx.GetContainerDataArray(entity.ContainerPreTrainModel),
BootFile: form.BootFile,
OutPut: ctx.GetContainerDataArray(entity.ContainerOutPutPath),
Params: form.ParamArray,
Spec: ctx.Spec,
CenterID: ctx.Spec.GetAvailableCenterIds(models.GetAvailableCenterIdOpts{
UserId: ctx.User.ID,
JobType: g.JobType,
HasInternet: form.HasInternet,
}),
PreTrainModel: ctx.GetContainerDataArray(entity.ContainerPreTrainModel),
BootFile: form.BootFile,
OutPut: ctx.GetContainerDataArray(entity.ContainerOutPutPath),
Params: form.ParamArray,
Spec: ctx.Spec,
},
},
}


+ 1
- 0
services/ai_task_service/task/opt_handler.go View File

@@ -358,6 +358,7 @@ func (DefaultCreationHandler) LoadSpec(ctx *context.CreationContext) *response.B
JobType: ctx.Request.JobType,
ComputeResource: ctx.Request.ComputeSource.Name,
Cluster: ctx.Request.Cluster.GetParentCluster(),
HasInternet: ctx.Request.HasInternet,
})
if err != nil || spec == nil {
return response.SPEC_NOT_AVAILABLE


+ 5
- 4
services/ai_task_service/task/task_base.go View File

@@ -61,7 +61,7 @@ type AITaskTemplate interface {
GetOperationProfile(cloudbrainId int64) (*entity.OperationProfile, *response.BizError)
GetResourceUsage(opts entity.GetResourceUsageOpts) (*entity.ResourceUsage, *response.BizError)
GetImages(computeSource models.ComputeSource) ([]entity.ClusterImage, bool, *response.BizError)
GetSpecs(userId int64, computeSource models.ComputeSource) ([]*api.SpecificationShow, *response.BizError)
GetSpecs(opts entity.GetSpecOpts) ([]*api.SpecificationShow, *response.BizError)
GetConfig(opts entity.AITaskConfigKey) *entity.AITaskBaseConfig
GetNodeInfo(cloudbrainId int64) ([]entity.AITaskNodeInfo, *response.BizError)
GetAllowedWorkerNum(userId int64, computeSource *models.ComputeSource) ([]int, *response.BizError)
@@ -484,11 +484,12 @@ func (g DefaultAITaskTemplate) GetImages(computeSource models.ComputeSource) ([]
return images, customFlag, nil
}

func (g DefaultAITaskTemplate) GetSpecs(userId int64, computeSource models.ComputeSource) ([]*api.SpecificationShow, *response.BizError) {
specs, err := resource.FindAvailableSpecs(userId, models.FindSpecsOptions{
func (g DefaultAITaskTemplate) GetSpecs(opts entity.GetSpecOpts) ([]*api.SpecificationShow, *response.BizError) {
specs, err := resource.FindAvailableSpecs(opts.UserId, models.FindSpecsOptions{
JobType: g.JobType,
ComputeResource: computeSource.Name,
ComputeResource: opts.ComputeSource.Name,
Cluster: g.ClusterType.GetParentCluster(),
HasInternet: opts.HasInternet,
})
if err != nil {
log.Error("GetSpecs err.%v", err)


+ 25
- 3
services/ai_task_service/task/task_creation_info.go View File

@@ -6,6 +6,7 @@ import (
"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/response"
"code.gitea.io/gitea/services/cloudbrain/cloudbrainTask"
"code.gitea.io/gitea/services/reward/point/account"
@@ -59,10 +60,31 @@ func GetAITaskCreationInfo(req entity.GetAITaskCreationInfoReq) (*entity.Creatio
result.Images = images
result.CanUseAllImages = canUseAll
}
//查询资源规格
if specs, err := t.GetSpecs(req.User.ID, *req.ComputeSource); err == nil {
result.Specs = specs
specsMap := make(map[string][]*structs.SpecificationShow, 0)
//查询有网资源规格
if specs, err := t.GetSpecs(entity.GetSpecOpts{
UserId: req.User.ID,
ComputeSource: *req.ComputeSource,
HasInternet: 2, //0 all;1 no internet;2 has internet
}); err == nil {
specsMap["has_internet"] = specs
}
//查询无网资源规格
if specs, err := t.GetSpecs(entity.GetSpecOpts{
UserId: req.User.ID,
ComputeSource: *req.ComputeSource,
HasInternet: 1, //0 all;1 no internet;2 has internet
}); err == nil {
specsMap["no_internet"] = specs
}
//查询所有资源规格
if specs, err := t.GetSpecs(entity.GetSpecOpts{
UserId: req.User.ID,
ComputeSource: *req.ComputeSource,
}); err == nil {
specsMap["all"] = specs
}
result.Specs = specsMap
result.Config = entity.AITaskCreationConfig{
DatasetMaxSize: setting.DebugAttachSize * 1000 * 1000 * 1000,
}


+ 10
- 1
services/cloudbrain/resource/resource_queue.go View File

@@ -16,9 +16,10 @@ func AddResourceQueue(req models.ResourceQueueReq) error {
}

func UpdateResourceQueue(queueId int64, req models.ResourceQueueReq) error {
if _, err := models.UpdateResourceCardsTotalNum(queueId, models.ResourceQueue{
if _, err := models.UpdateResourceCardsTotalNumAndInternetStatus(queueId, models.ResourceQueue{
CardsTotalNum: req.CardsTotalNum,
Remark: req.Remark,
HasInternet: req.HasInternet,
}); err != nil {
return err
}
@@ -81,6 +82,12 @@ func SyncGrampusQueue(doerId int64) error {
return err
}

var hasInternet int
if center.IsNetAccess {
hasInternet = 2
} else {
hasInternet = 1
}
if oldQueue == nil {
queueInsertList = append(queueInsertList, models.ResourceQueue{
Cluster: models.C2NetCluster,
@@ -89,6 +96,7 @@ func SyncGrampusQueue(doerId int64) error {
ComputeResource: computeResource,
AccCardType: accCardType,
IsAutomaticSync: true,
HasInternet: hasInternet,
CreatedBy: doerId,
UpdatedBy: doerId,
})
@@ -100,6 +108,7 @@ func SyncGrampusQueue(doerId int64) error {
AiCenterName: center.Name,
AccCardType: accCardType,
UpdatedBy: doerId,
HasInternet: hasInternet,
})
}



+ 3
- 0
services/cloudbrain/resource/resource_specification.go View File

@@ -281,17 +281,20 @@ func FindAvailableSpecs4Show(userId int64, opts models.FindSpecsOptions) ([]*api
}

func GetAndCheckSpec(userId int64, specId int64, opts models.FindSpecsOptions) (*models.Specification, error) {
log.Info("FindAvailableSpecs try to get spec. opts=%+v specId=%d ", opts, specId)
if specId == 0 {
return nil, nil
}
opts.SpecId = specId
r, err := FindAvailableSpecs(userId, opts)
log.Info("FindAvailableSpecs result=%+v", r)
if err != nil {
return nil, err
}
if r == nil || len(r) == 0 {
return nil, nil
}
log.Info("FindAvailableSpecs final result r[0]=%+v", r[0])
return r[0], nil
}



+ 1
- 0
templates/base/footer_content.tmpl View File

@@ -91,6 +91,7 @@
<div class="ui grid" style="margin-top:2rem;margin-bottom:2rem;border-top:1px solid #d6d6d6;">
<div class="sixteen wide mobile sixteen wide tablet wide computer column" style=" margin:1.0rem 0;text-align:center;">
{{.i18n.Tr "custom.foot.copyright"}} <a href="http://beian.miit.gov.cn/" target="_blank">京ICP备18004880号</a>
<a href="https://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010802042693" target="_blank"><img src="https://openi.org.cn/uploadfile/2023/0724/20230724154330922.png" style="width:18px;margin-left:5px;vertical-align:middle"> 京公网安备 11010802042693号</a>
<br>
{{.i18n.Tr "home.powerdby"}}<a href="https://www.trustie.net/" target="_blank">Trustie确实</a>{{.i18n.Tr "、Gitea"}}
<br>


+ 1
- 0
templates/base/footer_content_fluid.tmpl View File

@@ -61,6 +61,7 @@
</div>
<div class="sixteen wide mobile eight wide tablet eight wide computer column" style=" margin:2.0rem 0">
{{.i18n.Tr "custom.foot.copyright"}} <a href="http://beian.miit.gov.cn/" target="_blank">京ICP备18004880号</a>
<a href="https://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010802042693" target="_blank"><img src="https://openi.org.cn/uploadfile/2023/0724/20230724154330922.png" style="width:18px;margin-left:5px;vertical-align:middle"> 京公网安备 11010802042693号</a>
<br>
{{.i18n.Tr "Powered_by 鹏城实验室云脑、"}}<a href="https://www.trustie.net/" target="_blank">Trustie确实</a>{{.i18n.Tr "、gitea"}}
<br>


+ 6
- 4
templates/repo/datasets/index.tmpl View File

@@ -353,14 +353,15 @@
<span class="ui basic blue button" style="color: #fa8c16 !important;"
@click="setPrivate('{{.UUID}}',true,{{$k}})"
v-else="privates[{{$k}}]">{{$.i18n.Tr "dataset.set_private"}}</span>
{{end}}
{{end}}
{{ if $.IsSigned }}
<a class="ui basic blue button">
<el-dropdown size="medium">
<span class="el-dropdown-link">
{{$.i18n.Tr "repo.more"}}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="copyUrl('{{.DownloadURL}}')">{{$.i18n.Tr "dataset.copy_url"}}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="copyUrl('{{.S3DownloadURL}}')">{{$.i18n.Tr "dataset.copy_url"}}
</el-dropdown-item>
{{if and ($.CanWrite) (eq .DecompressState 1) }}
<el-dropdown-item @click.native="gotoAnnotate('{{$.RepoLink}}','{{.UUID}}',{{.Type}})">
@@ -376,7 +377,8 @@
{{end}}
</el-dropdown-menu>
</el-dropdown>
</a>
</a>
{{ end }}
</div>
</div>
</div>


+ 2
- 2
templates/repo/header.tmpl View File

@@ -201,9 +201,9 @@
</span>
</a>
{{end}}
{{if .Permission.CanRead $.UnitTypeCloudBrain}}
{{if .Permission.CanRead $.UnitTypeHPC}}
<a class="{{if .PageIsSuperCompute}}active{{end}} item" href="{{.RepoLink}}/supercompute">
<span>
<span>
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path d="M14 18V20L16 21V22H8L7.99639 21.0036L10 20V18H2.9918C2.44405 18 2 17.5511 2 16.9925V4.00748C2 3.45107 2.45531 3 2.9918 3H21.0082C21.556 3 22 3.44892 22 4.00748V16.9925C22 17.5489 21.5447 18 21.0082 18H14ZM4 5V14H20V5H4Z"></path></svg>
{{.i18n.Tr "repo.superCompute"}}
</span>


+ 41
- 0
templates/repo/modelmanage/create_online.tmpl View File

@@ -126,6 +126,19 @@
<ul id="treeDemo" class="ztree"></ul>
</div>
</div>
</div>
<div class="inline fields">
<div class="two wide field right aligned">
<label for="license">{{.i18n.Tr "repo.model.manage.modellicense"}} &nbsp</label>
</div>
<div class="ui ten wide field dropdown selection" id="choice_License">
<input class="ays-ignore" type="hidden" id="license" name="license" required>
<div class="default text newtext">{{.i18n.Tr "repo.model.manage.select.modellicense"}}</div>
<i class="dropdown icon"></i>
<div class="menu" id="job-license">

</div>
</div>
</div>
<div class="inline fields">
<div class="two wide field right aligned">
@@ -298,6 +311,7 @@
$("#job-name").empty()
createModelName()
loadTrainList()
initLicense()

$(function () {
$('#choice_model').dropdown({
@@ -522,6 +536,33 @@
}
}

function initLicense() {
$('#choice_License').dropdown('clear')
$('#choice_License').dropdown({ clearable: true })
$("#job-license").empty()
$.get(`/dashboard/invitation?filename=model/license.json`, (data) => {
try {
const license = JSON.parse(data) || [];
let itemHtml = '';
for (let i = 0, iLen = license.length; i < iLen; i++) {
const item = license[i];
if (i == 0) {
itemHtml = itemHtml + '<option class="active item" data-value="' + item.id + '">' + item.name + '</option>';
} else {
itemHtml = itemHtml + '<option class="item" data-value="' + item.id + '">' + item.name + '</option>';
}
}
if (license.length) {
$('#choice_License .default.text').text(license[0].name);
$('#choice_License input[name="license"]').val(license[0].id)
$("#job-license").append(itemHtml);
}
} catch(err) {
console.log(err);
}
});
}

function check() {
let jobid = document.getElementById("jobId").value;
let versionname = document.getElementById("versionName").value;


+ 8
- 0
templates/repo/settings/options.tmpl View File

@@ -199,6 +199,14 @@
<label>{{.i18n.Tr "repo.settings.cloudbrain_desc"}}</label>
</div>
</div>
{{$isHPCEnabled := .Repository.UnitEnabled $.UnitTypeHPC }}
<div class="inline field">
<label>{{.i18n.Tr "repo.superCompute"}}</label>
<div class="ui checkbox">
<input class="enable-system" name="enable_hpc" type="checkbox" {{if $isHPCEnabled}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.hpc_desc"}}</label>
</div>
</div>
{{$isWikiEnabled := or (.Repository.UnitEnabled $.UnitTypeWiki) (.Repository.UnitEnabled $.UnitTypeExternalWiki)}}
<div class="inline field">
<label>{{.i18n.Tr "repo.wiki"}}</label>


+ 9
- 1
web_src/vuepages/apis/modules/modelmanage.js View File

@@ -1,6 +1,5 @@
import service from "../service";
import Qs from 'qs';
import { param } from "jquery";

// 保存本地模型
export const saveLocalModel = (data) => {
@@ -163,3 +162,12 @@ export const getModelEvolutionMap = (params) => {
params: { id: params.id },
});
}

// 获取平台模型许可证列表
export const getModelLicenseList = () => {
return service({
url: `/dashboard/invitation`,
method: 'get',
params: { filename: 'model/license.json' },
});
}

+ 692
- 0
web_src/vuepages/components/cloudbrain/ModelSelectV2.vue View File

@@ -0,0 +1,692 @@
<template>
<div class="form-row">
<div class="title" v-if="showTitle"><span :class="required ? 'required' : ''">{{ $t('modelObj.model_label') }}</span>
</div>
<div class="content">
<div class="model-list-c" :class="errStatus ? 'error' : ''">
<div class="model-item" v-for="(item) in selectList" :key="item.id" :title="item.name">
{{ item.name }}
</div>
<div v-if="selectList.length == 0" class="model-item-placeholder">
{{ $t('modelObj.model_label') }}
</div>
</div>
</div>
<div class="right-area">
<div class="btn-select" @click="dlgShow = true">
<i class="el-icon-plus"></i>
<span>{{ $t('modelObj.model_select') }}</span>
</div>
</div>
<el-dialog class="model-dlg" :visible.sync="dlgShow" :title="$t('modelObj.model_select')" width="1000px" :modal="true"
:close-on-click-modal="false" :show-close="true" :destroy-on-close="false" :before-close="beforeClose" @open="open"
@closed="closed">
<div class="dlg-content">
<div class="dlg-left-area" v-loading="dlgLoading">
<div class="model-tabs-c">
<el-tabs class="model-tabs" v-model="dlgActiveName" @tab-click="dlgTabClick">
<el-tab-pane :label="$t('modelObj.model_current_repo')" name="first"></el-tab-pane>
<el-tab-pane :label="$t('modelObj.model_my')" name="second"></el-tab-pane>
<el-tab-pane :label="$t('modelObj.model_public')" name="third"></el-tab-pane>
<el-tab-pane :label="$t('modelObj.model_collected')" name="fourth"></el-tab-pane>
</el-tabs>
<el-input size="small" class="search-inp" :placeholder="$t('modelObj.model_search_placeholder')"
v-model="dlgSearchValue" @keydown.enter.stop.native.prevent="inputSearch">
<div slot="suffix" class="search-inp-icon" @click="inputSearch">
<i class="el-icon-search"></i>
</div>
</el-input>
</div>
<el-tree :data="dlgModelTreeData" ref="dlgTreeRef" highlight-current show-checkbox node-key="id"
:default-expanded-keys="dlgInitTreeNode" :props="dlgTreeProps" :index="10" accordion :check-strictly="true"
@check="onTreeCheckChange">
<span slot-scope="{ node, data }" class="slot-wrap">
<span v-if="data.parent" class="custom-tree-node">
<el-tooltip v-if="data.description" placement="top-start">
<div slot="content" class="multiple-wrap"> {{ data.description }}</div>
<span class="model-title model-nowrap">
<div class="model_flex">
<span style="flex: inherit" class="model-nowrap">{{ node.label }}<span
v-if="(data.version != '0.0.1' || data.new != 1)" class="model-version">
{{ data.version }} </span>
</span>
<img v-if="data.recommend == 1" style="margin-left: 0.4rem" src="/img/jian.svg" />
</div>
</span>
</el-tooltip>
<span v-else class="model-title model-nowrap">
<div class="model_flex">
<span style="flex: inherit" class="model-nowrap">{{ node.label }}<span
v-if="(data.version != '0.0.1' || data.new != 1)" class="model-version">
{{ data.version }} </span>
</span>
<img v-if="data.recommend == 1" style="margin-left: 0.4rem" src="/img/jian.svg" />
</div>
</span>
<span class="model-repolink model-nowrap" @click.stop="return false;">
<a :href="'/' + data.repoOwnerName + '/' + data.repoName + '/modelmanage/model_readme_tmpl?name=' + data.name"
target="_blank">
{{ data.repoOwnerName }}/{{ data.repoDisplayName }}
</a>
</span>
</span>
<span v-else style="display: flex">
<span class="model-nowrap" :title="node.label">
{{ node.label }}
</span>
</span>
</span>
</el-tree>
<div class="pagination-c">
<el-pagination background @current-change="dlgPageChange" :current-page="dlgPage" :page-size="dlgPageSize"
layout="total, prev, pager, next" :total="dlgTotal">
</el-pagination>
</div>
</div>
<div class="dlg-right-area">
<div class="right-title"><span>{{ $t('modelObj.model_selected') }}</span>
</div>
<div class="right-selected-list">
<el-checkbox-group v-model="dlgSelectedModel">
<el-checkbox v-for="(item) in dlgSelectedModelList " :key="item.id" :label="item.id" :true-label="item.id"
:title="item.name" @change="(checked) => dlgChangeSelect(checked, item)">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</div>
<div class="right-btn-c">
<el-button type="default" size="small" @click="clearSelect">{{ $t('clear') }}</el-button>
<el-button type="primary" size="small" @click="confirm">{{ $t('modelObj.model_ok') }}</el-button>
</div>
</div>
</div>
</el-dialog>
</div>
</template>

<script>
import { getModelList } from '~/apis/modules/modelsquare';
import { getUrlSearchParams } from '~/utils';
export default {
name: "ModelSelect",
props: {
value: { type: Array, required: true },
repoOwnerName: { type: String, required: true },
repoName: { type: String, required: true },
required: { type: Boolean, default: false },
multiple: { type: Boolean, default: false },
title: { type: String, default: '' },
showTitle: { type: Boolean, default: true },
},
data() {
return {
type: '1',
maxCount: 1,

selectList: [],
dlgShow: false,
dlgLoading: false,
dlgActiveName: 'first',
dlgSearchValue: '',

dlgTreeProps: {
children: "children",
label: "name",
},
dlgModelTreeData: [],
dlgInitTreeNode: [],

dlgPage: 1,
dlgPageSize: 10,
dlgTotal: 0,

dlgSelectedModel: [],
dlgSelectedModelList: [],

errStatus: false,
};
},
watch: {
value: {
immediate: true,
deep: true,
handler(newVal) {
newVal = newVal === undefined ? [] : newVal;
this.selectList = [...newVal];
}
},
dlgSearchValue: {
handler(newVal) {
if (newVal === '') {
this.searchModelData();
}
}
}
},
methods: {
open() {
this.dlgTotal = 0;
this.dlgPage = 1;
this.dlgSelectedModel = [];
this.dlgSelectedModelList = [];
for (let i = 0, iLen = this.selectList.length; i < iLen; i++) {
const item = this.selectList[i];
this.dlgSelectedModel.push(item.id);
this.dlgSelectedModelList.push({
...item,
id: item.id,
name: item.name,
});
}
this.searchModelData();
},
beforeClose(done) {
done();
},
closed() { },
dlgTabClick(tab, event) {
this.dlgTotal = 0;
this.dlgPage = 1;
this.searchModelData();
},
transformTreeData(data) {
for (let i = 0, iLen = data.length; i < iLen; i++) {
const dataI = data[i];
const _children = dataI.modelFileList || [];
const children = [];
dataI.parent = true;
dataI.disabled = false;
dataI._modelID = dataI.id;
dataI._modelName = dataI.name;
dataI._modelVersion = dataI.version;
dataI._preTrainModelUrl = dataI.path;
_children.forEach(item => {
item.disabled = true;
item.ModTimeNum = new Date(item.ModTime).getTime();
})
_children.sort((a, b) => a.FileName.localeCompare(b.FileName));
_children.sort((a, b) => b.ModTimeNum - a.ModTimeNum);
for (let j = 0, jLen = _children.length; j < jLen; j++) {
const file = _children[j];
if (file.IsDir) continue;
file._modelID = dataI.id;
file._modelName = dataI.name;
file._modelVersion = dataI.version;
file._preTrainModelUrl = dataI.path;
file.name = file.FileName;
file.id = `${file._modelID}|${file._modelName}|${file._modelVersion}|${file._preTrainModelUrl}|${file.name}`;
children.push(file);
}
dataI.children = children;
}
return data;
},
inputSearch() {
this.dlgTotal = 0;
this.dlgPage = 1;
this.searchModelData();
},
searchModelData() {
const tabName = this.dlgActiveName;
const tabPrams = {
'first': 4,
'second': 2,
'third': 1,
'fourth': 3,
}
const params = {
type: this.type,
queryType: tabPrams[tabName],
repoOwnerName: this.repoOwnerName,
repoName: this.repoName,
q: this.dlgSearchValue.trim(),
page: this.dlgPage,
needModelFile: true,
notNeedEmpty: true,
};
if (params.queryType == 2 || params.queryType == 4) {
params.orderBy = 'created_unix';
}
this.dlgLoading = true;
getModelList(params).then(res => {
this.dlgLoading = false;
const data = res.data?.data || [];
this.dlgModelTreeData = this.transformTreeData(data);
this.dlgInitTreeNode = this.dlgModelTreeData[0]?.id
? [this.dlgModelTreeData[0].id]
: [];
this.dlgTotal = parseInt(res.data?.count || 0);
const setCheckedKeysList = this.dlgModelTreeData.reduce((pre, cur) => {
if (this.dlgSelectedModel.includes(cur.id)) {
pre.push(cur.id);
cur.children.forEach((item) => {
pre.push(item.id);
});
}
return pre;
}, []);
this.$refs.dlgTreeRef.setCheckedKeys(setCheckedKeysList);
}).catch(err => {
this.dlgLoading = false;
console.log(err);
});
},
dlgChangeSelect(checked, data) {
const index = this.dlgSelectedModelList.findIndex((item) => {
return item.id === data.id;
});
this.dlgSelectedModelList.splice(index, 1);
this.dlgSelectedModel = this.dlgSelectedModelList.map(item => item.id);
this.$refs.dlgTreeRef.setCheckedKeys([]);
},
dlgPageChange(page) {
this.dlgPage = page;
this.searchModelData();
},
onTreeCheckChange(data) {
const children = [];
if (data.parent) {
if (this.dlgSelectedModel.indexOf(data.id) >= 0) {
this.dlgSelectedModelList = [];
this.dlgSelectedModel = [];
} else {
this.dlgSelectedModelList = [data];
this.dlgSelectedModel = this.dlgSelectedModelList.map(item => item.id);
children.push(...data.children.map(item => item.id));
}
this.$refs.dlgTreeRef.setCheckedKeys(this.dlgSelectedModel.concat(children));
}
},
onTreeCheckChange1(data, nodeStatus) {
if (this.multiple) {
this.maxCount = 30;
}
let checkedNodes = (nodeStatus.checkedNodes || []).filter(item => !item.parent);
if (this.maxCount == 1 && checkedNodes.length) {
this.dlgSelectedModelList = data.parent ? checkedNodes.slice(0, 1) : [data];
} else {
let diffModel = false;
checkedNodes.map((item1) => {
this.dlgSelectedModelList.map(item2 => {
if (item1._modelID != item2._modelID) {
diffModel = true;
}
})
})
if (diffModel) {
this.$message.warning(this.$t('modelObj.model_should_same_model'));
checkedNodes.map(item => {
this.$refs.dlgTreeRef.setChecked(item.id, false, false);
});
this.dlgSelectedModelList.map(item => {
this.$refs.dlgTreeRef.setChecked(item.id, true, true);
});
return;
}
if (checkedNodes.length > this.maxCount) {
this.$message.warning(this.$t('modelObj.model_most', { msg: this.maxCount }));
const newCheckedNodes = checkedNodes.slice(0, this.maxCount);
const keys = this.dlgSelectedModelList.map(item => item.id);
for (let i = 0, iLen = newCheckedNodes.length; i < iLen; i++) {
if (keys.indexOf(newCheckedNodes[i].id) < 0 && this.dlgSelectedModelList.length < this.maxCount) {
this.dlgSelectedModelList.push(newCheckedNodes[i]);
}
}
} else {
const list = [];
const key = [];
for (let i = 0, iLen = this.dlgSelectedModelList.length; i < iLen; i++) {
const node1 = this.dlgSelectedModelList[i];
for (let j = 0, jLen = checkedNodes.length; j < jLen; j++) {
const node2 = checkedNodes[j];
if (node1.id === node2.id) {
list.push(node2);
key.push(node2.id);
}
}
}
for (let j = 0, jLen = checkedNodes.length; j < jLen; j++) {
const node2 = checkedNodes[j];
if (key.indexOf(node2.id) < 0) {
list.push(node2);
}
}
this.dlgSelectedModelList = list;
}
}
this.$refs.dlgTreeRef.setCheckedKeys(this.dlgSelectedModelList.map(item => item.id), true);
this.dlgSelectedModel = this.dlgSelectedModelList.map((item) => {
return item.id;
});
},
clearSelect() {
this.dlgSelectedModelList = [];
this.dlgSelectedModel = this.dlgSelectedModelList.map((item) => {
return item.id;
});
this.$refs.dlgTreeRef.setCheckedKeys([], true);
},
confirm() {
const len = this.dlgSelectedModelList.length;
this.selectList.splice(0, Infinity);
if (len) {
for (let i = 0; i < len; i++) {
const item = this.dlgSelectedModelList[i];
this.selectList.push({ ...item });
}
}
if (this.selectList.length) {
this.errStatus = false;
}
this.dlgShow = false;
this.$emit('input', this.selectList);
this.$emit('change', this.selectList);
},
check() {
if (this.required && !this.selectList.length) {
this.errStatus = true;
return false;
}
this.errStatus = false;
return true;
},
},
beforeMount() { },
mounted() {
const urlParams = getUrlSearchParams();
if (urlParams.model_name) {
this.dlgActiveName = 'third'
this.dlgSearchValue = urlParams.model_name
}
},
};
</script>

<style scoped lang="less">
@import 'cloudbrain.less';

.form-row {
.content {
flex: inherit;
width: 50%;
margin-right: 5px;

.model-list-c {
min-height: 37.6px;
border-radius: 4px;
border: 1px solid #DCDFE6;
box-sizing: border-box;
color: #606266;
padding: 4px 15px;

.model-item {
line-height: 26px;
font-size: 14px;
color: rgba(0, 0, 0, 0.87);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.model-item-model {
color: rgba(136, 136, 136, 1);
}

.model-item-file {
padding-left: 5px;
}

.model-item-placeholder {
height: 27px;
line-height: 27px;
color: rgba(0, 0, 0, 0.4);
opacity: 0.45 !important;
font-size: 14px;
}

&.error {
color: #9f3a38;
background: #fff6f6;
border-color: #e0b4b4;
}
}

.model-item-placeholder {
line-height: 32px;
color: rgba(0, 0, 0, 0.6);
opacity: 0.45 !important;
font-size: 13px;

}
}

.right-area {
// width: 250px;
display: flex;
align-items: center;

.btn-select {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
color: rgb(3, 102, 214);

i {
margin-right: 4px;
}
}
}
}

.model-dlg {
/deep/.el-dialog__body {
padding-top: 0;
}

.dlg-content {
display: flex;
min-height: 300px;

.dlg-left-area {
flex: 1;
margin-right: 15px;
border-right: 1px solid rgb(245, 245, 246);
padding-right: 15px;
position: relative;
overflow: hidden;

.model-tabs-c {
display: flex;
align-items: center;

.model-tabs {
flex: 1;
overflow: hidden;
margin-right: 5px;
}

.search-inp {
overflow: hidden;
width: 200px;
z-index: 5;
position: relative;
margin-top: -10px;

.search-inp-icon {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
width: 22px;
cursor: pointer;
}
}
}

.pagination-c {
margin-top: 25px;
text-align: center;
}
}

.dlg-right-area {
width: 300px;
padding-right: 30px;
position: relative;
display: flex;
flex-direction: column;


.right-title {
font-size: 14px;
height: 40px;
text-align: left;
color: rgb(0, 102, 255);
line-height: 40px;
}

.right-selected-list {
margin: 14px 0;
overflow-y: auto;
flex: 1;
height: 0;
max-height: 390px;
}

.right-btn-c {
width: 100%;
display: flex;
justify-content: space-between;
height: 32px;
margin-bottom: 10px;

/deep/ .el-button--default {
color: #409EFF;
border: 1px solid #409EFF;
}

/deep/ .el-button--primary {
background: rgb(56, 158, 13);
color: rgb(255, 255, 255);
border: 1px solid rgb(56, 158, 13);
}
}
}
}
}

.el-tree {
max-height: 400px;
overflow-y: auto;
overflow-x: hidden;
position: relative;
cursor: default;
background: #fff;
color: #606266;
font-family: SourceHanSansSC-medium;
}

.custom-tree-node {
display: flex;
align-items: center;
justify-content: space-between;
}

.custom-tree-node .model-title {
font-size: 14px;
color: #101010;
font-weight: 600;
flex: 1;
}

.custom-tree-node .model-repolink {
flex: 1;
text-align: right;
font-size: 12px;
}

.el-tree /deep/ .el-tree-node__content {
height: 40px;
background-color: #f5f5f6;
}

.el-tree /deep/ .el-tree-node__children .el-tree-node__content {
height: 30px;
background-color: #fff;
line-height: 20px;
font-size: 12px;
}

/deep/ .el-checkbox-group .el-checkbox {
max-width: 100%;
min-width: 80%;
}

/deep/ .el-checkbox-group .el-checkbox .el-checkbox__label {
max-width: 100%;
overflow: hidden;
vertical-align: middle;
text-overflow: ellipsis;
}

.model-nowrap {
overflow: hidden;
text-overflow: ellipsis;
}

.slot-wrap {
flex: 1;
padding-right: 2rem;
max-width: 93%;
}

.multiple-wrap {
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
display: -webkit-box;
max-width: 400px;
overflow: hidden;
}

.unzip-failed {
margin-left: 1rem;
color: red;
}

.zip-loading {
margin-left: 1rem;
color: #fcca00;
}

.model-search-vue {
z-index: 9999;
position: absolute;
right: 31%;
height: 30px;
top: 6px;
}

.select-model-label {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 1rem;
white-space: nowrap;
}

.model_flex {
display: flex;
align-items: center;
}

.model-version {
margin-left: 4px;
border-radius: 4px;
color: rgba(16, 16, 16, 0.8);
border-radius: 4px;
font-size: 12px;
background: rgba(220, 220, 220, 0.8);
padding: 1px 3px;
}
</style>

+ 71
- 0
web_src/vuepages/components/cloudbrain/NetworkType.vue View File

@@ -0,0 +1,71 @@
<template>
<div class="form-row">
<div class="title">
<span :class="required ? 'required' : ''">{{ $t('cloudbrainObj.networkType') }}</span>
</div>
<div class="content" :class="errStatus ? 'error' : ''">
<el-radio-group v-model="currentValue" @input="handleInput">
<!-- <el-radio label="all">{{ $t('cloudbrainObj.allNetworkType') }}</el-radio> -->
<el-radio label="no_internet">{{ $t('cloudbrainObj.noInternet') }}</el-radio>
<el-radio label="has_internet">{{ $t('cloudbrainObj.hasInternet') }}</el-radio>
</el-radio-group>
<el-tooltip class="tooltip" placement="top" effect="light">
<i class="question circle icon link" style="margin-top:-7px"></i>
<div slot="content">
<div style="width:200px;text-align:center;" v-html="$t('cloudbrainObj.networkTypeDesc')"></div>
</div>
</el-tooltip>
</div>
</div>
</template>

<script>

export default {
name: 'NetworkType',
props: {
value: { type: String, required: true },
required: { type: Boolean, default: true },
},
data() {
return {
currentValue: '',
errStatus: false,
};
},
watch: {
value: {
immediate: true,
handler(newVal) {
newVal = newVal === undefined ? '' : newVal;
this.currentValue = newVal.toString();
}
}
},
methods: {
check() {
return !this.errStatus;
},
handleInput(value) {
this.currentValue = value;
this.$emit('input', value);
this.check();
},
},
beforeMount() { }
};
</script>

<style scoped lang="less">
@import 'cloudbrain.less';

.content {
display: flex;
align-items: center;
padding-top: 8px;
}

.tooltip {
margin-left: 25px;
}
</style>

+ 20
- 6
web_src/vuepages/components/cloudbrain/SpecSelect.vue View File

@@ -42,7 +42,8 @@
<div style="width:200px;text-align:center;">{{ $t('specObj.resSelectTips') }}</div>
</div>
</el-tooltip>
<a target="_blank" href="https://openi.pcl.ac.cn/docs/index.html#/quickstart/resources">{{ $t('cloudbrainObj.specDescr') }}</a>
<a target="_blank" href="https://openi.pcl.ac.cn/docs/index.html#/quickstart/resources">{{
$t('cloudbrainObj.specDescr') }}</a>
</div>
</div>
</div>
@@ -56,6 +57,7 @@ export default {
value: { type: String, required: true, },
configs: { type: Object, required: true, },
required: { type: Boolean, default: true },
networkType: { type: String, default: 'no_internet' },
workServerNum: { type: Number, default: 1 },
},
data() {
@@ -85,6 +87,13 @@ export default {
this.renderSpec();
}
},
networkType: {
immediate: true,
handler() {
const resetSpec = true;
this.renderSpec(resetSpec);
}
},
workServerNum: {
immediate: true,
handler() {
@@ -93,12 +102,15 @@ export default {
},
},
methods: {
renderSpec() {
renderSpec(resetSpec) {
const showPoint = this.configs.showPoint || false;
const specs = this.configs.specs || [];
const specs = this.configs.specs[this.networkType] || [];
this.list = specs.map((item) => {
return renderSpecObject(item, showPoint)
});
if (resetSpec) {
this.spec = specs.length ? specs[0].id.toString() : '';
}
this.changeSpec(this.spec);
},
changeSpec() {
@@ -146,8 +158,7 @@ export default {
return this.list.filter(item => item.id == this.spec)[0];
},
},
beforeMount() {
}
beforeMount() { }
};
</script>

@@ -159,13 +170,15 @@ export default {
flex: inherit;
width: 50%;
margin-right: 5px;
.spec-list-c{

.spec-list-c {
min-height: 37.6px;
border-radius: 4px;
border: 1px solid #DCDFE6;
box-sizing: border-box;
color: #606266;
padding: 4px 15px;

.spec-item-placeholder {
height: 27px;
line-height: 27px;
@@ -174,6 +187,7 @@ export default {
font-size: 14px;
}
}

.spec-info {
flex: 1;



+ 53
- 0
web_src/vuepages/components/cloudbrain/details/ExportModel.vue View File

@@ -66,6 +66,20 @@
</el-popover>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.license') }}</label></div>
<div class="r-content">
<el-select style="width:100%;" size="medium" v-model="state.license" class="license-sel"
:placeholder="$t('modelManage.selectLicense')">
<template slot="prefix">
<i v-if="state.license" class="el-select__caret el-input__icon el-icon-close"
@click.stop.prevent="handleClearLicenseClick"></i>
</template>
<el-option v-for="item in licenseList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.modelLabel') }}</label></div>
<div class="r-content">
@@ -106,6 +120,7 @@ import BaseDialog from '~/components/BaseDialog.vue';
import { MODEL_ENGINES } from '~/const';
import { getListValueWithKey } from '~/utils';
import { getAiTaskOutputResultAll, setAiTaskResultToModel } from '~/apis/modules/cloudbrain';
import { getModelLicenseList } from '~/apis/modules/modelmanage';

const MAX_LABEL_COUNT = 5;
const REPOISPRIVATE = window.REPO_IS_PRIVATE;
@@ -130,9 +145,11 @@ export default {
engine: '0',
filesStr: '',
label: '',
license: '',
description: '',
isPrivate: REPOISPRIVATE ? '1' : '0',
},
licenseList: [],
nameErr: false,
modelFileErr: false,
modelFilesData: [],
@@ -159,6 +176,9 @@ export default {
const list = this.state.label.trim().split(' ').filter(label => label != '');
this.state.label = list.slice(0, MAX_LABEL_COUNT).join(' ') + (hasEndSpace && list.length < MAX_LABEL_COUNT ? ' ' : '');
},
handleClearLicenseClick() {
this.state.license = '';
},
submit() {
this.state.name = this.state.name.trim();
if (!this.checkName()) {
@@ -186,6 +206,7 @@ export default {
engine_name: getListValueWithKey(MODEL_ENGINES, this.state.engine.toString()),
modelSelectedFile: this.state.filesStr,
label: this.state.label.split(/\s+/).join(' ').trim(),
license: this.state.license,
isPrivate: (this.repoIsPrivate || this.state.isPrivate == 1) ? true : false,
description: this.state.description,
}
@@ -293,6 +314,20 @@ export default {
console.log(err);
this.loading = false;
});
getModelLicenseList().then(res => {
res = res.data;
try {
const license = JSON.parse(res) || [];
this.licenseList = license;
if (license.length) {
this.state.license = license[0].id;
}
} catch (err) {
console.log(err);
}
}).catch(err => {
console.log(err);
});
},
closed() { },
},
@@ -391,6 +426,24 @@ export default {
}
}

.license-sel {
/deep/.el-input--prefix .el-input__inner {
padding-left: 15px;
}

/deep/.el-input__prefix {
position: absolute;
right: 0;

.el-icon-close {
position: absolute;
right: 24px;
color: rgba(0, 0, 0, .87);
font-weight: bold;
}
}
}

.input-disabled {
/deep/ .el-input__inner {
background-color: #f5f5f6 !important;


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

@@ -6,7 +6,7 @@ export const POINT_ACTIONS = [
{ k: 'CreatePublicRepo', v: i18n.t('createPublicProject') }, { k: 'CreateIssue', v: i18n.t('dailyPutforwardTasks') }, { k: 'CreatePullRequest', v: i18n.t('dailyPR') }, { k: 'CommentIssue', v: i18n.t('comment') }, { k: 'UploadAttachment', v: i18n.t('uploadDatasetFile') }, { k: 'CreateNewModelTask', v: i18n.t('importNewModel') }, { k: 'BindWechat', v: i18n.t('completeWechatCodeScanningVerification') },
{ k: 'CreateCloudbrainTask', v: i18n.t('dailyRunCloudbrainTasks') }, { k: 'DatasetRecommended', v: i18n.t('datasetRecommendedByThePlatform') }, { k: 'CreateImage', v: i18n.t('submitNewPublicImage') }, { k: 'ImageRecommend', v: i18n.t('imageRecommendedByThePlatform') }, { k: 'ChangeUserAvatar', v: i18n.t('firstChangeofAvatar') }, { k: 'PushCommits', v: i18n.t('dailyCommit') },
];
export const JOB_TYPE = [{ k: 'DEBUG', v: i18n.t('debugTask') }, { k: 'TRAIN', v: i18n.t('trainTask') }, { k: 'INFERENCE', v: i18n.t('inferenceTask') }, { k: 'BENCHMARK', v: i18n.t('benchmarkTask') },{ k: 'ONLINEINFERENCE', v: i18n.t('onlineinfer') }, { k: 'HPC', v: i18n.t('superComputeTask') }];
export const JOB_TYPE = [{ k: 'DEBUG', v: i18n.t('debugTask') }, { k: 'TRAIN', v: i18n.t('trainTask') }, { k: 'INFERENCE', v: i18n.t('inferenceTask') }, { k: 'BENCHMARK', v: i18n.t('benchmarkTask') }, { k: 'ONLINEINFERENCE', v: i18n.t('onlineinfer') }, { k: 'HPC', v: i18n.t('superComputeTask') }];

// 资源管理
export const CLUSTERS = [{ k: 'OpenI', v: i18n.t('resourcesManagement.OpenI') }, { k: 'C2Net', v: i18n.t('resourcesManagement.C2Net') }];
@@ -14,6 +14,8 @@ export const AI_CENTER = [{ k: 'OpenIOne', v: i18n.t('resourcesManagement.OpenIO
export const COMPUTER_RESOURCES = [{ k: 'CPU', v: 'CPU' }, { k: 'GPU', v: 'GPU' }, { k: 'NPU', v: 'NPU' }, { k: 'GCU', v: 'GCU' }, { k: 'MLU', v: 'MLU' }, { k: 'DCU', v: 'DCU' }];
export const ACC_CARD_TYPE = [{ k: 'T4', v: 'T4' }, { k: 'A100', v: 'A100' }, { k: 'V100', v: 'V100' }, { k: 'ASCEND910', v: 'Ascend 910' }, { k: 'MLU270', v: 'MLU270' }, { k: 'MLU290', v: 'MLU290' }, { k: 'RTX3080', v: 'RTX3080' }, { k: 'ENFLAME-T20', v: 'ENFLAME-T20' }, { k: 'DCU', v: 'DCU' }];
export const SPECIFICATION_STATUS = [{ k: '1', v: i18n.t('resourcesManagement.willOnShelf') }, { k: '2', v: i18n.t('resourcesManagement.onShelf') }, { k: '3', v: i18n.t('resourcesManagement.offShelf') }];
export const NETWORK_TYPE = [{ k: 1, v: `${i18n.t('cloudbrainObj.networkType')}(${i18n.t('cloudbrainObj.noInternet')})` }, { k: 2, v: `${i18n.t('cloudbrainObj.networkType')}(${i18n.t('cloudbrainObj.hasInternet')})` }];
export const NETWORK_TYPE_VALUE = [{ k: 1, v: i18n.t('cloudbrainObj.noInternet') }, { k: 2, v: i18n.t('cloudbrainObj.hasInternet') }];

// 模型
export const MODEL_ENGINES = [{ k: '0', v: 'PyTorch' }, { k: '1', v: 'TensorFlow' }, { k: '2', v: 'MindSpore' }, { k: '4', v: 'PaddlePaddle' }, { k: '5', v: 'OneFlow' }, { k: '6', v: 'MXNet' }, { k: '3', v: 'Other' }];

+ 8
- 0
web_src/vuepages/langs/config/en-US.js View File

@@ -138,6 +138,7 @@ const en = {
allComputeResource: 'All Compute Resources',
accCardType: 'Acc Card Type',
allAccCardType: 'All Acc Card Type',
allNetworkType: 'All Network Type',
cardsTotalNum: 'Cards Total Number',
accCardsNum: 'Acc Cards Number',
allCardsNum: 'All Cards Number',
@@ -257,6 +258,8 @@ const en = {
modelManage: 'Model management',
modelName: 'Model name',
useCluster: 'Available clusters',
license: 'License',
selectLicense: 'Select license',
local: 'Local',
online: 'Online',
createModel: 'Create Model',
@@ -544,6 +547,11 @@ const en = {
selectImage: 'Select Image',
selectImagePlaceholder: 'Select image or input image url',
dataset: 'Dataset',
networkType: 'Access Internet',
allNetworkType: 'All network',
noInternet: 'No',
hasInternet: 'Yes',
networkTypeDesc: '「Access Internet」Describe whether the computing resource center supports internet access',
resourceSpec: 'Specification',
specPlaceholder: 'Select resource specification',
specDescr: 'Resource Note',


+ 8
- 0
web_src/vuepages/langs/config/zh-CN.js View File

@@ -137,6 +137,7 @@ const zh = {
allComputeResource: "全部计算资源",
accCardType: "卡类型",
allAccCardType: "全部卡类型",
allNetworkType: "全部网络类型",
cardsTotalNum: "卡数",
accCardsNum: "卡数",
allCardsNum: "全部卡数",
@@ -273,6 +274,8 @@ const zh = {
modelManage: '模型管理',
modelName: '模型名称',
useCluster: '可用集群',
license: '许可证',
selectLicense: '选择许可证',
local: '本地',
online: '线上',
createModel: '创建模型',
@@ -560,6 +563,11 @@ const zh = {
selectImage: '选择镜像',
selectImagePlaceholder: '选择镜像或输入镜像地址',
dataset: '数据集',
networkType: '访问Internet',
allNetworkType: '全部网络',
noInternet: '否',
hasInternet: '是',
networkTypeDesc: '「访问Internet」是描述您的任务分配到的计算资源中心是否支持互联网访问情况',
resourceSpec: '资源规格',
specPlaceholder: '请选择资源规格',
specDescr: '资源说明',


+ 17
- 7
web_src/vuepages/pages/cloudbrain/configs.js View File

@@ -52,6 +52,7 @@ export const CreatePageConfigs = {
model: { required: false, multiple: true },
imagev1: { required: true },
dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
/* just test */
// imagev2: { required: true },
@@ -72,6 +73,7 @@ export const CreatePageConfigs = {
model: { required: false, multiple: true },
imagev2: { required: true },
dataset: { required: false, type: 1, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
},
}],
@@ -95,6 +97,7 @@ export const CreatePageConfigs = {
model: { required: false, multiple: true },
imagev1: { required: true, type: 2 },
dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true },
spec: {},
},
}],
@@ -109,6 +112,7 @@ export const CreatePageConfigs = {
model: { required: false, multiple: true },
imagev2: { required: true },
dataset: { required: false, type: 1, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
},
}],
@@ -127,6 +131,7 @@ export const CreatePageConfigs = {
model: { required: false, multiple: true },
imagev2: { required: true },
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
},
}],
@@ -145,6 +150,7 @@ export const CreatePageConfigs = {
model: { required: false, multiple: true },
imagev2: { required: true },
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
},
}],
@@ -163,6 +169,7 @@ export const CreatePageConfigs = {
model: { required: false, multiple: true },
imagev2: { required: true },
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
},
}],
@@ -194,6 +201,7 @@ export const CreatePageConfigs = {
imagev1: { required: true },
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/MNIST_PytorchExample_GPU' },
dataset: { required: true, type: 0 },
networkType: { required: true },
runParameters: { required: false },
spec: { required: true },
/* just test */
@@ -225,6 +233,7 @@ export const CreatePageConfigs = {
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/MINIST_Example' },
dataset: { required: true, type: 1 },
runParameters: { required: false },
networkType: { required: true },
spec: { required: true },
workServerNum: { required: true },
},
@@ -256,6 +265,7 @@ export const CreatePageConfigs = {
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/MNIST_PytorchExample_GPU/src/branch/master/train_for_c2net.py' },
dataset: { required: true, type: 0 },
runParameters: { required: false },
networkType: { required: true },
spec: {},
},
modify: { showIsContinue: false, },
@@ -277,6 +287,7 @@ export const CreatePageConfigs = {
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/MNIST_Example/src/branch/master/train_for_c2net.py' },
dataset: { required: true, type: 1 },
runParameters: { required: false },
networkType: { required: true },
spec: { required: true },
workServerNum: { required: true },
},
@@ -300,6 +311,7 @@ export const CreatePageConfigs = {
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/MNIST_PytorchExample_GCU/src/branch/master/train_for_c2net.py' },
dataset: { required: true },
runParameters: { required: false },
networkType: { required: true },
spec: { required: true },
workServerNum: { required: true },
},
@@ -322,6 +334,7 @@ export const CreatePageConfigs = {
bootFile: { required: true, sampleUrl: '' },
dataset: { required: true },
runParameters: { required: false },
networkType: { required: true },
spec: { required: true },
},
modify: { showIsContinue: false, },
@@ -372,6 +385,7 @@ export const CreatePageConfigs = {
model: { required: false, },
algBechmarkType: { required: true, },
imagev1: { required: true, },
networkType: { required: true },
spec: { required: true, },

},
@@ -441,6 +455,7 @@ export const CreatePageConfigs = {
model: { required: false, multiple: true },
imagev1: { required: true, type: 2 },
dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true },
spec: {},
},
}],
@@ -537,13 +552,7 @@ export const DetailPageConfigs = {
]
}, {
name: 'operationProfile'
}/*, {
name: 'logs'
}, {
name: 'resourceUseage'
}, {
name: 'resultDownload'
}*/],
}],
}],
'NPU': [{
detailUrl: 'modelarts/notebook/',
@@ -953,6 +962,7 @@ export const DetailPageConfigs = {
model: { required: false, },
algBechmarkType: { required: true, },
imagev1: { required: true, },
networkType: { required: true },
spec: { required: true, },

},


+ 63
- 19
web_src/vuepages/pages/cloudbrain/create/index.vue View File

@@ -60,8 +60,9 @@
<RunParameters ref="runParametersRef" v-if="formCfg.runParameters" v-model="state.runParameters"
:required="formCfg.runParameters.required">
</RunParameters>
<NetworkType ref="networkTypeRef" v-if="formCfg.networkType" v-model="state.networkType"></NetworkType>
<SpecSelect ref="specRef" v-if="formCfg.spec" v-model="state.spec" :required="formCfg.spec.required"
:configs="specConfigs" :workServerNum="state.workServerNum"></SpecSelect>
:configs="specConfigs" :workServerNum="state.workServerNum" :networkType="state.networkType"></SpecSelect>
<WorkServerNum ref="workServerNumRef" v-if="formCfg.workServerNum && workServerNumList.length > 1"
v-model="state.workServerNum" :required="formCfg.workServerNum.required" :data="workServerNumList">
</WorkServerNum>
@@ -82,8 +83,9 @@
<div class="form-row">
<div class="title"></div>
<div class="content">
<el-button type="primary" :disabled="maskLoading || alreadyMsgBoxShow || noSpecFlag" size="default"
class="submit-btn" @click="submit">{{ $t('cloudbrainObj.createTask') }}</el-button>
<el-button type="primary"
:disabled="maskLoading || alreadyMsgBoxShow || !specConfigs.specs[state.networkType].length"
size="default" class="submit-btn" @click="submit">{{ $t('cloudbrainObj.createTask') }}</el-button>
<el-button class="cancel-btn" size="default" @click="cancel">{{ $t('cancel') }}</el-button>
</div>
</div>
@@ -107,6 +109,7 @@ import ImageSelectV2 from '~/components/cloudbrain/ImageSelectV2.vue';
import BootFile from '~/components/cloudbrain/BootFile.vue';
import DatasetSelect from '~/components/cloudbrain/DatasetSelect.vue';
import RunParameters from '~/components/cloudbrain/RunParameters.vue';
import NetworkType from '~/components/cloudbrain/NetworkType.vue';
import SpecSelect from '~/components/cloudbrain/SpecSelect.vue';
import WorkServerNum from '~/components/cloudbrain/WorkServerNum.vue';
import AlgBechmarkType from '~/components/cloudbrain/AlgBechmarkType.vue';
@@ -136,6 +139,7 @@ export default {
bootFile: '',
dataset: [],
runParameters: [],
networkType: 'no_internet',
spec: '',
workServerNum: 1,
algBechmarkType: ['1', ''],
@@ -145,7 +149,11 @@ export default {
engineList: [],
imageList: [],
specConfigs: {
specs: [],
specs: {
'all': [],
'no_internet': [],
'has_internet': [],
},
blance: 0,
showPoint: false,
},
@@ -164,8 +172,8 @@ export default {
};
},
components: {
FormTop, TaskName, TaskDescr, BranchName, BootFile, AIEngineSelect, ImageSelectV1,
ImageSelectV2, ModelSelect, DatasetSelect, RunParameters, SpecSelect, WorkServerNum,
FormTop, TaskName, TaskDescr, BranchName, BootFile, AIEngineSelect, ImageSelectV1, ImageSelectV2,
ModelSelect, DatasetSelect, RunParameters, NetworkType, SpecSelect, WorkServerNum,
AlgBechmarkType,
LoadingMask
},
@@ -219,7 +227,6 @@ export default {
subObj['boot_file'] = this.state.bootFile;
break;
case 'dataset':
const datasets = this.state.dataset
subObj['dataset_uuid_str'] = this.state.dataset.map(item => item.id).join(';');
break;
case 'runParameters':
@@ -235,6 +242,15 @@ export default {
}
subObj['params'] = JSON.stringify(params);
break;
case 'networkType':
let networkType = 0; // all
if (this.state.networkType == 'no_internet') {
networkType = 1;
} else if (this.state.networkType == 'has_internet') {
networkType = 2;
}
subObj['has_internet'] = networkType;
break;
case 'spec':
subObj['spec_id'] = Number(this.state.spec);
break;
@@ -314,15 +330,38 @@ export default {
}
}
if (this.formCfg['spec'] && task.spec) {
this.state.spec = '';
let networkType = 'all';
if (task.spec.has_internet == 1) {
networkType = 'no_internet';
} else if (task.spec.has_internet == 2) {
networkType = 'has_internet'
}
this.state.networkType = networkType;
this.$nextTick(() => { this.state.spec = ''; });
const specs = this.specConfigs.specs[this.state.networkType] || [];
let spec = '';
if (task.spec.source_spec_id) {
const find = this.specConfigs.specs.filter(item => item.source_spec_id == task.spec.source_spec_id);
const find = specs.filter(item => item.source_spec_id == task.spec.source_spec_id);
if (find.length) {
this.state.spec = find[0].id.toString();
spec = find[0].id.toString();
this.$nextTick(() => { this.state.spec = find[0].id.toString(); });
}
} else if (task.spec.id) {
if (this.specConfigs.specs.filter(item => item.id == task.spec.id).length) {
this.state.spec = task.spec.id.toString();
if (specs.filter(item => item.id == task.spec.id).length) {
spec = task.spec.id.toString();
this.$nextTick(() => { this.state.spec = task.spec.id.toString(); });
}
}
if (this.formCfg['networkType'] && networkType == 'all') {
this.$nextTick(() => { this.state.spec = ''; });
this.state.networkType = 'no_internet';
if (spec && this.specConfigs.specs['no_internet'].filter(item => item.id == spec).length) {
this.state.networkType = 'no_internet';
this.$nextTick(() => { this.state.spec = spec; });
}
if (spec && this.specConfigs.specs['has_internet'].filter(item => item.id == spec).length) {
this.state.networkType = 'has_internet';
this.$nextTick(() => { this.state.spec = spec; });
}
}
}
@@ -462,14 +501,21 @@ export default {
this.alreadyMsgBoxShow = data.not_stop_task_count > 0;
this.specConfigs.showPoint = data.pay_switch;
this.specConfigs.blance = data.point_account ? data.point_account.balance : 0;
this.specConfigs.specs = data.specs || [];
if (!this.specConfigs.specs.length) {
this.noSpecFlag = true
this.specConfigs.specs = data.specs || {
'all': [],
'no_internet': [],
'has_internet': [],
};
this.state.networkType = this.formCfg.networkType ? 'no_internet' : 'all';
if (this.state.networkType == 'no_internet'
&& !this.specConfigs.specs['no_internet'].length
&& this.specConfigs.specs['has_internet'].length) {
this.state.networkType = 'has_internet';
}
this.state.spec = this.specConfigs.specs[this.state.networkType][0] ? this.specConfigs.specs[this.state.networkType][0].id.toString() : '';
this.queueNum = data.wait_count || 1;
this.state.branchName = data.default_branch;
this.state.taskName = data.display_job_name;
this.state.spec = this.specConfigs.specs.length ? this.specConfigs.specs[0].id.toString() : '';
this.state.image = this.imageList.length ? {
image_id: this.imageList[0].image_id,
image_name: this.imageList[0].image_name,
@@ -486,9 +532,7 @@ export default {
this.modeifyTaskId = taskId;
this.getTaskInfo(taskId);
}
} else {

}
} else { }
}).catch(err => {
console.log(err);
});


+ 78
- 47
web_src/vuepages/pages/modelmanage/intro/index.vue View File

@@ -4,8 +4,8 @@
<NotFound></NotFound>
</div>
<div v-else>
<ModelHeader :tab="'intro'" :repoOwnerName="repoOwnerName" :repoName="repoName" :modelName="modelName" v-if="JSON.stringify(modelData)!=='{}'"
:model="modelData">
<ModelHeader :tab="'intro'" :repoOwnerName="repoOwnerName" :repoName="repoName" :modelName="modelName"
v-if="JSON.stringify(modelData) !== '{}'" :model="modelData">
</ModelHeader>
<div class="ui container content">
<div class="content-l" v-loading="loading">
@@ -85,6 +85,13 @@
<span class="label add-btn" v-if="canEdit" @click="addLabel">{{ $t('modelManage.addLabels') }}</span>
</div>
<div class="summary">
<div class="row">
<div class="label">{{ $t('modelManage.license') }}:</div>
<div class="value">
<a v-if="licenseInfo.name" target="_blank" :href="licenseInfo.linkUrl">{{ licenseInfo.name }}</a>
<span v-else>--</span>
</div>
</div>
<div class="row">
<div class="label">{{ $t('modelManage.modelEngine') }}:</div>
<div class="value">{{ modelData.engineName }}</div>
@@ -116,34 +123,35 @@
<template v-if="onlineUrl">
<!-- <div class="divider-column-vertical"></div> -->
<div class="online-address">
<a :href="onlineUrl" target="_blank" style="width: 50%;background: rgb(255, 255, 255);">{{ $t('modelManage.onlineInference') }}</a>
<a :href="onlineUrl" target="_blank" style="width: 50%;background: rgb(255, 255, 255);">{{
$t('modelManage.onlineInference') }}</a>
</div>
</template>
<template v-if="onlineAddressList.length">
<!-- <div class="divider-column-vertical"></div> -->
<div class="summary" style="margin-left: 1rem;margin-top:1.5rem">
<div class="title">{{ $t('modelManage.otherOnline') }}:</div>
<div class="detail-address" v-for="item in onlineAddressList">
<div class="detail-address" v-for="(item, index) in onlineAddressList" :key="index">
<li class="nowrap">
<a :href="item.onlineInferUrl">{{item.ownerName}}/{{item.repoDisplayName}}</a>
<a :href="item.onlineInferUrl">{{ item.ownerName }}/{{ item.repoDisplayName }}</a>
</li>
</div>
</div>
</template>
</div>
<template v-if="trainUsedDataList.length">
<div class="divider-column-vertical"></div>
<div class="summary" style="margin-left: 1rem;">
<div class="title">{{ $t('modelManage.trainUsedDataList') }}:</div>
<div class="detail-address" v-for="item in trainUsedDataList">
<div class="detail-address" v-for="item in trainUsedDataList" :key="item.dataset_name">
<span style="display: flex;" v-if="!item.is_delete">
<i style="margin-right: 0.5rem;" class="ri-stack-line"></i>
<a class="nowrap" :href="item.repository_link" :title="item.dataset_name">{{item.dataset_name}}</a>
<a class="nowrap" :href="item.repository_link" :title="item.dataset_name">{{ item.dataset_name }}</a>
</span>
<span v-else style="display: flex;color:#888888" class="nowrap">
<span v-else style="display: flex;color:#888888" class="nowrap">
<i style="margin-right: 0.5rem;" class="ri-stack-line"></i>
<span style="width: 70%;" class="nowrap">{{item.dataset_name}}</span>
<span style="width: 70%;" class="nowrap">{{ item.dataset_name }}</span>
<span>{{ $t('modelManage.deleted') }}</span>
</span>
</div>
@@ -153,9 +161,9 @@
<div class="divider-column-vertical"></div>
<div class="summary" style="margin-left: 1rem;">
<div class="title">{{ $t('modelManage.modelUseTaskList') }}:</div>
<div class="detail-address" v-for="item in modelUseTaskList">
<div class="detail-address" v-for="item in modelUseTaskList" :key="item.jobName">
<li class="nowrap">
<a :href="item.url" :title="item.jobName">{{item.jobName}}</a>
<a :href="item.url" :title="item.jobName">{{ item.jobName }}</a>
</li>
</div>
</div>
@@ -170,7 +178,7 @@
import ModelHeader from '../components/ModelHeader.vue';
import NotFound from '~/components/NotFound.vue';
import { getUrlSearchParams, setWebpackPublicPath, getListValueWithKey, transFileSize } from '~/utils';
import { getModelInfoByName, getMarkdownPreview, getModelIntroduce, setModelIntroduce } from '~/apis/modules/modelmanage';
import { getModelInfoByName, getMarkdownPreview, getModelIntroduce, setModelIntroduce, getModelLicenseList } from '~/apis/modules/modelmanage';
import { MODEL_ENGINES } from '~/const';
import { formatDate } from 'element-ui/lib/utils/date-util';

@@ -184,6 +192,7 @@ export default {
repoName: location.pathname.split('/')[2],
loading: false,
modelData: {},
licenseInfo: {},
labels: [],
introFileName: '',

@@ -209,7 +218,7 @@ export default {
onlineAddressList: [],
trainUsedDataList: [],
modelUseTaskList: [],
onlineUrl:'',
onlineUrl: '',


};
@@ -217,7 +226,7 @@ export default {
components: { ModelHeader, NotFound },
methods: {
createIntro() {
this.content = this.emptyDefaultContent;
this.content = this.emptyDefaultContent;
this.toggleEdit(true);
},
changeEditTab(tab) {
@@ -309,7 +318,7 @@ export default {
this.content = data.content;
this.htmlContent = data.htmlcontent;
const match = data.content.match(/(#+)(.*)在线体验地址\s+(http[s]?:\/\/\S+)/g);
if(match){
if (match) {
this.onlineUrl = 'http' + match[0].split('http')[1]
}
}
@@ -360,52 +369,52 @@ export default {
this.submitLoading = false;
});
},
getFullUrlForType(data){
getFullUrlForType(data) {
const result = []
data.forEach(element => {
const detailObj = {jobName:element.displayJobName}
const detailObj = { jobName: element.displayJobName }
const prefixRepoLink = `${element.ownerName}/${element.repoName}`
if(element.type === 0){
if(element.jobType==='DEBUG'){
if (element.type === 0) {
if (element.jobType === 'DEBUG') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/${element.id}`
}else if(element.jobType==='TRAIN'){
} else if (element.jobType === 'TRAIN') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/train-job/${element.jobId}`
}else if(element.jobType==='INFERENCE'){
} else if (element.jobType === 'INFERENCE') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/inference-job/${element.jobId}`
}else if(element.jobType==='BENCHMARK' || element.jobType==='MODELSAFETY'){
} else if (element.jobType === 'BENCHMARK' || element.jobType === 'MODELSAFETY') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/benchmark/${element.id}`
}else{
} else {
}
}
if(element.type === 1){
if(element.jobType==='DEBUG'){
if (element.type === 1) {
if (element.jobType === 'DEBUG') {
detailObj.url = `/${prefixRepoLink}/modelarts/notebook/${element.id}`
}else if(element.jobType==='TRAIN'){
} else if (element.jobType === 'TRAIN') {
detailObj.url = `/${prefixRepoLink}/modelarts/train-job/${element.id}`
}else if(element.jobType==='INFERENCE'){
} else if (element.jobType === 'INFERENCE') {
detailObj.url = `/${prefixRepoLink}/modelarts/inference-job/${element.id}`
}else if(element.jobType==='BENCHMARK' || element.jobType==='MODELSAFETY'){
} else if (element.jobType === 'BENCHMARK' || element.jobType === 'MODELSAFETY') {
detailObj.url = `/${prefixRepoLink}/cloudbrain/benchmark/${element.id}`
}else{
} else {
}

}
if(element.type === 2){
if(element.jobType==='DEBUG'){
if (element.type === 2) {
if (element.jobType === 'DEBUG') {
detailObj.url = `/${prefixRepoLink}/grampus/notebook/${element.id}`
}else if(element.jobType==='TRAIN'){
} else if (element.jobType === 'TRAIN') {
detailObj.url = `/${prefixRepoLink}/grampus/train-job/${element.jobId}`
}else if(element.jobType==='INFERENCE'){
} else if (element.jobType === 'INFERENCE') {

}else if(element.jobType==='BENCHMARK'){
} else if (element.jobType === 'BENCHMARK') {

}else{
} else {
detailObj.url = `/${prefixRepoLink}/grampus/onlineinfer/${element.id}`
}
}
result.push(detailObj)
});
return result
}
},
@@ -447,9 +456,23 @@ export default {
this.canEdit = model.isCanOper;
this.onlineAddressList = model.onlineInfo
this.trainUsedDataList = model.datasetInfo === null ? [] : model.datasetInfo
const modelUseTaskList = model.usedCloudbrain.length > 5 ? model.usedCloudbrain.slice(0,5) : model.usedCloudbrain
const modelUseTaskList = model.usedCloudbrain.length > 5 ? model.usedCloudbrain.slice(0, 5) : model.usedCloudbrain
this.modelUseTaskList = this.getFullUrlForType(modelUseTaskList)
this.getIntroInfo();
getModelLicenseList().then(res => {
res = res.data;
try {
const license = JSON.parse(res) || [];
const matchLicense = license.filter(item => item.id == this.modelData.license);
if (matchLicense.length) {
this.licenseInfo = matchLicense[0];
}
} catch (err) {
console.log(err);
}
}).catch(err => {
console.log(err);
});
} else {
this.emptyPage = true;
}
@@ -638,18 +661,21 @@ export default {
margin-bottom: 8px;
line-height: 20px;
}
.online-container{
background: linear-gradient(180deg, rgba(203,212,251,0.3) 0%,rgba(255,255,255,0) 100%);

.online-container {
background: linear-gradient(180deg, rgba(203, 212, 251, 0.3) 0%, rgba(255, 255, 255, 0) 100%);
border-color: rgb(225, 227, 230);
border-width: 1px 0px 0px;
border-style: solid;
margin-top: 1.5rem;

.online-address {
width: 100%;
display: flex;
justify-content: center;
margin-top: 1.5rem;
a{

a {
width: 50%;
background: rgb(255, 255, 255);
color: rgb(22, 132, 252);
@@ -662,7 +688,7 @@ export default {
}
}
}
.labels {
margin-top: 12px;
margin-bottom: 12px;
@@ -711,24 +737,27 @@ export default {
border-radius: 2px;
font-size: 12px;
}
.divider-column-vertical {
background-color: rgb(243 244 246);
height: 1px;
margin-bottom: 1.25rem;
margin-top: 1.25rem;
}
.detail-address{

.detail-address {
margin: 0.5rem 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: rgba(5, 127, 255, 1);

>a {
color: rgba(5, 127, 255, 1);
}
}

.summary {
.row {
display: flex;
@@ -737,19 +766,21 @@ export default {
.label {
color: rgb(136, 136, 136)
}

.value {
color: rgb(16, 16, 16);
flex: 1;
}
}
.title{

.title {
height: 20px;
color: rgba(136, 136, 136, 1);
font-size: 14px;
margin-bottom: 1.2rem;
}
}
}
}
</style>

+ 55
- 5
web_src/vuepages/pages/modelmanage/local/index.vue View File

@@ -20,7 +20,7 @@
<div class="row" :class="nameErr ? 'error' : ''">
<div class="r-title"><label class="required">{{ $t('modelManage.modelName') }}</label></div>
<div class="r-content">
<el-input size="medium" :maxLength="25" v-model="state.name" @blur="checkName"
<el-input size="medium" style="width:100%;" :maxLength="64" v-model="state.name" @blur="checkName"
:placeholder="$t('modelManage.pleaseInputModelName')">
</el-input>
</div>
@@ -28,19 +28,33 @@
<div class="row" v-show="isShowVersion">
<div class="r-title"><label class="required">{{ $t('modelManage.version') }}</label></div>
<div class="r-content">
<el-input class="input-disabled" style="width:288px;" size="medium" v-model="state.version" readonly>
<el-input class="input-disabled" style="width:100%;" size="medium" v-model="state.version" readonly>
</el-input>
</div>
</div>
<div class="row">
<div class="r-title"><label class="required">{{ $t('modelManage.modelEngine') }}</label></div>
<div class="r-content">
<el-select style="width:288px;" size="medium" v-model="state.engine" placeholder="">
<el-select style="width:100%;" size="medium" v-model="state.engine" placeholder="">
<el-option v-for="item in engineList" :key="item.k" :label="item.v" :value="item.k">
</el-option>
</el-select>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.license') }}</label></div>
<div class="r-content">
<el-select style="width:100%;" size="medium" v-model="state.license" class="license-sel"
:placeholder="$t('modelManage.selectLicense')">
<template slot="prefix">
<i v-if="state.license" class="el-select__caret el-input__icon el-icon-close"
@click.stop.prevent="handleClearLicenseClick"></i>
</template>
<el-option v-for="item in licenseList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.modelLabel') }}</label></div>
<div class="r-content">
@@ -48,7 +62,6 @@
:placeholder="$t('modelManage.modelLabelInputTips')" @input="labelInput"></el-input>
</div>
</div>

<div class="row" v-if="repoIsPrivate == false">
<div class="r-title"><label>{{ $t('modelManage.modelAccess') }}</label></div>
<div class="field">
@@ -92,7 +105,7 @@

<script>

import { saveLocalModel, getModelInfoByName, modifyModel } from '~/apis/modules/modelmanage';
import { saveLocalModel, getModelInfoByName, modifyModel, getModelLicenseList } from '~/apis/modules/modelmanage';
import { getUrlSearchParams } from '~/utils';
import { MODEL_ENGINES } from '~/const'

@@ -109,10 +122,12 @@ export default {
name: REPO_NAME + '_model_' + Math.random().toString(36).substr(2, 4),
version: '0.0.1',
engine: '0',
license: '',
label: '',
description: '',
isPrivate: false,
},
licenseList: [],
nameErr: false,
isShowVersion: false,
engineList: MODEL_ENGINES,
@@ -130,6 +145,9 @@ export default {
const list = this.state.label.trim().split(' ').filter(label => label != '');
this.state.label = list.slice(0, MAX_LABEL_COUNT).join(' ') + (hasEndSpace && list.length < MAX_LABEL_COUNT ? ' ' : '');
},
handleClearLicenseClick() {
this.state.license = '';
},
submit() {
this.state.name = this.state.name.trim();
if (!this.checkName()) {
@@ -231,6 +249,20 @@ export default {
this.cancel();
});
}
getModelLicenseList().then(res => {
res = res.data;
try {
const license = JSON.parse(res) || [];
this.licenseList = license;
if (license.length) {
this.state.license = license[0].id;
}
} catch (err) {
console.log(err);
}
}).catch(err => {
console.log(err);
});
},
beforeDestroy() { },
};
@@ -393,6 +425,24 @@ export default {
}
}

.license-sel {
/deep/.el-input--prefix .el-input__inner {
padding-left: 15px;
}

/deep/.el-input__prefix {
position: absolute;
right: 0;

.el-icon-close {
position: absolute;
right: 24px;
color: rgba(0, 0, 0, .87);
font-weight: bold;
}
}
}

.input-disabled {
/deep/ .el-input__inner {
background-color: #f5f5f6 !important;


+ 54
- 5
web_src/vuepages/pages/modelmanage/settings/index.vue View File

@@ -4,8 +4,8 @@
<NotFound></NotFound>
</div>
<div v-else>
<ModelHeader :tab="'settings'" :repoOwnerName="repoOwnerName" :repoName="repoName" :modelName="modelName" v-if="JSON.stringify(modelData)!=='{}'"
:model="modelData">
<ModelHeader :tab="'settings'" :repoOwnerName="repoOwnerName" :repoName="repoName" :modelName="modelName"
v-if="JSON.stringify(modelData) !== '{}'" :model="modelData">
</ModelHeader>
<div class="ui container content-wrap">
<div class="header">
@@ -24,19 +24,33 @@
<div class="row" v-show="isShowVersion">
<div class="r-title"><label class="required">{{ $t('modelManage.version') }}</label></div>
<div class="r-content">
<el-input class="input-disabled" style="width:288px;" size="medium" v-model="state.version" readonly>
<el-input class="input-disabled" style="width:100%;" size="medium" v-model="state.version" readonly>
</el-input>
</div>
</div>
<div class="row">
<div class="r-title"><label class="required">{{ $t('modelManage.modelEngine') }}</label></div>
<div class="r-content">
<el-select style="width:288px;" size="medium" v-model="state.engine" placeholder="">
<el-select style="width:100%;" size="medium" v-model="state.engine" placeholder="">
<el-option v-for="item in engineList" :key="item.k" :label="item.v" :value="item.k">
</el-option>
</el-select>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.license') }}</label></div>
<div class="r-content">
<el-select style="width:100%;" size="medium" v-model="state.license" class="license-sel"
:placeholder="$t('modelManage.selectLicense')">
<template slot="prefix">
<i v-if="state.license" class="el-select__caret el-input__icon el-icon-close"
@click.stop.prevent="handleClearLicenseClick"></i>
</template>
<el-option v-for="item in licenseList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
</div>
<div class="row">
<div class="r-title"><label>{{ $t('modelManage.modelLabel') }}</label></div>
<div class="r-content">
@@ -77,7 +91,7 @@
import ModelHeader from '../components/ModelHeader.vue';
import NotFound from '~/components/NotFound.vue';
import { getUrlSearchParams } from '~/utils';
import { saveLocalModel, getModelInfoByName, modifyModel } from '~/apis/modules/modelmanage';
import { getModelInfoByName, modifyModel, getModelLicenseList } from '~/apis/modules/modelmanage';
import { MODEL_ENGINES } from '~/const';

const REPO_NAME = location.pathname.split('/')[2];
@@ -101,9 +115,11 @@ export default {
version: '0.0.1',
engine: '0',
label: '',
license: '',
description: '',
isPrivate: '1',
},
licenseList: [],
nameErr: false,
isShowVersion: false,
engineList: MODEL_ENGINES,
@@ -121,6 +137,9 @@ export default {
const list = this.state.label.trim().split(' ').filter(label => label != '');
this.state.label = list.slice(0, MAX_LABEL_COUNT).join(' ') + (hasEndSpace && list.length < MAX_LABEL_COUNT ? ' ' : '');
},
handleClearLicenseClick() {
this.state.license = '';
},
cancel() {
if (this.backUrl) {
window.location.href = this.backUrl;
@@ -193,8 +212,20 @@ export default {
this.state.version = data.version;
this.state.engine = data.engine.toString();
this.state.label = data.label;
this.state.license = data.license;
this.state.description = data.description;
this.state.isPrivate = data.isPrivate ? '1' : '0';
getModelLicenseList().then(res => {
res = res.data;
try {
const license = JSON.parse(res) || [];
this.licenseList = license;
} catch (err) {
console.log(err);
}
}).catch(err => {
console.log(err);
});
} else {
this.emptyPage = true;
}
@@ -330,6 +361,24 @@ export default {
}
}

.license-sel {
/deep/.el-input--prefix .el-input__inner {
padding-left: 15px;
}

/deep/.el-input__prefix {
position: absolute;
right: 0;

.el-icon-close {
position: absolute;
right: 24px;
color: rgba(0, 0, 0, .87);
font-weight: bold;
}
}
}

.input-disabled {
/deep/ .el-input__inner {
background-color: #f5f5f6 !important;


+ 15
- 3
web_src/vuepages/pages/resources/components/QueueDialog.vue View File

@@ -62,6 +62,16 @@
<el-input v-model="dataInfo.CardsTotalNum" type="number" placeholder=""></el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('cloudbrainObj.networkType') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.HasInternet">
<el-option v-for="item in networkTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row" style="margin-top: 10px">
<div class="title"><span>{{ $t('resourcesManagement.remark') }}</span></div>
<div class="content" style="width: 400px">
@@ -85,7 +95,7 @@
<script>
import BaseDialog from '~/components/BaseDialog.vue';
import { addResQueue, updateResQueue } from '~/apis/modules/resources';
import { CLUSTERS, AI_CENTER, COMPUTER_RESOURCES, ACC_CARD_TYPE } from '~/const';
import { CLUSTERS, AI_CENTER, COMPUTER_RESOURCES, ACC_CARD_TYPE, NETWORK_TYPE_VALUE } from '~/const';

export default {
name: "QueueDialog",
@@ -105,7 +115,7 @@ export default {
computingCenterList: [AI_CENTER[0], AI_CENTER[1], AI_CENTER[2]],
computingTypeList: [...COMPUTER_RESOURCES],
cardTypeList: [...ACC_CARD_TYPE],
networkTypeList: [...NETWORK_TYPE_VALUE],
dataInfo: {},
};
},
@@ -124,6 +134,7 @@ export default {
ComputeResource: '',
AccCardType: '',
CardsTotalNum: '',
HasInternet: '',
Remark: '',
}
},
@@ -147,7 +158,8 @@ export default {
this.$emit("update:visible", false);
},
confirm() {
if (!this.dataInfo.QueueCode || !this.dataInfo.Cluster || !this.dataInfo.AiCenterCode || !this.dataInfo.ComputeResource || !this.dataInfo.AccCardType || !this.dataInfo.CardsTotalNum) {
if (!this.dataInfo.QueueCode || !this.dataInfo.Cluster || !this.dataInfo.AiCenterCode || !this.dataInfo.ComputeResource
|| !this.dataInfo.AccCardType || !this.dataInfo.CardsTotalNum || this.dataInfo.HasInternet === '') {
this.$message({
type: 'info',
message: this.$t('pleaseCompleteTheInformationFirst'),


+ 4
- 2
web_src/vuepages/pages/resources/components/SceneDialog.vue View File

@@ -79,8 +79,8 @@
<script>
import BaseDialog from '~/components/BaseDialog.vue';
import SpecSelect from './SpecSelect.vue';
import { getResQueueCode, getResSpecificationListAll, addResScene, updateResScene } from '~/apis/modules/resources';
import { JOB_TYPE, CLUSTERS, ACC_CARD_TYPE, SPECIFICATION_STATUS, COMPUTER_RESOURCES } from '~/const';
import { getResSpecificationListAll, addResScene, updateResScene } from '~/apis/modules/resources';
import { JOB_TYPE, CLUSTERS, ACC_CARD_TYPE, SPECIFICATION_STATUS, COMPUTER_RESOURCES, NETWORK_TYPE_VALUE } from '~/const';
import { getListValueWithKey } from '~/utils';

export default {
@@ -102,6 +102,7 @@ export default {
statusList: [...SPECIFICATION_STATUS],
isExclusiveList: [{ k: '2', v: this.$t('resourcesManagement.commonUse') }, { k: '1', v: this.$t('resourcesManagement.exclusive') }],
resourceList: [...COMPUTER_RESOURCES],
networkTypeList: [...NETWORK_TYPE_VALUE],
specsList: [],
};
},
@@ -145,6 +146,7 @@ export default {
QueueStr: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${item.AiCenterName})`,
SpecStr: `${NGPU}, CPU:${item.CpuCores}, ${this.$t('resourcesManagement.gpuMem')}:${item.GPUMemGiB}GB, ${this.$t('resourcesManagement.mem')}:${item.MemGiB}GB, ${this.$t('resourcesManagement.shareMem')}:${item.ShareMemGiB}GB`,
PriceStr: `, ${this.$t('resourcesManagement.unitPrice')}:${item.UnitPrice}${this.$t('resourcesManagement.point_hr')}`,
NetworkTypeStr: `, ${this.$t('cloudbrainObj.networkType')}:${getListValueWithKey(this.networkTypeList, item.HasInternet)}`,
}
});
this.specsList.splice(0, Infinity, ...data);


+ 7
- 3
web_src/vuepages/pages/resources/components/SpecSelect.vue View File

@@ -36,7 +36,8 @@
</div>
<div class="" v-for="_item in item.queues" :key="_item.ID">
<el-checkbox :value="_item.checked" @change="selectChange(_item.ID)">
<span v-html="_item.QueueStr + _item.PriceStr + _item.StatusStr"></span>
<span
v-html="_item.QueueStr + _item.PriceStr + _item.NetworkTypeStr + _item.StatusStr"></span>
</el-checkbox>
</div>
</div>
@@ -59,7 +60,7 @@
</div>
<div class="" v-for="_item in item.specs" :key="_item.ID">
<el-checkbox :value="_item.checked" @change="selectChange(_item.ID)">
<span v-html="_item.SpecStr + _item.PriceStr + _item.StatusStr"></span>
<span v-html="_item.SpecStr + _item.PriceStr + _item.NetworkTypeStr + _item.StatusStr"></span>
</el-checkbox>
</div>
</div>
@@ -87,7 +88,7 @@
<div class="row-l" style="width:400px;">{{ item.SpecStr }}</div>
<div class="row-r" style="flex:1">
<div class="" v-for="(_item, _index) in item.queues" :key="_index">
<span v-html="_item.QueueStr + _item.PriceStr + _item.StatusStr"></span>
<span v-html="_item.QueueStr + _item.PriceStr + _item.NetworkTypeStr + _item.StatusStr"></span>
</div>
</div>
</div>
@@ -145,6 +146,7 @@ export default {
AiCenterName: spec.AiCenterName,
QueueStr: spec.QueueStr,
PriceStr: spec.PriceStr,
NetworkTypeStr: spec.NetworkTypeStr,
checked: this.selectList.indexOf(spec.ID) > -1,
};
if (map[key]) {
@@ -175,6 +177,7 @@ export default {
AiCenterName: spec.AiCenterName,
QueueStr: spec.QueueStr,
PriceStr: spec.PriceStr,
NetworkTypeStr: spec.NetworkTypeStr,
checked: this.selectList.indexOf(spec.ID) > -1,
};
if (map[key]) {
@@ -206,6 +209,7 @@ export default {
AiCenterName: spec.AiCenterName,
QueueStr: spec.QueueStr,
PriceStr: spec.PriceStr,
NetworkTypeStr: spec.NetworkTypeStr,
};
if (map[key]) {
map[key].queues.push(queue);


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

@@ -15,6 +15,9 @@
<el-select class="select" size="medium" v-model="selCardType" @change="selectChange">
<el-option v-for="item in cardTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selNetworkType" @change="selectChange">
<el-option v-for="item in networkTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
<div>
<el-button size="medium" icon="el-icon-refresh" @click="syncComputerNetwork" v-loading="syncLoading">
@@ -46,6 +49,8 @@
header-align="center"></el-table-column>
<el-table-column prop="CardsTotalNum" :label="$t('resourcesManagement.cardsTotalNum')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="HasInternetStr" :label="$t('cloudbrainObj.networkType')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="UpdatedTimeStr" :label="$t('resourcesManagement.lastUpdateTime')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="Remark" :label="$t('resourcesManagement.remark')" align="left" header-align="center"
@@ -54,16 +59,16 @@
<el-table-column :label="$t('operation')" align="center" header-align="center" width="80">
<template slot-scope="scope">
<span v-if="scope.row.Cluster !== 'C2Net'" class="op-btn" @click="showDialog('edit', scope.row)">{{
$t('edit')
$t('edit')
}}</span>
<span v-else class="op-btn" style="color:rgb(187, 187, 187);cursor:not-allowed">{{
$t('edit')
$t('edit')
}}</span>
</template>
</el-table-column>
<template slot="empty">
<span style="font-size: 12px">{{
loading ? $t('loading') : $t('noData')
loading ? $t('loading') : $t('noData')
}}</span>
</template>
</el-table>
@@ -86,8 +91,8 @@

<script>
import QueueDialog from '../components/QueueDialog.vue';
import { getAiCenterList, getResQueueList, addResQueue, updateResQueue, syncResQueue } from '~/apis/modules/resources';
import { CLUSTERS, COMPUTER_RESOURCES, ACC_CARD_TYPE } from '~/const';
import { getAiCenterList, getResQueueList, syncResQueue } from '~/apis/modules/resources';
import { CLUSTERS, COMPUTER_RESOURCES, ACC_CARD_TYPE, NETWORK_TYPE, NETWORK_TYPE_VALUE } from '~/const';
import { getListValueWithKey } from '~/utils';
import { formatDate } from 'element-ui/lib/utils/date-util';

@@ -102,6 +107,8 @@ export default {
computingTypeList: [{ k: '', v: this.$t('resourcesManagement.allComputeResource') }, ...COMPUTER_RESOURCES],
selCardType: '',
cardTypeList: [{ k: '', v: this.$t('resourcesManagement.allAccCardType') }, ...ACC_CARD_TYPE],
selNetworkType: 0,
networkTypeList: [{ k: 0, v: this.$t('resourcesManagement.allNetworkType') }, ...NETWORK_TYPE],
syncLoading: false,
loading: false,
tableData: [],
@@ -141,6 +148,7 @@ export default {
center: this.selComputingCenter,
resource: this.selComputingType,
card: this.selCardType,
hasInternet: this.selNetworkType,
page: this.pageInfo.curpage,
pagesize: this.pageInfo.pageSize,
};
@@ -157,6 +165,7 @@ export default {
ClusterName: getListValueWithKey(this.clusterList, item.Cluster),
ComputeResourceName: getListValueWithKey(this.computingTypeList, item.ComputeResource),
AccCardTypeName: getListValueWithKey(this.cardTypeList, item.AccCardType),
HasInternetStr: getListValueWithKey(NETWORK_TYPE_VALUE, item.HasInternet),
UpdatedTimeStr: formatDate(new Date(item.UpdatedTime * 1000), 'yyyy-MM-dd HH:mm:ss'),
}
});


+ 12
- 4
web_src/vuepages/pages/resources/scene/index.vue View File

@@ -24,6 +24,9 @@
<el-select class="select" size="medium" v-model="selCardType" @change="selectChange">
<el-option v-for="item in cardTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selNetworkType" @change="selectChange">
<el-option v-for="item in networkTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
<div>
<el-button type="primary" icon="el-icon-plus" size="medium" @click="showDialog('add')">
@@ -71,7 +74,7 @@
<template slot-scope="scope">
<div v-if="!scope.row.queues.length">--</div>
<div v-for=" item in scope.row.queues " :key="item.key">
<span v-html="item.QueueStr + item.priceStr + item.statusStr"></span>
<span v-html="item.QueueStr + item.priceStr + item.networkTypeStr + item.statusStr"></span>
</div>
</template>
</el-table-column>
@@ -106,10 +109,9 @@

<script>
import SceneDialog from '../components/SceneDialog.vue';
import { getQueueList, getResQueueCode, getResSceneList, updateResScene, getAiCenterList } from '~/apis/modules/resources';
import { JOB_TYPE, CLUSTERS, ACC_CARD_TYPE, COMPUTER_RESOURCES, SPECIFICATION_STATUS } from '~/const';
import { getResQueueCode, getResSceneList, updateResScene, getAiCenterList } from '~/apis/modules/resources';
import { JOB_TYPE, CLUSTERS, ACC_CARD_TYPE, COMPUTER_RESOURCES, SPECIFICATION_STATUS, NETWORK_TYPE, NETWORK_TYPE_VALUE } from '~/const';
import { getListValueWithKey } from '~/utils';
import { formatDate } from 'element-ui/lib/utils/date-util';

export default {
data() {
@@ -130,6 +132,8 @@ export default {
resourceList: [{ k: '', v: this.$t('resourcesManagement.allComputeResource') }, ...COMPUTER_RESOURCES],
selCardType: '',
cardTypeList: [{ k: '', v: this.$t('resourcesManagement.allAccCardType') }, ...ACC_CARD_TYPE],
selNetworkType: 0,
networkTypeList: [{ k: 0, v: this.$t('resourcesManagement.allNetworkType') }, ...NETWORK_TYPE],
loading: false,
tableData: [],
pageInfo: {
@@ -205,6 +209,7 @@ export default {
center: this.selAiCenter,
resource: this.selResource,
cardType: this.selCardType,
hasInternet: this.selNetworkType,
page: this.pageInfo.curpage,
pagesize: this.pageInfo.pageSize,
};
@@ -228,6 +233,7 @@ export default {
spec.IsExclusiveStr = getListValueWithKey(this.isExclusiveList, item.IsExclusive ? '1' : '2');
spec.statusStr = statusStr;
spec.priceStr = `, ${this.$t('resourcesManagement.unitPrice')}:${spec.UnitPrice}${this.$t('resourcesManagement.point_hr')}`
spec.networkTypeStr = `, ${this.$t('cloudbrainObj.networkType')}:${getListValueWithKey(NETWORK_TYPE_VALUE, spec.HasInternet)}`
spec._id_ = item.ID;
const sourceSpecId = spec.SourceSpecId;
if (sourceSpecIdMap[sourceSpecId]) {
@@ -251,6 +257,7 @@ export default {
QueueStr: `${_spec.QueueCode}(${getListValueWithKey(this.clusterList, _spec.Cluster)} - ${_spec.AiCenterName})`,
statusStr: _spec.statusStr,
priceStr: _spec.priceStr,
networkTypeStr: _spec.networkTypeStr,
key: Math.random(),
});
}
@@ -268,6 +275,7 @@ export default {
QueueStr: `${_spec.QueueCode}(${getListValueWithKey(this.clusterList, _spec.Cluster)} - ${_spec.AiCenterName})`,
statusStr: _spec.statusStr,
priceStr: _spec.priceStr,
networkTypeStr: _spec.networkTypeStr,
key: Math.random(),
}]
})


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

@@ -21,6 +21,9 @@
<el-select class="select" size="medium" v-model="selCardsNum" @change="selectChange">
<el-option v-for="item in cardsNumList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selNetworkType" @change="selectChange">
<el-option v-for="item in networkTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
<div class="tools-bar-btn-c">
<el-button size="medium" icon="el-icon-refresh" @click="syncComputerNetwork" v-loading="syncLoading">
@@ -61,6 +64,8 @@
<span style="font-weight:600;font-size:14px;">{{ scope.row.UnitPrice }}</span>
</template>
</el-table-column>
<el-table-column prop="HasInternetStr" :label="$t('cloudbrainObj.networkType')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="IsAvailableStr" :label="$t('resourcesManagement.resourceSpecificationIsAvailable')"
align="center" header-align="center" width="100">
<template slot-scope="scope">
@@ -147,7 +152,7 @@
import SpecificationDialog from '../components/SpecificationDialog.vue';
import BaseDialog from '~/components/BaseDialog.vue';
import { getResQueueCode, getResSpecificationList, updateResSpecification, syncResSpecification, getResSpecificationScenes } from '~/apis/modules/resources';
import { SPECIFICATION_STATUS, CLUSTERS, ACC_CARD_TYPE, COMPUTER_RESOURCES } from '~/const';
import { SPECIFICATION_STATUS, CLUSTERS, ACC_CARD_TYPE, COMPUTER_RESOURCES, NETWORK_TYPE, NETWORK_TYPE_VALUE } from '~/const';
import { getListValueWithKey } from '~/utils';
import { formatDate } from 'element-ui/lib/utils/date-util';

@@ -168,6 +173,8 @@ export default {
cardTypeList: [{ k: '', v: this.$t('resourcesManagement.allAccCardType') }, ...ACC_CARD_TYPE],
selCardsNum: -1,
cardsNumList: [{ k: -1, v: this.$t('resourcesManagement.allCardsNum') }, ...[0, 1, 2, 4, 6, 8, 16, 32, 64].map(item => { return { k: item, v: `${this.$t('resourcesManagement.accCardsNum')}(${item})` } })],
selNetworkType: 0,
networkTypeList: [{ k: 0, v: this.$t('resourcesManagement.allNetworkType') }, ...NETWORK_TYPE],
syncLoading: false,
loading: false,
tableData: [],
@@ -216,6 +223,7 @@ export default {
resource: this.selResource,
cardType: this.selCardType,
cardsNum: this.selCardsNum,
hasInternet: this.selNetworkType,
page: this.pageInfo.curpage,
pagesize: this.pageInfo.pageSize,
};
@@ -239,6 +247,7 @@ export default {
UpdatedTimeStr: formatDate(new Date(Spec.UpdatedTime * 1000), 'yyyy-MM-dd HH:mm:ss'),
Status: Spec.Status.toString(),
StatusStr: getListValueWithKey(this.statusList, Spec.Status.toString()),
HasInternetStr: getListValueWithKey(NETWORK_TYPE_VALUE, Queue.HasInternet),
IsAvailable: Spec.IsAvailable,
IsAvailableStr: Spec.IsAvailable ? this.$t('resourcesManagement.available') : this.$t('resourcesManagement.notAvailable'),
}


+ 34
- 13
web_src/vuepages/pages/supercompute/create/index.vue View File

@@ -51,13 +51,15 @@
<DatasetSelect ref="datasetRef" v-if="formCfg.dataset" v-model="state.dataset"
:required="formCfg.dataset.required" :type="formCfg.dataset.type != undefined ? formCfg.dataset.type : -1"
:repoOwnerName="repoOwnerName" :repoName="repoName" :exceedSize="datasetSize"></DatasetSelect>
<NetworkType ref="networkTypeRef" v-if="formCfg.networkType" v-model="state.networkType"></NetworkType>
<SpecSelect ref="specRef" v-if="formCfg.spec" v-model="state.spec" :required="formCfg.spec.required"
:configs="specConfigs" :workServerNum="state.workServerNum"></SpecSelect>
:configs="specConfigs" :workServerNum="state.workServerNum" :networkType="state.networkType"></SpecSelect>
<div class="form-row">
<div class="title"></div>
<div class="content">
<el-button type="primary" :disabled="maskLoading || alreadyMsgBoxShow || noSpecFlag" size="default"
class="submit-btn" @click="submit">{{ $t('cloudbrainObj.createTask') }}</el-button>
<el-button type="primary"
:disabled="maskLoading || alreadyMsgBoxShow || !specConfigs.specs[state.networkType].length"
size="default" class="submit-btn" @click="submit">{{ $t('cloudbrainObj.createTask') }}</el-button>
<el-button class="cancel-btn" size="default" @click="cancel">{{ $t('cancel') }}</el-button>
</div>
</div>
@@ -77,6 +79,7 @@ import BranchName from '~/components/cloudbrain/BranchName.vue';
import ModelSelect from '~/components/cloudbrain/ModelSelect.vue';
import ImageSelectV2 from '~/components/cloudbrain/ImageSelectV2.vue';
import DatasetSelect from '~/components/cloudbrain/DatasetSelect.vue';
import NetworkType from '~/components/cloudbrain/NetworkType.vue';
import SpecSelect from '~/components/cloudbrain/SpecSelect.vue';
import LoadingMask from '~/components/cloudbrain/LoadingMask.vue';
import { getCreatePageConfigs } from '../configs';
@@ -100,12 +103,17 @@ export default {
image_url: '',
image: {},
dataset: [],
networkType: 'no_internet',
spec: '',
},
branchList: [],
imageList: [],
specConfigs: {
specs: [],
specs: {
'all': [],
'no_internet': [],
'has_internet': [],
},
blance: 0,
showPoint: false,
},
@@ -117,11 +125,10 @@ export default {
maskLoading: false,
maskLoadingContent: '',
datasetSize: 0,
noSpecFlag: false,
};
},
components: {
FormTop, TaskName, TaskDescr, BranchName, ImageSelectV2, ModelSelect, DatasetSelect, SpecSelect,
FormTop, TaskName, TaskDescr, BranchName, ImageSelectV2, ModelSelect, DatasetSelect, SpecSelect, NetworkType,
LoadingMask
},
methods: {
@@ -171,6 +178,15 @@ export default {
case 'dataset':
subObj['dataset_uuid_str'] = this.state.dataset.map(item => item.id).join(';');
break;
case 'networkType':
let networkType = 0; // all
if (this.state.networkType == 'no_internet') {
networkType = 1;
} else if (this.state.networkType == 'has_internet') {
networkType = 2;
}
subObj['has_internet'] = networkType;
break;
case 'spec':
subObj['spec_id'] = Number(this.state.spec);
break;
@@ -241,14 +257,21 @@ export default {
this.alreadyMsgBoxShow = data.not_stop_task_count > 0;
this.specConfigs.showPoint = data.pay_switch;
this.specConfigs.blance = data.point_account ? data.point_account.balance : 0;
this.specConfigs.specs = data.specs || [];
if (!this.specConfigs.specs.length) {
this.noSpecFlag = true
this.specConfigs.specs = data.specs || {
'all': [],
'no_internet': [],
'has_internet': [],
};
this.state.networkType = this.formCfg.networkType ? 'no_internet' : 'all';
if (this.state.networkType == 'no_internet'
&& !this.specConfigs.specs['no_internet'].length
&& this.specConfigs.specs['has_internet'].length) {
this.state.networkType = 'has_internet';
}
this.state.spec = this.specConfigs.specs[this.state.networkType][0] ? this.specConfigs.specs[this.state.networkType][0].id.toString() : '';
this.queueNum = data.wait_count || 1;
this.state.branchName = data.default_branch;
this.state.taskName = data.display_job_name;
this.state.spec = this.specConfigs.specs.length ? this.specConfigs.specs[0].id.toString() : '';
this.state.image = this.imageList.length ? {
image_id: this.imageList[0].image_id,
image_name: this.imageList[0].image_name,
@@ -256,9 +279,7 @@ export default {
if (data.config && data.config.dataset_max_size) {
this.datasetSize = data.config.dataset_max_size;
}
} else {

}
} else { }
}).catch(err => {
console.log(err);
});


Loading…
Cancel
Save