| @@ -0,0 +1,49 @@ | |||
| package models | |||
| import ( | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/timeutil" | |||
| "encoding/json" | |||
| ) | |||
| type AdminOperateLog struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| BizType string | |||
| OperateType string | |||
| OldValue string `xorm:"TEXT"` | |||
| NewValue string `xorm:"TEXT"` | |||
| RelatedId string `xorm:"INDEX"` | |||
| Comment string | |||
| CreatedTime timeutil.TimeStamp `xorm:"created"` | |||
| CreatedBy int64 | |||
| } | |||
| type LogValues struct { | |||
| Params []LogValue | |||
| } | |||
| type LogValue struct { | |||
| Key string | |||
| Val interface{} | |||
| } | |||
| func (l *LogValues) Add(key string, val interface{}) *LogValues { | |||
| l.Params = append(l.Params, LogValue{Key: key, Val: val}) | |||
| return l | |||
| } | |||
| func (l *LogValues) JsonString() string { | |||
| if len(l.Params) == 0 { | |||
| return "" | |||
| } | |||
| b, err := json.Marshal(l) | |||
| if err != nil { | |||
| log.Error("LogValues JsonString error . %v", err) | |||
| return "" | |||
| } | |||
| return string(b) | |||
| } | |||
| func InsertAdminOperateLog(log AdminOperateLog) (int64, error) { | |||
| return x.Insert(&log) | |||
| } | |||
| @@ -71,6 +71,7 @@ const ( | |||
| ModelArtsStopping ModelArtsJobStatus = "STOPPING" //停止中 | |||
| ModelArtsStopped ModelArtsJobStatus = "STOPPED" //停止 | |||
| ModelArtsUnavailable ModelArtsJobStatus = "UNAVAILABLE" //故障 | |||
| ModelArtsDeleting ModelArtsJobStatus = "DELETING" //删除中 | |||
| ModelArtsDeleted ModelArtsJobStatus = "DELETED" //已删除 | |||
| ModelArtsResizing ModelArtsJobStatus = "RESIZING" //规格变更中 | |||
| ModelArtsResizFailed ModelArtsJobStatus = "RESIZE_FAILED" //规格变更失败 | |||
| @@ -111,6 +112,16 @@ const ( | |||
| GrampusStatusWaiting = "WAITING" | |||
| ) | |||
| const ( | |||
| //cluster | |||
| OpenICluster = "OpenI" | |||
| C2NetCluster = "C2Net" | |||
| //AI center | |||
| AICenterOfCloudBrainOne = "OpenIOne" | |||
| AICenterOfCloudBrainTwo = "OpenITwo" | |||
| ) | |||
| type Cloudbrain struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| JobID string `xorm:"INDEX NOT NULL"` | |||
| @@ -1082,6 +1093,7 @@ type DatasetDownload struct { | |||
| DatasetName string `json:"dataset_name"` | |||
| DatasetDownloadLink string `json:"dataset_download_link"` | |||
| RepositoryLink string `json:"repository_link"` | |||
| IsDelete bool `json:"is_delete"` | |||
| } | |||
| type DataSource struct { | |||
| @@ -1338,6 +1350,34 @@ type GrampusSpec struct { | |||
| Name string `json:"name"` | |||
| ProcessorType string `json:"processorType"` | |||
| Centers []Center `json:"centers"` | |||
| SpecInfo SpecInfo `json:"specInfo"` | |||
| } | |||
| type GrampusAiCenter struct { | |||
| AccDevices []GrampusAccDevice `json:"accDevices"` | |||
| Id string `json:"id"` | |||
| Name string `json:"name"` | |||
| Resource []GrampusCenterResource `json:"resource"` | |||
| } | |||
| type GrampusAccDevice struct { | |||
| Kind string `json:"kind"` //加速卡类别, npu.huawei.com/NPU,nvidia.com/gpu,cambricon.com/mlu | |||
| Model string `json:"model"` //加速卡型号 | |||
| } | |||
| type GrampusCenterResource struct { | |||
| Allocated string `json:"allocated"` | |||
| Capacity string `json:"capacity"` | |||
| Name string `json:"name"` | |||
| } | |||
| type SpecInfo struct { | |||
| AccDeviceKind string `json:"accDeviceKind"` | |||
| AccDeviceMemory string `json:"accDeviceMemory"` | |||
| AccDeviceModel string `json:"accDeviceModel"` | |||
| AccDeviceNum int `json:"accDeviceNum"` | |||
| CpuCoreNum int `json:"cpuCoreNum"` | |||
| MemorySize string `json:"memorySize"` | |||
| } | |||
| type GetGrampusResourceSpecsResult struct { | |||
| @@ -1345,6 +1385,12 @@ type GetGrampusResourceSpecsResult struct { | |||
| Infos []GrampusSpec `json:"resourceSpecs"` | |||
| } | |||
| type GetGrampusAiCentersResult struct { | |||
| GrampusResult | |||
| Infos []GrampusAiCenter `json:"aiCenterInfos"` | |||
| TotalSize int `json:"totalSize"` | |||
| } | |||
| type GrampusImage struct { | |||
| CreatedAt int64 `json:"createdAt"` | |||
| UpdatedAt int64 `json:"updatedAt"` | |||
| @@ -1889,9 +1935,9 @@ func GetCloudbrainCountByUserID(userID int64, jobType string) (int, error) { | |||
| func GetCloudbrainRunCountByRepoID(repoID int64) (int, error) { | |||
| count, err := x.In("status", JobWaiting, JobRunning, ModelArtsCreateQueue, ModelArtsCreating, ModelArtsStarting, | |||
| ModelArtsReadyToStart, ModelArtsResizing, ModelArtsStartQueuing, ModelArtsRunning, ModelArtsRestarting, ModelArtsTrainJobInit, | |||
| ModelArtsTrainJobImageCreating, ModelArtsTrainJobSubmitTrying, ModelArtsTrainJobWaiting, ModelArtsTrainJobRunning, | |||
| ModelArtsTrainJobScaling, ModelArtsTrainJobCheckInit, ModelArtsTrainJobCheckRunning, ModelArtsTrainJobCheckRunningCompleted).And("repo_id = ?", repoID).Count(new(Cloudbrain)) | |||
| ModelArtsReadyToStart, ModelArtsResizing, ModelArtsStartQueuing, ModelArtsRunning, ModelArtsDeleting, ModelArtsRestarting, ModelArtsTrainJobInit, | |||
| ModelArtsTrainJobImageCreating, ModelArtsTrainJobSubmitTrying, ModelArtsTrainJobWaiting, ModelArtsTrainJobRunning, ModelArtsStopping, ModelArtsResizing, | |||
| ModelArtsTrainJobScaling, ModelArtsTrainJobCheckInit, ModelArtsTrainJobCheckRunning, ModelArtsTrainJobKilling, ModelArtsTrainJobCheckRunningCompleted).And("repo_id = ?", repoID).Count(new(Cloudbrain)) | |||
| return int(count), err | |||
| } | |||
| @@ -121,22 +121,20 @@ func (datasets DatasetList) loadAttachmentAttributes(opts *SearchDatasetOptions) | |||
| for i := range datasets { | |||
| if attachment.DatasetID == datasets[i].ID { | |||
| if opts.StarByMe { | |||
| if !attachment.IsPrivate{ | |||
| datasets[i].Attachments = append(datasets[i].Attachments, attachment) | |||
| }else{ | |||
| permission, ok := permissionMap[datasets[i].ID] | |||
| if !ok { | |||
| permission = false | |||
| datasets[i].Repo.GetOwner() | |||
| if datasets[i].Repo.Owner.IsOrganization() { | |||
| if datasets[i].Repo.Owner.IsUserPartOfOrg(opts.User.ID) { | |||
| log.Info("user is member of org.") | |||
| permission = true | |||
| } | |||
| } | |||
| if !permission { | |||
| isCollaborator, _ := datasets[i].Repo.IsCollaborator(opts.User.ID) | |||
| if isCollaborator { | |||
| isInRepoTeam,_:=datasets[i].Repo.IsInRepoTeam(opts.User.ID) | |||
| if isCollaborator ||isInRepoTeam { | |||
| log.Info("Collaborator user may visit the attach.") | |||
| permission = true | |||
| } | |||
| @@ -147,11 +145,7 @@ func (datasets DatasetList) loadAttachmentAttributes(opts *SearchDatasetOptions) | |||
| if permission { | |||
| datasets[i].Attachments = append(datasets[i].Attachments, attachment) | |||
| } else if !attachment.IsPrivate { | |||
| datasets[i].Attachments = append(datasets[i].Attachments, attachment) | |||
| } | |||
| } else { | |||
| datasets[i].Attachments = append(datasets[i].Attachments, attachment) | |||
| } | |||
| } | |||
| @@ -171,16 +165,17 @@ func (datasets DatasetList) loadAttachmentAttributes(opts *SearchDatasetOptions) | |||
| } | |||
| type SearchDatasetOptions struct { | |||
| Keyword string | |||
| OwnerID int64 | |||
| User *User | |||
| RepoID int64 | |||
| IncludePublic bool | |||
| RecommendOnly bool | |||
| Category string | |||
| Task string | |||
| License string | |||
| DatasetIDs []int64 | |||
| Keyword string | |||
| OwnerID int64 | |||
| User *User | |||
| RepoID int64 | |||
| IncludePublic bool | |||
| RecommendOnly bool | |||
| Category string | |||
| Task string | |||
| License string | |||
| DatasetIDs []int64 | |||
| ExcludeDatasetId int64 | |||
| ListOptions | |||
| SearchOrderBy | |||
| IsOwner bool | |||
| @@ -240,6 +235,10 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond { | |||
| cond = cond.And(builder.Eq{"dataset.repo_id": opts.RepoID}) | |||
| } | |||
| if opts.ExcludeDatasetId > 0 { | |||
| cond = cond.And(builder.Neq{"dataset.id": opts.ExcludeDatasetId}) | |||
| } | |||
| if opts.PublicOnly { | |||
| cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | |||
| cond = cond.And(builder.Eq{"attachment.is_private": false}) | |||
| @@ -145,6 +145,11 @@ func init() { | |||
| new(OrgStatistic), | |||
| new(SearchRecord), | |||
| new(AiModelConvert), | |||
| new(ResourceQueue), | |||
| new(ResourceSpecification), | |||
| new(ResourceScene), | |||
| new(ResourceSceneSpec), | |||
| new(AdminOperateLog), | |||
| new(CloudbrainTemp), | |||
| new(DatasetReference), | |||
| ) | |||
| @@ -130,6 +130,20 @@ func (repo *Repository) IsCollaborator(userID int64) (bool, error) { | |||
| return repo.isCollaborator(x, userID) | |||
| } | |||
| func (repo *Repository) IsInRepoTeam(userID int64) (bool, error) { | |||
| teams,err:=repo.GetRepoTeams() | |||
| if err!=nil || len(teams)==0{ | |||
| return false,err | |||
| } | |||
| for _,team :=range teams{ | |||
| if team.IsMember(userID){ | |||
| return true,nil | |||
| } | |||
| } | |||
| return false,nil | |||
| } | |||
| func (repo *Repository) changeCollaborationAccessMode(e Engine, uid int64, mode AccessMode) error { | |||
| // Discard invalid input | |||
| if mode <= AccessModeNone || mode > AccessModeOwner { | |||
| @@ -0,0 +1,349 @@ | |||
| package models | |||
| import ( | |||
| "code.gitea.io/gitea/modules/timeutil" | |||
| "errors" | |||
| "strconv" | |||
| "strings" | |||
| "xorm.io/builder" | |||
| ) | |||
| type ResourceQueue struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| QueueCode string | |||
| Cluster string `xorm:"notnull"` | |||
| AiCenterCode string | |||
| AiCenterName string | |||
| ComputeResource string | |||
| AccCardType string | |||
| CardsTotalNum int | |||
| IsAutomaticSync bool | |||
| Remark string | |||
| DeletedTime timeutil.TimeStamp `xorm:"deleted"` | |||
| CreatedTime timeutil.TimeStamp `xorm:"created"` | |||
| CreatedBy int64 | |||
| UpdatedTime timeutil.TimeStamp `xorm:"updated"` | |||
| UpdatedBy int64 | |||
| } | |||
| func (r ResourceQueue) ConvertToRes() *ResourceQueueRes { | |||
| return &ResourceQueueRes{ | |||
| ID: r.ID, | |||
| QueueCode: r.QueueCode, | |||
| Cluster: r.Cluster, | |||
| AiCenterCode: r.AiCenterCode, | |||
| AiCenterName: r.AiCenterName, | |||
| ComputeResource: r.ComputeResource, | |||
| AccCardType: r.AccCardType, | |||
| CardsTotalNum: r.CardsTotalNum, | |||
| UpdatedTime: r.UpdatedTime, | |||
| Remark: r.Remark, | |||
| } | |||
| } | |||
| type ResourceQueueReq struct { | |||
| QueueCode string | |||
| Cluster string `binding:"Required"` | |||
| AiCenterCode string | |||
| ComputeResource string `binding:"Required"` | |||
| AccCardType string `binding:"Required"` | |||
| CardsTotalNum int | |||
| CreatorId int64 | |||
| IsAutomaticSync bool | |||
| Remark string | |||
| } | |||
| func (r ResourceQueueReq) ToDTO() ResourceQueue { | |||
| q := ResourceQueue{ | |||
| QueueCode: r.QueueCode, | |||
| Cluster: r.Cluster, | |||
| AiCenterCode: r.AiCenterCode, | |||
| ComputeResource: strings.ToUpper(r.ComputeResource), | |||
| AccCardType: strings.ToUpper(r.AccCardType), | |||
| CardsTotalNum: r.CardsTotalNum, | |||
| IsAutomaticSync: r.IsAutomaticSync, | |||
| Remark: r.Remark, | |||
| CreatedBy: r.CreatorId, | |||
| UpdatedBy: r.CreatorId, | |||
| } | |||
| if r.Cluster == OpenICluster { | |||
| if r.AiCenterCode == AICenterOfCloudBrainOne { | |||
| q.AiCenterName = "云脑一" | |||
| } else if r.AiCenterCode == AICenterOfCloudBrainTwo { | |||
| q.AiCenterName = "云脑二" | |||
| } | |||
| } | |||
| return q | |||
| } | |||
| type SearchResourceQueueOptions struct { | |||
| ListOptions | |||
| Cluster string | |||
| AiCenterCode string | |||
| ComputeResource string | |||
| AccCardType string | |||
| } | |||
| type ResourceQueueListRes struct { | |||
| TotalSize int64 | |||
| List []*ResourceQueueRes | |||
| } | |||
| type ResourceQueueCodesRes struct { | |||
| ID int64 | |||
| QueueCode string | |||
| Cluster string | |||
| AiCenterCode string | |||
| AiCenterName string | |||
| } | |||
| func (ResourceQueueCodesRes) TableName() string { | |||
| return "resource_queue" | |||
| } | |||
| type ResourceAiCenterRes struct { | |||
| AiCenterCode string | |||
| AiCenterName string | |||
| } | |||
| type GetQueueCodesOptions struct { | |||
| Cluster string | |||
| } | |||
| func NewResourceQueueListRes(totalSize int64, list []ResourceQueue) *ResourceQueueListRes { | |||
| resList := make([]*ResourceQueueRes, len(list)) | |||
| for i, v := range list { | |||
| resList[i] = v.ConvertToRes() | |||
| } | |||
| return &ResourceQueueListRes{ | |||
| TotalSize: totalSize, | |||
| List: resList, | |||
| } | |||
| } | |||
| type ResourceQueueRes struct { | |||
| ID int64 | |||
| QueueCode string | |||
| Cluster string | |||
| AiCenterCode string | |||
| AiCenterName string | |||
| ComputeResource string | |||
| AccCardType string | |||
| CardsTotalNum int | |||
| UpdatedTime timeutil.TimeStamp | |||
| Remark string | |||
| } | |||
| func InsertResourceQueue(queue ResourceQueue) (int64, error) { | |||
| return x.Insert(&queue) | |||
| } | |||
| func UpdateResourceQueueById(queueId int64, queue ResourceQueue) (int64, error) { | |||
| return x.ID(queueId).Update(&queue) | |||
| } | |||
| func SearchResourceQueue(opts SearchResourceQueueOptions) (int64, []ResourceQueue, error) { | |||
| var cond = builder.NewCond() | |||
| if opts.Page <= 0 { | |||
| opts.Page = 1 | |||
| } | |||
| if opts.Cluster != "" { | |||
| cond = cond.And(builder.Eq{"cluster": opts.Cluster}) | |||
| } | |||
| if opts.AiCenterCode != "" { | |||
| cond = cond.And(builder.Eq{"ai_center_code": opts.AiCenterCode}) | |||
| } | |||
| if opts.ComputeResource != "" { | |||
| cond = cond.And(builder.Eq{"compute_resource": opts.ComputeResource}) | |||
| } | |||
| if opts.AccCardType != "" { | |||
| cond = cond.And(builder.Eq{"acc_card_type": opts.AccCardType}) | |||
| } | |||
| n, err := x.Where(cond).Unscoped().Count(&ResourceQueue{}) | |||
| if err != nil { | |||
| return 0, nil, err | |||
| } | |||
| r := make([]ResourceQueue, 0) | |||
| err = x.Where(cond).Desc("id").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Unscoped().Find(&r) | |||
| if err != nil { | |||
| return 0, nil, err | |||
| } | |||
| return n, r, nil | |||
| } | |||
| func GetResourceQueueCodes(opts GetQueueCodesOptions) ([]*ResourceQueueCodesRes, error) { | |||
| cond := builder.NewCond() | |||
| if opts.Cluster != "" { | |||
| cond = cond.And(builder.Eq{"cluster": opts.Cluster}) | |||
| } | |||
| cond = cond.And(builder.Or(builder.IsNull{"deleted_time"}, builder.Eq{"deleted_time": 0})) | |||
| r := make([]*ResourceQueueCodesRes, 0) | |||
| err := x.Where(cond).OrderBy("cluster desc,ai_center_code asc").Find(&r) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return r, nil | |||
| } | |||
| func GetResourceQueue(r *ResourceQueue) (*ResourceQueue, error) { | |||
| has, err := x.Get(r) | |||
| if err != nil { | |||
| return nil, err | |||
| } else if !has { | |||
| return nil, nil | |||
| } | |||
| return r, nil | |||
| } | |||
| func ParseComputeResourceFormGrampus(grampusDeviceKind string) string { | |||
| t := strings.Split(grampusDeviceKind, "/") | |||
| if len(t) < 2 { | |||
| return "" | |||
| } | |||
| return strings.ToUpper(t[1]) | |||
| } | |||
| type MemSize struct { | |||
| Sizes []string | |||
| Hex int | |||
| } | |||
| var memSize = MemSize{Sizes: []string{"K", "M", "G", "T", "P", "E"}, Hex: 1000} | |||
| var iMemSize = MemSize{Sizes: []string{"Ki", "Mi", "Gi", "Ti", "Pi", "Ei"}, Hex: 1024} | |||
| func MatchMemSize(memSize MemSize, val string) (int, float32, error) { | |||
| for i, v := range memSize.Sizes { | |||
| if strings.HasSuffix(val, v) { | |||
| s := strings.TrimSuffix(val, v) | |||
| f, err := strconv.ParseFloat(s, 32) | |||
| if err != nil { | |||
| return 0, 0, err | |||
| } | |||
| return i, float32(f), nil | |||
| } | |||
| } | |||
| return -1, 0, nil | |||
| } | |||
| //TransferMemSize transfer oldValue format from old index to new index | |||
| //eg: memSize.Sizes = []string{"M", "G", "T", "P", "E"}, oldValue = 10 , oldIndex = 1 , newIndex = 0. it means transfer 10G to 10000M | |||
| //so it returns 10000 | |||
| func TransferMemSize(memSize MemSize, oldValue float32, oldIndex int, newIndex int) float32 { | |||
| diff := oldIndex - newIndex | |||
| r := oldValue | |||
| if diff > 0 { | |||
| r = oldValue * float32(diff) * float32(memSize.Hex) | |||
| } else if diff < 0 { | |||
| r = oldValue / float32(-1*diff) / float32(memSize.Hex) | |||
| } | |||
| return r | |||
| } | |||
| //ParseMemSize find the memSize which matches value's format,and parse the number from value | |||
| func ParseMemSize(value string, memSize MemSize, newIndex int) (bool, float32, error) { | |||
| index, r, err := MatchMemSize(memSize, value) | |||
| if err != nil { | |||
| return false, 0, err | |||
| } | |||
| if index < 0 { | |||
| return false, 0, nil | |||
| } | |||
| return true, TransferMemSize(memSize, r, index, newIndex), nil | |||
| } | |||
| func ParseMemSizeFromGrampus(grampusMemSize string) (float32, error) { | |||
| if grampusMemSize == "" { | |||
| return 0, nil | |||
| } | |||
| memflag, memResult, err := ParseMemSize(grampusMemSize, memSize, 2) | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| if memflag { | |||
| return memResult, nil | |||
| } | |||
| iMemFlag, imemResult, err := ParseMemSize(grampusMemSize, iMemSize, 2) | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| if iMemFlag { | |||
| return imemResult, nil | |||
| } | |||
| return 0, errors.New("grampus memSize format error") | |||
| } | |||
| func SyncGrampusQueues(updateList []ResourceQueue, insertList []ResourceQueue, existIds []int64) error { | |||
| sess := x.NewSession() | |||
| var err error | |||
| defer func() { | |||
| if err != nil { | |||
| sess.Rollback() | |||
| } | |||
| sess.Close() | |||
| }() | |||
| //delete queues that no longer exists | |||
| deleteQueueIds := make([]int64, 0) | |||
| queueCond := builder.NewCond() | |||
| queueCond = queueCond.And(builder.NotIn("resource_queue.id", existIds)).And(builder.Eq{"resource_queue.cluster": C2NetCluster}) | |||
| if err := sess.Cols("resource_queue.id").Table("resource_queue"). | |||
| Where(queueCond).Find(&deleteQueueIds); err != nil { | |||
| return err | |||
| } | |||
| if len(deleteQueueIds) > 0 { | |||
| if _, err = sess.In("id", deleteQueueIds).Update(&ResourceQueue{Remark: "自动同步时被下架"}); err != nil { | |||
| return err | |||
| } | |||
| if _, err = sess.In("id", deleteQueueIds).Delete(&ResourceQueue{}); err != nil { | |||
| return err | |||
| } | |||
| //delete specs and scene that no longer exists | |||
| deleteSpcIds := make([]int64, 0) | |||
| if err := sess.Cols("resource_specification.id").Table("resource_specification"). | |||
| In("queue_id", deleteQueueIds).Find(&deleteSpcIds); err != nil { | |||
| return err | |||
| } | |||
| if len(deleteSpcIds) > 0 { | |||
| if _, err = sess.In("id", deleteSpcIds).Update(&ResourceSpecification{Status: SpecOffShelf}); err != nil { | |||
| return err | |||
| } | |||
| if _, err = sess.In("spec_id", deleteSpcIds).Delete(&ResourceSceneSpec{}); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| } | |||
| //update exists specs | |||
| if len(updateList) > 0 { | |||
| for _, v := range updateList { | |||
| if _, err = sess.ID(v.ID).Update(&v); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| } | |||
| //insert new specs | |||
| if len(insertList) > 0 { | |||
| if _, err = sess.Insert(insertList); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| return sess.Commit() | |||
| } | |||
| func GetResourceAiCenters() ([]ResourceAiCenterRes, error) { | |||
| r := make([]ResourceAiCenterRes, 0) | |||
| err := x.SQL("SELECT t.ai_center_code, t.ai_center_name FROM (SELECT DISTINCT ai_center_code, ai_center_name,cluster FROM resource_queue WHERE (deleted_time IS NULL OR deleted_time=0)) t ORDER BY cluster desc,ai_center_code asc").Find(&r) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return r, nil | |||
| } | |||
| @@ -0,0 +1,329 @@ | |||
| package models | |||
| import ( | |||
| "code.gitea.io/gitea/modules/timeutil" | |||
| "errors" | |||
| "xorm.io/builder" | |||
| ) | |||
| const ( | |||
| Exclusive = iota + 1 | |||
| NotExclusive | |||
| ) | |||
| type ResourceScene struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| SceneName string | |||
| JobType string | |||
| IsExclusive bool | |||
| ExclusiveOrg string | |||
| CreatedTime timeutil.TimeStamp `xorm:"created"` | |||
| CreatedBy int64 | |||
| UpdatedTime timeutil.TimeStamp `xorm:"updated"` | |||
| UpdatedBy int64 | |||
| DeleteTime timeutil.TimeStamp `xorm:"deleted"` | |||
| DeletedBy int64 | |||
| } | |||
| type ResourceSceneSpec struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| SceneId int64 `xorm:"unique(idx_scene_spec)"` | |||
| SpecId int64 `xorm:"unique(idx_scene_spec)"` | |||
| CreatedTime timeutil.TimeStamp `xorm:"created"` | |||
| } | |||
| type ResourceSceneReq struct { | |||
| ID int64 | |||
| SceneName string | |||
| JobType string | |||
| IsExclusive bool | |||
| ExclusiveOrg string | |||
| CreatorId int64 | |||
| SpecIds []int64 | |||
| } | |||
| type SearchResourceSceneOptions struct { | |||
| ListOptions | |||
| JobType string | |||
| IsExclusive int | |||
| AiCenterCode string | |||
| QueueId int64 | |||
| } | |||
| type ResourceSceneListRes struct { | |||
| TotalSize int64 | |||
| List []ResourceSceneRes | |||
| } | |||
| func NewResourceSceneListRes(totalSize int64, list []ResourceSceneRes) *ResourceSceneListRes { | |||
| return &ResourceSceneListRes{ | |||
| TotalSize: totalSize, | |||
| List: list, | |||
| } | |||
| } | |||
| type ResourceSceneRes struct { | |||
| ID int64 | |||
| SceneName string | |||
| JobType JobType | |||
| IsExclusive bool | |||
| ExclusiveOrg string | |||
| Specs []ResourceSpecWithSceneId | |||
| } | |||
| func (ResourceSceneRes) TableName() string { | |||
| return "resource_scene" | |||
| } | |||
| type ResourceSceneBriefRes struct { | |||
| ID int64 | |||
| SceneName string | |||
| } | |||
| func (ResourceSceneBriefRes) TableName() string { | |||
| return "resource_scene" | |||
| } | |||
| type ResourceSpecWithSceneId struct { | |||
| ID int64 | |||
| SourceSpecId string | |||
| AccCardsNum int | |||
| CpuCores int | |||
| MemGiB float32 | |||
| GPUMemGiB float32 | |||
| ShareMemGiB float32 | |||
| UnitPrice int | |||
| Status int | |||
| UpdatedTime timeutil.TimeStamp | |||
| SceneId int64 | |||
| //queue | |||
| Cluster string | |||
| AiCenterCode string | |||
| AiCenterName string | |||
| QueueCode string | |||
| QueueId int64 | |||
| ComputeResource string | |||
| AccCardType string | |||
| } | |||
| func (ResourceSpecWithSceneId) TableName() string { | |||
| return "resource_specification" | |||
| } | |||
| func InsertResourceScene(r ResourceSceneReq) error { | |||
| sess := x.NewSession() | |||
| defer sess.Close() | |||
| //check | |||
| specs := make([]ResourceSpecification, 0) | |||
| cond := builder.In("id", r.SpecIds).And(builder.Eq{"status": SpecOnShelf}) | |||
| if err := sess.Where(cond).Find(&specs); err != nil { | |||
| return err | |||
| } | |||
| if len(specs) < len(r.SpecIds) { | |||
| return errors.New("specIds not correct") | |||
| } | |||
| rs := ResourceScene{ | |||
| SceneName: r.SceneName, | |||
| JobType: r.JobType, | |||
| IsExclusive: r.IsExclusive, | |||
| ExclusiveOrg: r.ExclusiveOrg, | |||
| CreatedBy: r.CreatorId, | |||
| UpdatedBy: r.CreatorId, | |||
| } | |||
| _, err := sess.InsertOne(&rs) | |||
| if err != nil { | |||
| sess.Rollback() | |||
| return err | |||
| } | |||
| if len(r.SpecIds) == 0 { | |||
| return sess.Commit() | |||
| } | |||
| rss := make([]ResourceSceneSpec, len(r.SpecIds)) | |||
| for i, v := range r.SpecIds { | |||
| rss[i] = ResourceSceneSpec{ | |||
| SceneId: rs.ID, | |||
| SpecId: v, | |||
| } | |||
| } | |||
| _, err = sess.Insert(&rss) | |||
| if err != nil { | |||
| sess.Rollback() | |||
| return err | |||
| } | |||
| return sess.Commit() | |||
| } | |||
| func UpdateResourceScene(r ResourceSceneReq) error { | |||
| sess := x.NewSession() | |||
| var err error | |||
| defer func() { | |||
| if err != nil { | |||
| sess.Rollback() | |||
| } | |||
| sess.Close() | |||
| }() | |||
| // find old scene | |||
| old := ResourceScene{} | |||
| if has, _ := sess.ID(r.ID).Get(&old); !has { | |||
| return errors.New("ResourceScene not exist") | |||
| } | |||
| //check specification | |||
| specs := make([]ResourceSpecification, 0) | |||
| cond := builder.In("id", r.SpecIds).And(builder.Eq{"status": SpecOnShelf}) | |||
| if err := sess.Where(cond).Find(&specs); err != nil { | |||
| return err | |||
| } | |||
| if len(specs) < len(r.SpecIds) { | |||
| return errors.New("specIds not correct") | |||
| } | |||
| //update scene | |||
| rs := ResourceScene{ | |||
| SceneName: r.SceneName, | |||
| IsExclusive: r.IsExclusive, | |||
| ExclusiveOrg: r.ExclusiveOrg, | |||
| } | |||
| if _, err = sess.ID(r.ID).UseBool("is_exclusive").Update(&rs); err != nil { | |||
| return err | |||
| } | |||
| //delete scene spec relation | |||
| if _, err = sess.Where("scene_id = ? ", r.ID).Delete(&ResourceSceneSpec{}); err != nil { | |||
| sess.Rollback() | |||
| return err | |||
| } | |||
| if len(r.SpecIds) == 0 { | |||
| return sess.Commit() | |||
| } | |||
| //build new scene spec relation | |||
| rss := make([]ResourceSceneSpec, len(r.SpecIds)) | |||
| for i, v := range r.SpecIds { | |||
| rss[i] = ResourceSceneSpec{ | |||
| SceneId: r.ID, | |||
| SpecId: v, | |||
| } | |||
| } | |||
| if _, err = sess.Insert(&rss); err != nil { | |||
| sess.Rollback() | |||
| return err | |||
| } | |||
| return sess.Commit() | |||
| } | |||
| func DeleteResourceScene(sceneId int64) error { | |||
| sess := x.NewSession() | |||
| var err error | |||
| defer func() { | |||
| if err != nil { | |||
| sess.Rollback() | |||
| } | |||
| sess.Close() | |||
| }() | |||
| if _, err = sess.ID(sceneId).Delete(&ResourceScene{}); err != nil { | |||
| return err | |||
| } | |||
| if _, err = sess.Where("scene_id = ? ", sceneId).Delete(&ResourceSceneSpec{}); err != nil { | |||
| return err | |||
| } | |||
| return sess.Commit() | |||
| } | |||
| func SearchResourceScene(opts SearchResourceSceneOptions) (int64, []ResourceSceneRes, error) { | |||
| var cond = builder.NewCond() | |||
| if opts.Page <= 0 { | |||
| opts.Page = 1 | |||
| } | |||
| if opts.JobType != "" { | |||
| cond = cond.And(builder.Eq{"resource_scene.job_type": opts.JobType}) | |||
| } | |||
| if opts.IsExclusive == Exclusive { | |||
| cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 1}) | |||
| } else if opts.IsExclusive == NotExclusive { | |||
| cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 0}) | |||
| } | |||
| if opts.AiCenterCode != "" { | |||
| cond = cond.And(builder.Eq{"resource_queue.ai_center_code": opts.AiCenterCode}) | |||
| } | |||
| if opts.QueueId > 0 { | |||
| cond = cond.And(builder.Eq{"resource_queue.id": opts.QueueId}) | |||
| } | |||
| 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"} | |||
| count, err := x.Where(cond). | |||
| Distinct("resource_scene.id"). | |||
| Join("INNER", "resource_scene_spec", "resource_scene_spec.scene_id = resource_scene.id"). | |||
| Join("INNER", "resource_specification", "resource_specification.id = resource_scene_spec.spec_id"). | |||
| Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id"). | |||
| Count(&ResourceSceneRes{}) | |||
| if err != nil { | |||
| return 0, nil, err | |||
| } | |||
| r := make([]ResourceSceneRes, 0) | |||
| if err = x.Where(cond).Distinct(cols...). | |||
| Join("INNER", "resource_scene_spec", "resource_scene_spec.scene_id = resource_scene.id"). | |||
| Join("INNER", "resource_specification", "resource_specification.id = resource_scene_spec.spec_id"). | |||
| Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id"). | |||
| Desc("resource_scene.id"). | |||
| Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). | |||
| Find(&r); err != nil { | |||
| return 0, nil, err | |||
| } | |||
| if len(r) == 0 { | |||
| return 0, r, err | |||
| } | |||
| //find related specs | |||
| sceneIds := make([]int64, 0, len(r)) | |||
| for _, v := range r { | |||
| sceneIds = append(sceneIds, v.ID) | |||
| } | |||
| specs := make([]ResourceSpecWithSceneId, 0) | |||
| if err := x.Cols("resource_specification.id", "resource_specification.source_spec_id", | |||
| "resource_specification.acc_cards_num", "resource_specification.cpu_cores", | |||
| "resource_specification.mem_gi_b", "resource_specification.gpu_mem_gi_b", | |||
| "resource_specification.share_mem_gi_b", "resource_specification.unit_price", | |||
| "resource_specification.status", "resource_specification.updated_time", | |||
| "resource_scene_spec.scene_id", "resource_queue.cluster", | |||
| "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", | |||
| ).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"). | |||
| OrderBy("resource_specification.acc_cards_num"). | |||
| Find(&specs); err != nil { | |||
| return 0, nil, err | |||
| } | |||
| specsMap := make(map[int64][]ResourceSpecWithSceneId, 0) | |||
| for _, v := range specs { | |||
| if _, ok := specsMap[v.SceneId]; !ok { | |||
| specsMap[v.SceneId] = []ResourceSpecWithSceneId{v} | |||
| } else { | |||
| specsMap[v.SceneId] = append(specsMap[v.SceneId], v) | |||
| } | |||
| } | |||
| for i, v := range r { | |||
| s := specsMap[v.ID] | |||
| if s == nil { | |||
| s = make([]ResourceSpecWithSceneId, 0) | |||
| } | |||
| r[i].Specs = s | |||
| } | |||
| return count, r, nil | |||
| } | |||
| @@ -0,0 +1,285 @@ | |||
| package models | |||
| import ( | |||
| "code.gitea.io/gitea/modules/timeutil" | |||
| "xorm.io/builder" | |||
| ) | |||
| const ( | |||
| SpecNotVerified int = iota + 1 | |||
| SpecOnShelf | |||
| SpecOffShelf | |||
| ) | |||
| type ResourceSpecification struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| QueueId int64 `xorm:"INDEX"` | |||
| SourceSpecId string `xorm:"INDEX"` | |||
| AccCardsNum int | |||
| CpuCores int | |||
| MemGiB float32 | |||
| GPUMemGiB float32 | |||
| ShareMemGiB float32 | |||
| UnitPrice int | |||
| Status int | |||
| IsAutomaticSync bool | |||
| CreatedTime timeutil.TimeStamp `xorm:"created"` | |||
| CreatedBy int64 | |||
| UpdatedTime timeutil.TimeStamp `xorm:"updated"` | |||
| UpdatedBy int64 | |||
| } | |||
| func (r ResourceSpecification) ConvertToRes() *ResourceSpecificationRes { | |||
| return &ResourceSpecificationRes{ | |||
| ID: r.ID, | |||
| SourceSpecId: r.SourceSpecId, | |||
| AccCardsNum: r.AccCardsNum, | |||
| CpuCores: r.CpuCores, | |||
| MemGiB: r.MemGiB, | |||
| ShareMemGiB: r.ShareMemGiB, | |||
| GPUMemGiB: r.GPUMemGiB, | |||
| UnitPrice: r.UnitPrice, | |||
| Status: r.Status, | |||
| UpdatedTime: r.UpdatedTime, | |||
| } | |||
| } | |||
| type ResourceSpecificationReq struct { | |||
| QueueId int64 `binding:"Required"` | |||
| SourceSpecId string | |||
| AccCardsNum int | |||
| CpuCores int | |||
| MemGiB float32 | |||
| GPUMemGiB float32 | |||
| ShareMemGiB float32 | |||
| UnitPrice int | |||
| Status int | |||
| IsAutomaticSync bool | |||
| CreatorId int64 | |||
| } | |||
| func (r ResourceSpecificationReq) ToDTO() ResourceSpecification { | |||
| return ResourceSpecification{ | |||
| QueueId: r.QueueId, | |||
| SourceSpecId: r.SourceSpecId, | |||
| AccCardsNum: r.AccCardsNum, | |||
| CpuCores: r.CpuCores, | |||
| MemGiB: r.MemGiB, | |||
| GPUMemGiB: r.GPUMemGiB, | |||
| ShareMemGiB: r.ShareMemGiB, | |||
| UnitPrice: r.UnitPrice, | |||
| Status: r.Status, | |||
| IsAutomaticSync: r.IsAutomaticSync, | |||
| CreatedBy: r.CreatorId, | |||
| UpdatedBy: r.CreatorId, | |||
| } | |||
| } | |||
| type SearchResourceSpecificationOptions struct { | |||
| ListOptions | |||
| QueueId int64 | |||
| Status int | |||
| Cluster string | |||
| } | |||
| type SearchResourceBriefSpecificationOptions struct { | |||
| QueueId int64 | |||
| Cluster string | |||
| } | |||
| type ResourceSpecAndQueueListRes struct { | |||
| TotalSize int64 | |||
| List []*ResourceSpecAndQueueRes | |||
| } | |||
| func NewResourceSpecAndQueueListRes(totalSize int64, list []ResourceSpecAndQueue) *ResourceSpecAndQueueListRes { | |||
| resList := make([]*ResourceSpecAndQueueRes, len(list)) | |||
| for i, v := range list { | |||
| resList[i] = v.ConvertToRes() | |||
| } | |||
| return &ResourceSpecAndQueueListRes{ | |||
| TotalSize: totalSize, | |||
| List: resList, | |||
| } | |||
| } | |||
| type ResourceSpecificationRes struct { | |||
| ID int64 | |||
| SourceSpecId string | |||
| AccCardsNum int | |||
| CpuCores int | |||
| MemGiB float32 | |||
| GPUMemGiB float32 | |||
| ShareMemGiB float32 | |||
| UnitPrice int | |||
| Status int | |||
| UpdatedTime timeutil.TimeStamp | |||
| } | |||
| func (ResourceSpecificationRes) TableName() string { | |||
| return "resource_specification" | |||
| } | |||
| type ResourceSpecAndQueueRes struct { | |||
| Spec *ResourceSpecificationRes | |||
| Queue *ResourceQueueRes | |||
| } | |||
| type ResourceSpecAndQueue struct { | |||
| ResourceSpecification `xorm:"extends"` | |||
| ResourceQueue `xorm:"extends"` | |||
| } | |||
| func (*ResourceSpecAndQueue) TableName() string { | |||
| return "resource_specification" | |||
| } | |||
| func (r ResourceSpecAndQueue) ConvertToRes() *ResourceSpecAndQueueRes { | |||
| return &ResourceSpecAndQueueRes{ | |||
| Spec: r.ResourceSpecification.ConvertToRes(), | |||
| Queue: r.ResourceQueue.ConvertToRes(), | |||
| } | |||
| } | |||
| func InsertResourceSpecification(r ResourceSpecification) (int64, error) { | |||
| return x.Insert(&r) | |||
| } | |||
| func UpdateResourceSpecificationById(queueId int64, spec ResourceSpecification) (int64, error) { | |||
| return x.ID(queueId).Update(&spec) | |||
| } | |||
| func UpdateSpecUnitPriceById(id int64, unitPrice int) error { | |||
| _, err := x.Exec("update resource_specification set unit_price = ? ,updated_time = ? where id = ?", unitPrice, timeutil.TimeStampNow(), id) | |||
| return err | |||
| } | |||
| func SearchResourceSpecification(opts SearchResourceSpecificationOptions) (int64, []ResourceSpecAndQueue, error) { | |||
| var cond = builder.NewCond() | |||
| if opts.Page <= 0 { | |||
| opts.Page = 1 | |||
| } | |||
| if opts.QueueId > 0 { | |||
| cond = cond.And(builder.Eq{"resource_specification.queue_id": opts.QueueId}) | |||
| } | |||
| if opts.Status > 0 { | |||
| cond = cond.And(builder.Eq{"resource_specification.status": opts.Status}) | |||
| } | |||
| if opts.Cluster != "" { | |||
| cond = cond.And(builder.Eq{"resource_queue.cluster": opts.Cluster}) | |||
| } | |||
| //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{}) | |||
| if err != nil { | |||
| return 0, nil, err | |||
| } | |||
| r := make([]ResourceSpecAndQueue, 0) | |||
| err = x.Where(cond). | |||
| Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id"). | |||
| Desc("resource_specification.id"). | |||
| Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). | |||
| Unscoped().Find(&r) | |||
| if err != nil { | |||
| return 0, nil, err | |||
| } | |||
| return n, r, nil | |||
| } | |||
| func GetSpecScenes(specId int64) ([]ResourceSceneBriefRes, error) { | |||
| r := make([]ResourceSceneBriefRes, 0) | |||
| err := x.Where("resource_scene_spec.spec_id = ?", specId). | |||
| Join("INNER", "resource_scene_spec", "resource_scene_spec.scene_id = resource_scene.id"). | |||
| Find(&r) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return r, nil | |||
| } | |||
| func ResourceSpecOnShelf(id int64, unitPrice int) error { | |||
| _, err := x.Exec("update resource_specification set unit_price = ?,updated_time = ?,status = ? where id = ?", unitPrice, timeutil.TimeStampNow(), SpecOnShelf, id) | |||
| return err | |||
| } | |||
| func ResourceSpecOffShelf(id int64) (int64, error) { | |||
| sess := x.NewSession() | |||
| var err error | |||
| defer func() { | |||
| if err != nil { | |||
| sess.Rollback() | |||
| } | |||
| sess.Close() | |||
| }() | |||
| //delete scene spec relation | |||
| if _, err = sess.Where("spec_id = ?", id).Delete(&ResourceSceneSpec{}); err != nil { | |||
| return 0, err | |||
| } | |||
| param := ResourceSpecification{ | |||
| Status: SpecOffShelf, | |||
| } | |||
| n, err := sess.Where("id = ? and status = ?", id, SpecOnShelf).Update(¶m) | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| sess.Commit() | |||
| return n, err | |||
| } | |||
| func GetResourceSpecification(r *ResourceSpecification) (*ResourceSpecification, error) { | |||
| has, err := x.Get(r) | |||
| if err != nil { | |||
| return nil, err | |||
| } else if !has { | |||
| return nil, nil | |||
| } | |||
| return r, nil | |||
| } | |||
| func SyncGrampusSpecs(updateList []ResourceSpecification, insertList []ResourceSpecification, existIds []int64) error { | |||
| sess := x.NewSession() | |||
| var err error | |||
| defer func() { | |||
| if err != nil { | |||
| sess.Rollback() | |||
| } | |||
| sess.Close() | |||
| }() | |||
| //delete specs and scene that no longer exists | |||
| deleteIds := make([]int64, 0) | |||
| cond := builder.NewCond() | |||
| cond = cond.And(builder.NotIn("resource_specification.id", existIds)).And(builder.Eq{"resource_queue.cluster": C2NetCluster}) | |||
| if err := sess.Cols("resource_specification.id").Table("resource_specification"). | |||
| Where(cond).Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id"). | |||
| Find(&deleteIds); err != nil { | |||
| return err | |||
| } | |||
| if len(deleteIds) > 0 { | |||
| if _, err = sess.In("id", deleteIds).Update(&ResourceSpecification{Status: SpecOffShelf}); err != nil { | |||
| return err | |||
| } | |||
| if _, err = sess.In("spec_id", deleteIds).Delete(&ResourceSceneSpec{}); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| //update exists specs | |||
| if len(updateList) > 0 { | |||
| for _, v := range updateList { | |||
| if _, err = sess.ID(v.ID).Update(&v); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| } | |||
| //insert new specs | |||
| if len(insertList) > 0 { | |||
| if _, err = sess.Insert(insertList); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| return sess.Commit() | |||
| } | |||
| @@ -20,7 +20,7 @@ import ( | |||
| const ( | |||
| //Command = `pip3 install jupyterlab==2.2.5 -i https://pypi.tuna.tsinghua.edu.cn/simple;service ssh stop;jupyter lab --no-browser --ip=0.0.0.0 --allow-root --notebook-dir="/code" --port=80 --LabApp.token="" --LabApp.allow_origin="self https://cloudbrain.pcl.ac.cn"` | |||
| //CommandBenchmark = `echo "start benchmark";python /code/test.py;echo "end benchmark"` | |||
| CommandBenchmark = `echo "start benchmark";cd /benchmark && bash run_bk.sh;echo "end benchmark"` | |||
| CommandBenchmark = `echo "start benchmark";cd /benchmark && bash run_bk.sh | tee /model/benchmark-log.txt;echo "end benchmark"` | |||
| CodeMountPath = "/code" | |||
| DataSetMountPath = "/dataset" | |||
| ModelMountPath = "/model" | |||
| @@ -30,8 +30,8 @@ const ( | |||
| Snn4imagenetMountPath = "/snn4imagenet" | |||
| BrainScoreMountPath = "/brainscore" | |||
| TaskInfoName = "/taskInfo" | |||
| Snn4imagenetCommand = `/opt/conda/bin/python /snn4imagenet/testSNN_script.py --modelname '%s' --modelpath '/dataset' --modeldescription '%s'` | |||
| BrainScoreCommand = `bash /brainscore/brainscore_test_par4shSrcipt.sh -b '%s' -n '%s' -p '/dataset' -d '%s'` | |||
| Snn4imagenetCommand = `/opt/conda/bin/python /snn4imagenet/testSNN_script.py --modelname '%s' --modelpath '/dataset' --modeldescription '%s' | tee /model/benchmark-log.txt` | |||
| BrainScoreCommand = `bash /brainscore/brainscore_test_par4shSrcipt.sh -b '%s' -n '%s' -p '/dataset' -d '%s' | tee /model/benchmark-log.txt` | |||
| SubTaskName = "task1" | |||
| @@ -93,7 +93,7 @@ sendjob: | |||
| return nil, fmt.Errorf("resty get queues detail failed: %s", err) | |||
| } | |||
| if jobResult.Code == errInvalidToken && retry < 1 { | |||
| if (res.StatusCode() == http.StatusUnauthorized || jobResult.Code == errInvalidToken) && retry < 1 { | |||
| retry++ | |||
| _ = loginCloudbrain() | |||
| goto sendjob | |||
| @@ -5,6 +5,7 @@ | |||
| package cron | |||
| import ( | |||
| "code.gitea.io/gitea/services/cloudbrain/resource" | |||
| "code.gitea.io/gitea/modules/modelarts" | |||
| "context" | |||
| "time" | |||
| @@ -208,6 +209,17 @@ func registerSyncCloudbrainStatus() { | |||
| }) | |||
| } | |||
| func registerSyncResourceSpecs() { | |||
| RegisterTaskFatal("sync_grampus_specs", &BaseConfig{ | |||
| Enabled: true, | |||
| RunAtStart: true, | |||
| Schedule: "0 0 1 * * ?", | |||
| }, func(ctx context.Context, _ *models.User, _ Config) error { | |||
| resource.SyncGrampusQueueAndSpecs() | |||
| return nil | |||
| }) | |||
| } | |||
| func registerSyncModelArtsTempJobs() { | |||
| RegisterTaskFatal("sync_model_arts_temp_jobs", &BaseConfig{ | |||
| Enabled: true, | |||
| @@ -239,5 +251,6 @@ func initBasicTasks() { | |||
| registerSyncCloudbrainStatus() | |||
| registerHandleOrgStatistic() | |||
| registerSyncResourceSpecs() | |||
| registerSyncModelArtsTempJobs() | |||
| } | |||
| @@ -24,6 +24,7 @@ const ( | |||
| urlGetToken = urlOpenApiV1 + "token" | |||
| urlTrainJob = urlOpenApiV1 + "trainjob" | |||
| urlGetResourceSpecs = urlOpenApiV1 + "resourcespec" | |||
| urlGetAiCenter = urlOpenApiV1 + "sharescreen/aicenter" | |||
| urlGetImages = urlOpenApiV1 + "image" | |||
| errorIllegalToken = 1005 | |||
| @@ -276,3 +277,35 @@ sendjob: | |||
| return &result, nil | |||
| } | |||
| func GetAiCenters(pageIndex, pageSize int) (*models.GetGrampusAiCentersResult, error) { | |||
| checkSetting() | |||
| client := getRestyClient() | |||
| var result models.GetGrampusAiCentersResult | |||
| retry := 0 | |||
| sendjob: | |||
| _, err := client.R(). | |||
| SetAuthToken(TOKEN). | |||
| SetResult(&result). | |||
| Get(HOST + urlGetAiCenter + "?pageIndex=" + fmt.Sprint(pageIndex) + "&pageSize=" + fmt.Sprint(pageSize)) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("resty GetAiCenters: %v", err) | |||
| } | |||
| if result.ErrorCode == errorIllegalToken && retry < 1 { | |||
| retry++ | |||
| log.Info("retry get token") | |||
| _ = getToken() | |||
| goto sendjob | |||
| } | |||
| if result.ErrorCode != 0 { | |||
| log.Error("GetAiCenters failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
| return &result, fmt.Errorf("GetAiCenters failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
| } | |||
| return &result, nil | |||
| } | |||
| @@ -71,7 +71,8 @@ var ( | |||
| FlavorInfos *models.FlavorInfos | |||
| ImageInfos *models.ImageInfosModelArts | |||
| TrainFlavorInfos *Flavor | |||
| SpecialPools *models.SpecialPools | |||
| SpecialPools *models.SpecialPools | |||
| MultiNodeConfig *MultiNodes | |||
| ) | |||
| type GenerateTrainJobReq struct { | |||
| @@ -166,6 +167,14 @@ type ResourcePool struct { | |||
| } `json:"resource_pool"` | |||
| } | |||
| type MultiNodes struct{ | |||
| Info []OrgMultiNode `json:"multinode"` | |||
| } | |||
| type OrgMultiNode struct{ | |||
| Org string `json:"org"` | |||
| Node []int `json:"node"` | |||
| } | |||
| // type Parameter struct { | |||
| // Label string `json:"label"` | |||
| // Value string `json:"value"` | |||
| @@ -773,6 +782,13 @@ func InitSpecialPool() { | |||
| } | |||
| } | |||
| func InitMultiNode(){ | |||
| if MultiNodeConfig ==nil && setting.ModelArtsMultiNode!=""{ | |||
| json.Unmarshal([]byte(setting.ModelArtsMultiNode), &MultiNodeConfig) | |||
| } | |||
| } | |||
| func HandleTrainJobInfo(task *models.Cloudbrain) error { | |||
| result, err := GetTrainJob(task.JobID, strconv.FormatInt(task.VersionID, 10)) | |||
| @@ -547,6 +547,7 @@ var ( | |||
| FlavorInfos string | |||
| TrainJobFLAVORINFOS string | |||
| ModelArtsSpecialPools string | |||
| ModelArtsMultiNode string | |||
| //grampus config | |||
| Grampus = struct { | |||
| @@ -1432,6 +1433,7 @@ func NewContext() { | |||
| FlavorInfos = sec.Key("FLAVOR_INFOS").MustString("") | |||
| TrainJobFLAVORINFOS = sec.Key("TrainJob_FLAVOR_INFOS").MustString("") | |||
| ModelArtsSpecialPools = sec.Key("SPECIAL_POOL").MustString("") | |||
| ModelArtsMultiNode=sec.Key("MULTI_NODE").MustString("") | |||
| sec = Cfg.Section("elk") | |||
| ElkUrl = sec.Key("ELKURL").MustString("") | |||
| @@ -924,7 +924,7 @@ dataset_name_tooltips = Please enter letters, numbers, _ and - up to 100 charact | |||
| dataset_no_create = No dataset has been created yet | |||
| dataset_explain = Dataset: CloudBrain I provides CPU/GPU resources, Cloudbrain II provides Ascend NPU resources, and the data set used for debugging also needs to be uploaded to the corresponding environment; | |||
| dataset_instructions_for_use = Instructions for use: You can refer to Openi AI Collaboration Platform | |||
| dataset_camp_course = Newcomer Training Camp Course; | |||
| dataset_camp_course = OpenI_Learning; | |||
| dataset_upload = Upload | |||
| dataset_upload_status= Upload Status | |||
| dataset_file_name = File Name | |||
| @@ -959,6 +959,7 @@ unfavorite=Unlike | |||
| favorite=Like | |||
| disassociate=Disassociate | |||
| benchmark_dataset_tip=Note: first use the dataset function to upload the model, and then select the model from the dataset list. | |||
| file_deleted=The file has been deleted | |||
| [repo] | |||
| owner = Owner | |||
| @@ -1140,7 +1141,7 @@ modelarts.train_job.compute_node=Compute Node | |||
| modelarts.create_model = Create Model | |||
| modelarts.model_label=Model Label | |||
| modelarts.infer_dataset = Inference Dataset | |||
| modelarts.train_job.label_place=Input labels, multiple labels are separated by spaces | |||
| modelarts.train_job.basic_info=Basic Info | |||
| modelarts.train_job.job_status=Job Status | |||
| @@ -1212,6 +1213,7 @@ modelarts.infer_job.select_model = Select Model | |||
| modelarts.infer_job.boot_file_helper=The startup file is the entry file for your program execution and must end in.py.Such as inference.py, main.py, example/inference.py, case/main.py. | |||
| modelarts.infer_job.tooltip = The model has been deleted and cannot be viewed. | |||
| modelarts.download_log=Download log file | |||
| modelarts.no_node_right = The value of 'Amount of Compute Node' is wrong, you have no right to use the current value of 'Amount of Compute Node'. | |||
| debug_task_not_created = Debug task has not been created | |||
| @@ -1221,7 +1223,10 @@ model_Evaluation_not_created = Model evaluation has not been created | |||
| repo_not_initialized = Code version: You have not initialized the code repository, please <a href="%s"> initialized </a> first ; | |||
| debug_task_running_limit =Running time: no more than 4 hours, it will automatically stop if it exceeds 4 hours; | |||
| dataset_desc = Dataset: Cloud Brain 1 provides CPU/GPU,Cloud Brain 2 provides Ascend NPU.And dataset also needs to be uploaded to the corresponding environment; | |||
| platform_instructions = Instructions for use: You can refer to the <a href="https://git.openi.org.cn/zeizei/OpenI_Learning">Xiaobai training camp </a> course of Openi AI collaboration platform. | |||
| platform_instructions = Instructions for use: You can refer to the <a href="https://git.openi.org.cn/zeizei/OpenI_Learning"> OpenI_Learning </a> course of Qizhi AI collaboration platform. | |||
| platform_instructions1 = Instructions for use: You can refer to the | |||
| platform_instructions2 = OpenI_Learning | |||
| platform_instructions3 = course of Openi AI collaboration platform. | |||
| model_not_exist = Model file: You do not have a model file yet, please generate and <a href="%s/modelmanage/show_model">export the model</a> through the <a href="%s/modelarts/train-job">training task</a> first ; | |||
| benchmark_leaderboards = Benchmark leaderboards | |||
| @@ -1244,11 +1249,11 @@ model.convert=Model Transformation | |||
| model.list=Model List | |||
| model.manage.create_new_convert_task=Create Model Transformation Task | |||
| model.manage.notcreatemodel=No model has been created. | |||
| model.manage.notcreatemodel=No model has been created | |||
| model.manage.init1=Code version: You have not initialized the code repository, please | |||
| model.manage.init2=initialized first ; | |||
| model.manage.createtrainjob_tip=Training task: you haven't created a training task, please create it first | |||
| model.manage.createtrainjob=Training task | |||
| model.manage.createtrainjob=Training task. | |||
| model.manage.delete=Delete Model | |||
| model.manage.delete_confirm=Are you sure to delete this model? Once this model is deleted, it cannot be restored. | |||
| model.manage.select.trainjob=Select train task | |||
| @@ -1260,9 +1265,9 @@ model.manage.modellabel=Model label | |||
| model.manage.modeldesc=Model description | |||
| model.manage.baseinfo=Base Information | |||
| modelconvert.notcreate=No model conversion task has been created. | |||
| modelconvert.importfirst1=Please import first | |||
| modelconvert.importfirst2=download model | |||
| modelconvert.importfirst3=, then converts it. | |||
| modelconvert.importfirst1=Please import the | |||
| modelconvert.importfirst2=model | |||
| modelconvert.importfirst3=first, then converts it. | |||
| modelconvert.download=Download | |||
| modelconvert.taskname=Task name | |||
| modelconvert.modelname=Model name | |||
| @@ -3024,6 +3029,13 @@ notices.desc = Description | |||
| notices.op = Op. | |||
| notices.delete_success = The system notices have been deleted. | |||
| user_management = User Management | |||
| resource_management = Resource Management | |||
| resource_pool = Resource Pool(queue) | |||
| resource_price = Resource Price | |||
| application_scenario = Application Scenario | |||
| system_configuration = System Configuration | |||
| [action] | |||
| create_repo = created repository <a href="%s">%s</a> | |||
| rename_repo = renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a> | |||
| @@ -965,6 +965,7 @@ unfavorite=取消收藏 | |||
| favorite=收藏 | |||
| disassociate=取消关联 | |||
| benchmark_dataset_tip=说明:先使用数据集功能上传模型,然后从数据集列表选模型。 | |||
| file_deleted=文件已经被删除 | |||
| [repo] | |||
| owner=拥有者 | |||
| @@ -1225,6 +1226,7 @@ modelarts.infer_job.select_model = 选择模型 | |||
| modelarts.infer_job.boot_file_helper=启动文件是您程序执行的入口文件,必须是以.py结尾的文件。比如inference.py、main.py、example/inference.py、case/main.py。 | |||
| modelarts.infer_job.tooltip = 该模型已删除,无法查看。 | |||
| modelarts.download_log=下载日志文件 | |||
| modelarts.no_node_right = 计算节点数的值配置错误,您没有权限使用当前配置的计算节点数。 | |||
| debug_task_not_created = 未创建过调试任务 | |||
| @@ -1235,6 +1237,10 @@ repo_not_initialized = 代码版本:您还没有初始化代码仓库,请先 | |||
| debug_task_running_limit = 运行时长:最长不超过4个小时,超过4个小时将自动停止; | |||
| dataset_desc = 数据集:云脑1提供 CPU / GPU 资源,云脑2提供 Ascend NPU 资源,调试使用的数据集也需要上传到对应的环境; | |||
| platform_instructions = 使用说明:可以参考启智AI协作平台<a href="https://git.openi.org.cn/zeizei/OpenI_Learning">小白训练营课程</a>。 | |||
| platform_instructions1 = 使用说明:可以参考启智AI协作平台 | |||
| platform_instructions2 = 小白训练营课程 | |||
| platform_instructions3 = 。 | |||
| model_not_exist = 模型文件:您还没有模型文件,请先通过<a href="%s/modelarts/train-job">训练任务</a>产生并 <a href="%s/modelmanage/show_model">导出模型</a> ; | |||
| benchmark_leaderboards = 基准测试排行榜 | |||
| @@ -1261,7 +1267,7 @@ model.manage.notcreatemodel=未创建过模型 | |||
| model.manage.init1=代码版本:您还没有初始化代码仓库,请先 | |||
| model.manage.init2=创建代码版本; | |||
| model.manage.createtrainjob_tip=训练任务:您还没创建过训练任务,请先创建 | |||
| model.manage.createtrainjob=训练任务 | |||
| model.manage.createtrainjob=训练任务。 | |||
| model.manage.delete=删除模型 | |||
| model.manage.delete_confirm=你确认删除该模型么?此模型一旦删除不可恢复。 | |||
| model.manage.select.trainjob=选择训练任务 | |||
| @@ -1274,7 +1280,7 @@ model.manage.modeldesc=模型描述 | |||
| model.manage.baseinfo=基本信息 | |||
| modelconvert.notcreate=未创建过模型转换任务 | |||
| modelconvert.importfirst1=请您先导入 | |||
| modelconvert.importfirst2=模型下载 | |||
| modelconvert.importfirst2=模型 | |||
| modelconvert.importfirst3=,然后再对其进行转换。 | |||
| modelconvert.download=下载 | |||
| modelconvert.taskname=任务名称 | |||
| @@ -3040,6 +3046,13 @@ notices.desc=提示描述 | |||
| notices.op=操作 | |||
| notices.delete_success=系统通知已被删除。 | |||
| user_management = 用户管理 | |||
| resource_management = 资源管理 | |||
| resource_pool = 资源池(队列) | |||
| resource_price = 资源规格单价 | |||
| application_scenario = 应用场景 | |||
| system_configuration = 系统配置 | |||
| [action] | |||
| create_repo=创建了项目 <a href="%s">%s</a> | |||
| rename_repo=重命名项目 <code>%[1]s</code> 为 <a href="%[2]s">%[3]s</a> | |||
| @@ -54,6 +54,7 @@ | |||
| "vue": "2.6.11", | |||
| "vue-bar-graph": "1.2.0", | |||
| "vue-calendar-heatmap": "0.8.4", | |||
| "vue-i18n": "6.1.3", | |||
| "vue-loader": "15.9.2", | |||
| "vue-router": "3.3.4", | |||
| "vue-template-compiler": "2.6.11", | |||
| @@ -0,0 +1,248 @@ | |||
| package admin | |||
| import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/base" | |||
| "code.gitea.io/gitea/modules/context" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/routers/response" | |||
| "code.gitea.io/gitea/services/cloudbrain/resource" | |||
| "net/http" | |||
| ) | |||
| const ( | |||
| tplResourceQueue base.TplName = "admin/resources/queue" | |||
| tplResourceSpecification base.TplName = "admin/resources/specification" | |||
| tplResourceScene base.TplName = "admin/resources/scene" | |||
| ) | |||
| func GetQueuePage(ctx *context.Context) { | |||
| ctx.Data["PageIsAdmin"] = true | |||
| ctx.Data["PageIsAdminResources"] = true | |||
| ctx.Data["PageIsAdminResourcesQueue"] = true | |||
| ctx.HTML(200, tplResourceQueue) | |||
| } | |||
| func GetSpecificationPage(ctx *context.Context) { | |||
| ctx.Data["PageIsAdmin"] = true | |||
| ctx.Data["PageIsAdminResources"] = true | |||
| ctx.Data["PageIsAdminResourcesSpecification"] = true | |||
| ctx.HTML(200, tplResourceSpecification) | |||
| } | |||
| func GetScenePage(ctx *context.Context) { | |||
| ctx.Data["PageIsAdmin"] = true | |||
| ctx.Data["PageIsAdminResources"] = true | |||
| ctx.Data["PageIsAdminResourcesScene"] = true | |||
| ctx.HTML(200, tplResourceScene) | |||
| } | |||
| func GetResourceQueueList(ctx *context.Context) { | |||
| page := ctx.QueryInt("page") | |||
| cluster := ctx.Query("cluster") | |||
| aiCenterCode := ctx.Query("center") | |||
| computeResource := ctx.Query("resource") | |||
| accCardType := ctx.Query("card") | |||
| list, err := resource.GetResourceQueueList(models.SearchResourceQueueOptions{ | |||
| ListOptions: models.ListOptions{Page: page, PageSize: 10}, | |||
| Cluster: cluster, | |||
| AiCenterCode: aiCenterCode, | |||
| ComputeResource: computeResource, | |||
| AccCardType: accCardType, | |||
| }) | |||
| if err != nil { | |||
| log.Error("GetResourceQueueList error.%v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.SuccessWithData(list)) | |||
| } | |||
| func GetResourceQueueCodes(ctx *context.Context) { | |||
| cluster := ctx.Query("cluster") | |||
| list, err := resource.GetResourceQueueCodes(models.GetQueueCodesOptions{Cluster: cluster}) | |||
| if err != nil { | |||
| log.Error("GetResourceQueueCodes error.%v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.SuccessWithData(list)) | |||
| } | |||
| func GetResourceAiCenters(ctx *context.Context) { | |||
| list, err := resource.GetResourceAiCenters() | |||
| if err != nil { | |||
| log.Error("GetResourceAiCenters error.%v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.SuccessWithData(list)) | |||
| } | |||
| func AddResourceQueue(ctx *context.Context, req models.ResourceQueueReq) { | |||
| req.IsAutomaticSync = false | |||
| req.CreatorId = ctx.User.ID | |||
| err := resource.AddResourceQueue(req) | |||
| if err != nil { | |||
| log.Error("AddResourceQueue error. %v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.Success()) | |||
| } | |||
| 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) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.Success()) | |||
| } | |||
| func SyncGrampusQueue(ctx *context.Context) { | |||
| err := resource.SyncGrampusQueue(ctx.User.ID) | |||
| if err != nil { | |||
| log.Error("AddResourceQueue error. %v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.Success()) | |||
| } | |||
| func GetResourceSpecificationList(ctx *context.Context) { | |||
| page := ctx.QueryInt("page") | |||
| queue := ctx.QueryInt64("queue") | |||
| status := ctx.QueryInt("status") | |||
| cluster := ctx.Query("cluster") | |||
| list, err := resource.GetResourceSpecificationList(models.SearchResourceSpecificationOptions{ | |||
| ListOptions: models.ListOptions{Page: page, PageSize: 10}, | |||
| QueueId: queue, | |||
| Status: status, | |||
| Cluster: cluster, | |||
| }) | |||
| if err != nil { | |||
| log.Error("GetResourceSpecificationList error.%v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.SuccessWithData(list)) | |||
| } | |||
| func GetResourceSpecificationScenes(ctx *context.Context) { | |||
| specId := ctx.ParamsInt64(":id") | |||
| list, err := resource.GetResourceSpecificationScenes(specId) | |||
| if err != nil { | |||
| log.Error("GetResourceSpecificationScenes error.%v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| r := make(map[string]interface{}) | |||
| r["List"] = list | |||
| ctx.JSON(http.StatusOK, response.SuccessWithData(r)) | |||
| } | |||
| func AddResourceSpecification(ctx *context.Context, req models.ResourceSpecificationReq) { | |||
| req.IsAutomaticSync = false | |||
| req.CreatorId = ctx.User.ID | |||
| err := resource.AddResourceSpecification(ctx.User.ID, req) | |||
| if err != nil { | |||
| log.Error("AddResourceQueue error. %v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.Success()) | |||
| } | |||
| func UpdateResourceSpecification(ctx *context.Context, req models.ResourceSpecificationReq) { | |||
| id := ctx.ParamsInt64(":id") | |||
| action := ctx.Query("action") | |||
| var err *response.BizError | |||
| switch action { | |||
| case "edit": | |||
| if req.UnitPrice < 0 { | |||
| ctx.JSON(http.StatusOK, response.ServerError("param error")) | |||
| return | |||
| } | |||
| //only UnitPrice and permitted to change | |||
| err = resource.UpdateSpecUnitPrice(ctx.User.ID, id, req.UnitPrice) | |||
| case "on-shelf": | |||
| err = resource.ResourceSpecOnShelf(ctx.User.ID, id, req.UnitPrice) | |||
| case "off-shelf": | |||
| err = resource.ResourceSpecOffShelf(ctx.User.ID, id) | |||
| } | |||
| if err != nil { | |||
| log.Error("UpdateResourceSpecification error. %v", err) | |||
| ctx.JSON(http.StatusOK, response.ResponseError(err)) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.Success()) | |||
| } | |||
| func SyncGrampusSpecs(ctx *context.Context) { | |||
| err := resource.SyncGrampusSpecs(ctx.User.ID) | |||
| if err != nil { | |||
| log.Error("AddResourceQueue error. %v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.Success()) | |||
| } | |||
| func GetResourceSceneList(ctx *context.Context) { | |||
| page := ctx.QueryInt("page") | |||
| jobType := ctx.Query("jobType") | |||
| aiCenterCode := ctx.Query("center") | |||
| queueId := ctx.QueryInt64("queue") | |||
| isExclusive := ctx.QueryInt("IsExclusive") | |||
| list, err := resource.GetResourceSceneList(models.SearchResourceSceneOptions{ | |||
| ListOptions: models.ListOptions{Page: page, PageSize: 10}, | |||
| JobType: jobType, | |||
| IsExclusive: isExclusive, | |||
| AiCenterCode: aiCenterCode, | |||
| QueueId: queueId, | |||
| }) | |||
| if err != nil { | |||
| log.Error("GetResourceSceneList error.%v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.SuccessWithData(list)) | |||
| } | |||
| func AddResourceScene(ctx *context.Context, req models.ResourceSceneReq) { | |||
| req.CreatorId = ctx.User.ID | |||
| err := resource.AddResourceScene(req) | |||
| if err != nil { | |||
| log.Error("AddResourceScene error. %v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.Success()) | |||
| } | |||
| func UpdateResourceScene(ctx *context.Context, req models.ResourceSceneReq) { | |||
| id := ctx.ParamsInt64(":id") | |||
| action := ctx.Query("action") | |||
| req.ID = id | |||
| var err error | |||
| switch action { | |||
| case "edit": | |||
| err = resource.UpdateResourceScene(req) | |||
| case "delete": | |||
| err = resource.DeleteResourceScene(id) | |||
| } | |||
| if err != nil { | |||
| log.Error("UpdateResourceScene error. %v", err) | |||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, response.Success()) | |||
| } | |||
| @@ -405,52 +405,159 @@ func CloudbrainDownloadLogFile(ctx *context.Context) { | |||
| func CloudbrainGetLog(ctx *context.Context) { | |||
| ID := ctx.Params(":id") | |||
| startLine := ctx.QueryInt("base_line") | |||
| lines := ctx.QueryInt("lines") | |||
| endLine := startLine + lines | |||
| order := ctx.Query("order") | |||
| if order == "asc" { | |||
| endLine = startLine | |||
| startLine = endLine - lines | |||
| if startLine < 0 { | |||
| startLine = 0 | |||
| } | |||
| } | |||
| job, err := models.GetCloudbrainByID(ID) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainByJobName failed: %v", err, ctx.Data["MsgID"]) | |||
| ctx.ServerError(err.Error(), err) | |||
| return | |||
| } | |||
| result := getLogFromModelDir(job.JobName, startLine, endLine) | |||
| if result == nil { | |||
| log.Error("GetJobLog failed: %v", err, ctx.Data["MsgID"]) | |||
| ctx.ServerError(err.Error(), err) | |||
| return | |||
| lines := ctx.QueryInt("lines") | |||
| baseLine := ctx.Query("base_line") | |||
| order := ctx.Query("order") | |||
| var result map[string]interface{} | |||
| resultPath := "/model" | |||
| if job.JobType == string(models.JobTypeInference) { | |||
| resultPath = "/result" | |||
| } | |||
| if baseLine == "" && order == "desc" { | |||
| result = getLastLogFromModelDir(job.JobName, lines, resultPath) | |||
| } else { | |||
| startLine := ctx.QueryInt("base_line") | |||
| endLine := startLine + lines | |||
| if order == "asc" { | |||
| if baseLine == "" { | |||
| startLine = 0 | |||
| endLine = lines | |||
| } else { | |||
| endLine = startLine | |||
| startLine = endLine - lines | |||
| if startLine < 0 { | |||
| startLine = 0 | |||
| } | |||
| } | |||
| } | |||
| result = getLogFromModelDir(job.JobName, startLine, endLine, resultPath) | |||
| if result == nil { | |||
| log.Error("GetJobLog failed: %v", err, ctx.Data["MsgID"]) | |||
| ctx.ServerError(err.Error(), err) | |||
| return | |||
| } | |||
| } | |||
| re := map[string]interface{}{ | |||
| "JobID": ID, | |||
| "LogFileName": result["FileName"], | |||
| "StartLine": startLine, | |||
| "EndLine": result["endLine"], | |||
| "StartLine": result["StartLine"], | |||
| "EndLine": result["EndLine"], | |||
| "Content": result["Content"], | |||
| "Lines": result["lines"], | |||
| "Lines": result["Lines"], | |||
| "CanLogDownload": result["FileName"] != "", | |||
| } | |||
| //result := CloudbrainGetLogByJobId(job.JobID, job.JobName) | |||
| ctx.JSON(http.StatusOK, re) | |||
| } | |||
| func getLogFromModelDir(jobName string, startLine int, endLine int) map[string]interface{} { | |||
| prefix := "/" + setting.CBCodePathPrefix + jobName + "/model" | |||
| func getAllLineFromFile(path string) int { | |||
| count := 0 | |||
| reader, err := os.Open(path) | |||
| defer reader.Close() | |||
| if err == nil { | |||
| r := bufio.NewReader(reader) | |||
| for { | |||
| _, error := r.ReadString('\n') | |||
| if error == io.EOF { | |||
| log.Info("read file completed.") | |||
| break | |||
| } | |||
| if error != nil { | |||
| log.Info("read file error." + error.Error()) | |||
| break | |||
| } | |||
| count = count + 1 | |||
| } | |||
| } else { | |||
| log.Info("error:" + err.Error()) | |||
| } | |||
| return count | |||
| } | |||
| func getLastLogFromModelDir(jobName string, lines int, resultPath string) map[string]interface{} { | |||
| prefix := "/" + setting.CBCodePathPrefix + jobName + resultPath | |||
| files, err := storage.GetOneLevelAllObjectUnderDirMinio(setting.Attachment.Minio.Bucket, prefix, "") | |||
| if err != nil { | |||
| log.Error("query cloudbrain model failed: %v", err) | |||
| return nil | |||
| } | |||
| re := "" | |||
| fileName := "" | |||
| count := 0 | |||
| allLines := 0 | |||
| startLine := 0 | |||
| for _, file := range files { | |||
| if strings.HasSuffix(file.FileName, "log.txt") { | |||
| fileName = file.FileName | |||
| path := storage.GetMinioPath(jobName+resultPath+"/", file.FileName) | |||
| allLines = getAllLineFromFile(path) | |||
| startLine = allLines - lines | |||
| if startLine < 0 { | |||
| startLine = 0 | |||
| } | |||
| count = allLines - startLine | |||
| log.Info("path=" + path) | |||
| reader, err := os.Open(path) | |||
| defer reader.Close() | |||
| if err == nil { | |||
| r := bufio.NewReader(reader) | |||
| for i := 0; i < allLines; i++ { | |||
| line, error := r.ReadString('\n') | |||
| if error == io.EOF { | |||
| log.Info("read file completed.") | |||
| break | |||
| } | |||
| if error != nil { | |||
| log.Info("read file error." + error.Error()) | |||
| break | |||
| } | |||
| if error == nil { | |||
| if i >= startLine { | |||
| re = re + line | |||
| } | |||
| } | |||
| } | |||
| } else { | |||
| log.Info("error:" + err.Error()) | |||
| } | |||
| break | |||
| } | |||
| } | |||
| return map[string]interface{}{ | |||
| "JobName": jobName, | |||
| "Content": re, | |||
| "FileName": fileName, | |||
| "Lines": count, | |||
| "EndLine": allLines, | |||
| "StartLine": startLine, | |||
| } | |||
| } | |||
| func getLogFromModelDir(jobName string, startLine int, endLine int, resultPath string) map[string]interface{} { | |||
| prefix := "/" + setting.CBCodePathPrefix + jobName + resultPath | |||
| files, err := storage.GetOneLevelAllObjectUnderDirMinio(setting.Attachment.Minio.Bucket, prefix, "") | |||
| if err != nil { | |||
| log.Error("query cloudbrain model failed: %v", err) | |||
| return nil | |||
| } | |||
| if startLine == endLine { | |||
| return map[string]interface{}{ | |||
| "JobName": jobName, | |||
| "Content": "", | |||
| "FileName": "", | |||
| "Lines": 0, | |||
| "EndLine": startLine, | |||
| "StartLine": startLine, | |||
| } | |||
| } | |||
| re := "" | |||
| fileName := "" | |||
| count := 0 | |||
| @@ -458,7 +565,7 @@ func getLogFromModelDir(jobName string, startLine int, endLine int) map[string]i | |||
| for _, file := range files { | |||
| if strings.HasSuffix(file.FileName, "log.txt") { | |||
| fileName = file.FileName | |||
| path := storage.GetMinioPath(jobName+"/model/", file.FileName) | |||
| path := storage.GetMinioPath(jobName+resultPath+"/", file.FileName) | |||
| log.Info("path=" + path) | |||
| reader, err := os.Open(path) | |||
| defer reader.Close() | |||
| @@ -467,7 +574,6 @@ func getLogFromModelDir(jobName string, startLine int, endLine int) map[string]i | |||
| for i := 0; i < endLine; i++ { | |||
| line, error := r.ReadString('\n') | |||
| log.Info("line=" + line) | |||
| fileEndLine = i | |||
| if error == io.EOF { | |||
| log.Info("read file completed.") | |||
| break | |||
| @@ -478,11 +584,13 @@ func getLogFromModelDir(jobName string, startLine int, endLine int) map[string]i | |||
| } | |||
| if error == nil { | |||
| if i >= startLine { | |||
| fileEndLine = i | |||
| re = re + line | |||
| count++ | |||
| } | |||
| } | |||
| } | |||
| fileEndLine = fileEndLine + 1 | |||
| } else { | |||
| log.Info("error:" + err.Error()) | |||
| } | |||
| @@ -491,11 +599,12 @@ func getLogFromModelDir(jobName string, startLine int, endLine int) map[string]i | |||
| } | |||
| return map[string]interface{}{ | |||
| "JobName": jobName, | |||
| "Content": re, | |||
| "FileName": fileName, | |||
| "lines": count, | |||
| "endLine": fileEndLine, | |||
| "JobName": jobName, | |||
| "Content": re, | |||
| "FileName": fileName, | |||
| "Lines": count, | |||
| "EndLine": fileEndLine, | |||
| "StartLine": startLine, | |||
| } | |||
| } | |||
| @@ -928,7 +928,7 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo | |||
| } | |||
| } | |||
| ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, false) | |||
| ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, task.DatasetName, false) | |||
| ctx.Data["task"] = task | |||
| labelName := strings.Fields(task.LabelName) | |||
| ctx.Data["LabelName"] = labelName | |||
| @@ -2431,7 +2431,8 @@ func BenchMarkAlgorithmCreate(ctx *context.Context, form auth.CreateCloudBrainFo | |||
| ctx.RenderWithErr(ctx.Tr("cloudbrain.error.dataset_select"), tplCloudBrainBenchmarkNew, &form) | |||
| return | |||
| } | |||
| log.Info("Command=" + command) | |||
| log.Info("ModelPath=" + storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/")) | |||
| req := cloudbrain.GenerateCloudBrainTaskReq{ | |||
| Ctx: ctx, | |||
| DisplayJobName: displayJobName, | |||
| @@ -2560,7 +2561,8 @@ func ModelBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm) | |||
| ctx.RenderWithErr(ctx.Tr("cloudbrain.error.dataset_select"), tpl, &form) | |||
| return | |||
| } | |||
| log.Info("Command=" + command) | |||
| log.Info("ModelPath=" + storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/")) | |||
| req := cloudbrain.GenerateCloudBrainTaskReq{ | |||
| Ctx: ctx, | |||
| DisplayJobName: displayJobName, | |||
| @@ -2689,7 +2691,7 @@ func getInferenceJobCommand(form auth.CreateCloudBrainInferencForm) (string, err | |||
| param += " --modelname" + "=" + form.CkptName | |||
| command += "python /code/" + bootFile + param + " > " + cloudbrain.ResultPath + "/" + form.DisplayJobName + "-" + cloudbrain.LogFile | |||
| command += "python /code/" + bootFile + param + " | tee " + cloudbrain.ResultPath + "/" + form.DisplayJobName + "-" + cloudbrain.LogFile | |||
| return command, nil | |||
| } | |||
| @@ -45,15 +45,10 @@ func newFilterPrivateAttachments(ctx *context.Context, list []*models.Attachment | |||
| repo.GetOwner() | |||
| } | |||
| permission := false | |||
| if repo.Owner.IsOrganization() && ctx.User != nil { | |||
| if repo.Owner.IsUserPartOfOrg(ctx.User.ID) { | |||
| log.Info("user is member of org.") | |||
| permission = true | |||
| } | |||
| } | |||
| if !permission && ctx.User != nil { | |||
| isCollaborator, _ := repo.IsCollaborator(ctx.User.ID) | |||
| if isCollaborator { | |||
| isInRepoTeam,_:=repo.IsInRepoTeam(ctx.User.ID) | |||
| if isCollaborator ||isInRepoTeam { | |||
| log.Info("Collaborator user may visit the attach.") | |||
| permission = true | |||
| } | |||
| @@ -529,6 +524,10 @@ func ReferenceDatasetAvailable(ctx *context.Context) { | |||
| NeedAttachment: false, | |||
| CloudBrainType: models.TypeCloudBrainAll, | |||
| } | |||
| dataset, _ := models.GetDatasetByRepo(&models.Repository{ID: ctx.Repo.Repository.ID}) | |||
| if dataset != nil { | |||
| opts.ExcludeDatasetId = dataset.ID | |||
| } | |||
| datasetMultiple(ctx, opts) | |||
| } | |||
| @@ -713,7 +713,7 @@ func GrampusTrainJobShow(ctx *context.Context) { | |||
| taskList := make([]*models.Cloudbrain, 0) | |||
| taskList = append(taskList, task) | |||
| ctx.Data["version_list_task"] = taskList | |||
| ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, false) | |||
| ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, task.DatasetName, false) | |||
| ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, task) | |||
| ctx.Data["displayJobName"] = task.DisplayJobName | |||
| @@ -285,7 +285,7 @@ func NotebookShow(ctx *context.Context) { | |||
| datasetDownload := make([]models.DatasetDownload, 0) | |||
| if ctx.IsSigned { | |||
| if task.Uuid != "" && task.UserID == ctx.User.ID { | |||
| datasetDownload = GetCloudBrainDataSetInfo(task.Uuid, true) | |||
| datasetDownload = GetCloudBrainDataSetInfo(task.Uuid, task.DatasetName, true) | |||
| } | |||
| } | |||
| user, err := models.GetUserByID(task.UserID) | |||
| @@ -331,36 +331,52 @@ func NotebookShow(ctx *context.Context) { | |||
| ctx.HTML(200, tplModelArtsNotebookShow) | |||
| } | |||
| func GetCloudBrainDataSetInfo(uuid string, isNeedDown bool) []models.DatasetDownload { | |||
| func GetCloudBrainDataSetInfo(uuid string, datasetname string, isNeedDown bool) []models.DatasetDownload { | |||
| datasetDownload := make([]models.DatasetDownload, 0) | |||
| if len(uuid) == 0 { | |||
| return datasetDownload | |||
| } | |||
| uuidList := strings.Split(uuid, ";") | |||
| for _, uuidStr := range uuidList { | |||
| datasetnameList := strings.Split(datasetname, ";") | |||
| for i, uuidStr := range uuidList { | |||
| name := "" | |||
| link := "" | |||
| url := "" | |||
| isDelete := false | |||
| attachment, err := models.GetAttachmentByUUID(uuidStr) | |||
| if err != nil { | |||
| log.Error("GetAttachmentByUUID failed:%v", err.Error()) | |||
| return datasetDownload | |||
| } | |||
| dataset, err := models.GetDatasetByID(attachment.DatasetID) | |||
| if err != nil { | |||
| log.Error("GetDatasetByID failed:%v", err.Error()) | |||
| return datasetDownload | |||
| } | |||
| repo, err := models.GetRepositoryByID(dataset.RepoID) | |||
| if err != nil { | |||
| log.Error("GetRepositoryByID failed:%v", err.Error()) | |||
| return datasetDownload | |||
| } | |||
| url := "" | |||
| if isNeedDown { | |||
| url = attachment.S3DownloadURL() | |||
| if len(datasetnameList) <= i || len(datasetname) == 0 { | |||
| continue | |||
| } | |||
| name = datasetnameList[i] | |||
| isDelete = true | |||
| } else { | |||
| name = attachment.Name | |||
| dataset, err := models.GetDatasetByID(attachment.DatasetID) | |||
| if err != nil { | |||
| log.Error("GetDatasetByID failed:%v", err.Error()) | |||
| } else { | |||
| repo, err := models.GetRepositoryByID(dataset.RepoID) | |||
| if err != nil { | |||
| log.Error("GetRepositoryByID failed:%v", err.Error()) | |||
| } else { | |||
| link = repo.Link() + "/datasets" | |||
| } | |||
| } | |||
| if isNeedDown { | |||
| url = attachment.S3DownloadURL() | |||
| } | |||
| } | |||
| datasetDownload = append(datasetDownload, models.DatasetDownload{ | |||
| DatasetName: attachment.Name, | |||
| DatasetName: name, | |||
| DatasetDownloadLink: url, | |||
| RepositoryLink: repo.Link() + "/datasets", | |||
| RepositoryLink: link, | |||
| IsDelete: isDelete, | |||
| }) | |||
| } | |||
| log.Info("dataset length=" + fmt.Sprint(len(datasetDownload))) | |||
| return datasetDownload | |||
| } | |||
| @@ -747,9 +763,23 @@ func trainJobNewDataPrepare(ctx *context.Context) error { | |||
| waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") | |||
| ctx.Data["WaitCount"] = waitCount | |||
| setMultiNodeIfConfigureMatch(ctx) | |||
| return nil | |||
| } | |||
| func setMultiNodeIfConfigureMatch(ctx *context.Context) { | |||
| modelarts.InitMultiNode() | |||
| if modelarts.MultiNodeConfig != nil { | |||
| for _, info := range modelarts.MultiNodeConfig.Info { | |||
| if isInOrg, _ := models.IsOrganizationMemberByOrgName(info.Org, ctx.User.ID); isInOrg { | |||
| ctx.Data["WorkNode"] = info.Node | |||
| break | |||
| } | |||
| } | |||
| } | |||
| } | |||
| func setSpecBySpecialPoolConfig(ctx *context.Context, jobType string) { | |||
| modelarts.InitSpecialPool() | |||
| @@ -864,6 +894,7 @@ func trainJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModelArts | |||
| ctx.Data["datasetType"] = models.TypeCloudBrainTwo | |||
| waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") | |||
| ctx.Data["WaitCount"] = waitCount | |||
| setMultiNodeIfConfigureMatch(ctx) | |||
| return nil | |||
| } | |||
| @@ -949,14 +980,17 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error { | |||
| } | |||
| _, _, datasetNames, _, err := getDatasUrlListByUUIDS(task.Uuid) | |||
| if err != nil { | |||
| ctx.ServerError("GetAllUserAttachments failed:", err) | |||
| return err | |||
| log.Info("query dataset error," + err.Error()) | |||
| //ctx.ServerError("GetAllUserAttachments failed:", err) | |||
| //return err | |||
| } else { | |||
| ctx.Data["dataset_name"] = datasetNames | |||
| } | |||
| ctx.Data["branches"] = branches | |||
| ctx.Data["branch_name"] = task.BranchName | |||
| ctx.Data["description"] = task.Description | |||
| ctx.Data["boot_file"] = task.BootFile | |||
| ctx.Data["dataset_name"] = datasetNames | |||
| ctx.Data["work_server_number"] = task.WorkServerNumber | |||
| ctx.Data["flavor_name"] = task.FlavorName | |||
| ctx.Data["engine_name"] = task.EngineName | |||
| @@ -1096,6 +1130,13 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) | |||
| VersionCount := modelarts.VersionCountOne | |||
| EngineName := form.EngineName | |||
| errStr:=checkMultiNode(ctx.User.ID,form.WorkServerNumber) | |||
| if errStr!=""{ | |||
| trainJobErrorNewDataPrepare(ctx, form) | |||
| ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsTrainJobNew, &form) | |||
| return | |||
| } | |||
| count, err := models.GetCloudbrainTrainJobCountByUserID(ctx.User.ID) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainTrainJobCountByUserID failed:%v", err, ctx.Data["MsgID"]) | |||
| @@ -1126,7 +1167,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) | |||
| return | |||
| } | |||
| errStr := checkModelArtsSpecialPool(ctx, flavorCode, string(models.JobTypeTrain)) | |||
| errStr = checkModelArtsSpecialPool(ctx, flavorCode, string(models.JobTypeTrain)) | |||
| if errStr != "" { | |||
| trainJobErrorNewDataPrepare(ctx, form) | |||
| ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsTrainJobNew, &form) | |||
| @@ -1330,6 +1371,48 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) | |||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job") | |||
| } | |||
| func checkMultiNode(userId int64, serverNum int) string{ | |||
| if serverNum==1{ | |||
| return "" | |||
| } | |||
| modelarts.InitMultiNode() | |||
| var isServerNumValid=false | |||
| if modelarts.MultiNodeConfig != nil { | |||
| for _, info := range modelarts.MultiNodeConfig.Info { | |||
| if isInOrg, _ := models.IsOrganizationMemberByOrgName(info.Org, userId); isInOrg { | |||
| if isInNodes(info.Node,serverNum){ | |||
| isServerNumValid=true | |||
| break | |||
| } | |||
| } | |||
| } | |||
| } | |||
| if isServerNumValid{ | |||
| return "" | |||
| }else{ | |||
| return "repo.modelarts.no_node_right" | |||
| } | |||
| } | |||
| func checkInferenceJobMultiNode(userId int64, serverNum int) string{ | |||
| if serverNum==1{ | |||
| return "" | |||
| } | |||
| return "repo.modelarts.no_node_right" | |||
| } | |||
| func isInNodes(nodes []int, num int) bool { | |||
| for _, node:=range nodes{ | |||
| if node==num{ | |||
| return true | |||
| } | |||
| } | |||
| return false | |||
| } | |||
| func getUserCommand(engineId int, req *modelarts.GenerateTrainJobReq) (string, string) { | |||
| userImageUrl := "" | |||
| userCommand := "" | |||
| @@ -1364,6 +1447,13 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ | |||
| ctx.Data["PageIsTrainJob"] = true | |||
| var jobID = ctx.Params(":jobid") | |||
| errStr:=checkMultiNode(ctx.User.ID,form.WorkServerNumber) | |||
| if errStr!=""{ | |||
| versionErrorDataPrepare(ctx, form) | |||
| ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsTrainJobVersionNew, &form) | |||
| return | |||
| } | |||
| count, err := models.GetCloudbrainTrainJobCountByUserID(ctx.User.ID) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainTrainJobCountByUserID failed:%v", err, ctx.Data["MsgID"]) | |||
| @@ -1431,7 +1521,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ | |||
| return | |||
| } | |||
| errStr := checkModelArtsSpecialPool(ctx, flavorCode, string(models.JobTypeTrain)) | |||
| errStr = checkModelArtsSpecialPool(ctx, flavorCode, string(models.JobTypeTrain)) | |||
| if errStr != "" { | |||
| versionErrorDataPrepare(ctx, form) | |||
| ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsTrainJobVersionNew, &form) | |||
| @@ -1699,11 +1789,7 @@ func paramCheckCreateTrainJob(form auth.CreateModelArtsTrainJobForm) error { | |||
| log.Error("the boot file(%s) must be a python file", strings.TrimSpace(form.BootFile)) | |||
| return errors.New("启动文件必须是python文件") | |||
| } | |||
| if form.WorkServerNumber > 2 || form.WorkServerNumber < 1 { | |||
| log.Error("the WorkServerNumber(%d) must be in (1,2)", form.WorkServerNumber) | |||
| return errors.New("计算节点数必须在1-2之间") | |||
| } | |||
| if form.BranchName == "" { | |||
| log.Error("the branch must not be null!", form.BranchName) | |||
| return errors.New("代码分支不能为空!") | |||
| @@ -1810,7 +1896,7 @@ func TrainJobShow(ctx *context.Context) { | |||
| } else { | |||
| VersionListTasks[i].Parameters = "" | |||
| } | |||
| datasetList = append(datasetList, GetCloudBrainDataSetInfo(task.Uuid, false)) | |||
| datasetList = append(datasetList, GetCloudBrainDataSetInfo(task.Uuid, task.DatasetName, false)) | |||
| VersionListTasks[i].CanDel = cloudbrain.CanDeleteJob(ctx, &task.Cloudbrain) | |||
| VersionListTasks[i].CanModify = cloudbrain.CanModifyJob(ctx, &task.Cloudbrain) | |||
| } | |||
| @@ -2002,6 +2088,13 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference | |||
| ckptUrl := "/" + form.TrainUrl + form.CkptName | |||
| log.Info("ckpt url:" + ckptUrl) | |||
| errStr:=checkInferenceJobMultiNode(ctx.User.ID,form.WorkServerNumber) | |||
| if errStr!=""{ | |||
| inferenceJobErrorNewDataPrepare(ctx, form) | |||
| ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsInferenceJobNew, &form) | |||
| return | |||
| } | |||
| count, err := models.GetCloudbrainInferenceJobCountByUserID(ctx.User.ID) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainInferenceJobCountByUserID failed:%v", err, ctx.Data["MsgID"]) | |||
| @@ -2050,7 +2143,7 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference | |||
| } | |||
| } | |||
| errStr := checkModelArtsSpecialPool(ctx, flavorCode, string(models.JobTypeInference)) | |||
| errStr = checkModelArtsSpecialPool(ctx, flavorCode, string(models.JobTypeInference)) | |||
| if errStr != "" { | |||
| inferenceJobErrorNewDataPrepare(ctx, form) | |||
| ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsInferenceJobNew, &form) | |||
| @@ -2526,7 +2619,7 @@ func InferenceJobShow(ctx *context.Context) { | |||
| ctx.Data["displayJobName"] = task.DisplayJobName | |||
| ctx.Data["task"] = task | |||
| ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, task) | |||
| ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, false) | |||
| ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, task.DatasetName, false) | |||
| tempUids := []int64{} | |||
| tempUids = append(tempUids, task.UserID) | |||
| JobCreater, err := models.GetUserNamesByIDs(tempUids) | |||
| @@ -0,0 +1,14 @@ | |||
| package response | |||
| type BizError struct { | |||
| Code int | |||
| Err string | |||
| } | |||
| func (b BizError) Error() string { | |||
| return b.Err | |||
| } | |||
| func NewBizError(err error) *BizError { | |||
| return &BizError{Code: RESPONSE_CODE_ERROR_DEFAULT, Err: err.Error()} | |||
| } | |||
| @@ -24,8 +24,12 @@ func ServerError(msg string) *AiforgeResponse { | |||
| return &AiforgeResponse{Code: RESPONSE_CODE_ERROR_DEFAULT, Msg: msg} | |||
| } | |||
| func ResponseError(err *BizError) *AiforgeResponse { | |||
| return &AiforgeResponse{Code: err.Code, Msg: err.Err} | |||
| } | |||
| func SuccessWithData(data interface{}) *AiforgeResponse { | |||
| return &AiforgeResponse{Code: RESPONSE_CODE_ERROR_DEFAULT, Msg: RESPONSE_MSG_SUCCESS, Data: data} | |||
| return &AiforgeResponse{Code: RESPONSE_CODE_SUCCESS, Msg: RESPONSE_MSG_SUCCESS, Data: data} | |||
| } | |||
| func ErrorWithData(code int, msg string, data interface{}) *AiforgeResponse { | |||
| return &AiforgeResponse{Code: code, Msg: msg, Data: data} | |||
| @@ -0,0 +1,4 @@ | |||
| package response | |||
| var RESOURCE_QUEUE_NOT_AVAILABLE = &BizError{Code: 1001, Err: "resource queue not available"} | |||
| var SPECIFICATION_NOT_EXIST = &BizError{Code: 1002, Err: "specification not exist"} | |||
| @@ -608,6 +608,32 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Post("/delete", admin.DeleteNotices) | |||
| m.Post("/empty", admin.EmptyNotices) | |||
| }) | |||
| m.Group("/resources", func() { | |||
| m.Group("/queue", func() { | |||
| m.Get("", admin.GetQueuePage) | |||
| m.Get("/list", admin.GetResourceQueueList) | |||
| m.Post("/grampus/sync", admin.SyncGrampusQueue) | |||
| m.Get("/codes", admin.GetResourceQueueCodes) | |||
| m.Get("/centers", admin.GetResourceAiCenters) | |||
| m.Post("/add", binding.Bind(models.ResourceQueueReq{}), admin.AddResourceQueue) | |||
| m.Post("/update/:id", binding.BindIgnErr(models.ResourceQueueReq{}), admin.UpdateResourceQueue) | |||
| }) | |||
| m.Group("/specification", func() { | |||
| m.Get("", admin.GetSpecificationPage) | |||
| m.Get("/list", admin.GetResourceSpecificationList) | |||
| m.Get("/scenes/:id", admin.GetResourceSpecificationScenes) | |||
| m.Post("/grampus/sync", admin.SyncGrampusSpecs) | |||
| m.Post("/add", binding.Bind(models.ResourceSpecificationReq{}), admin.AddResourceSpecification) | |||
| m.Post("/update/:id", binding.BindIgnErr(models.ResourceSpecificationReq{}), admin.UpdateResourceSpecification) | |||
| }) | |||
| m.Group("/scene", func() { | |||
| m.Get("", admin.GetScenePage) | |||
| m.Get("/list", admin.GetResourceSceneList) | |||
| m.Post("/add", binding.Bind(models.ResourceSceneReq{}), admin.AddResourceScene) | |||
| m.Post("/update/:id", binding.BindIgnErr(models.ResourceSceneReq{}), admin.UpdateResourceScene) | |||
| }) | |||
| }) | |||
| }, adminReq) | |||
| // ***** END: Admin ***** | |||
| @@ -0,0 +1,14 @@ | |||
| package operate_log | |||
| import ( | |||
| "code.gitea.io/gitea/models" | |||
| ) | |||
| func Log(log models.AdminOperateLog) error { | |||
| _, err := models.InsertAdminOperateLog(log) | |||
| return err | |||
| } | |||
| func NewLogValues() *models.LogValues { | |||
| return &models.LogValues{Params: make([]models.LogValue, 0)} | |||
| } | |||
| @@ -0,0 +1,122 @@ | |||
| package resource | |||
| import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/grampus" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "fmt" | |||
| "strings" | |||
| ) | |||
| func AddResourceQueue(req models.ResourceQueueReq) error { | |||
| if _, err := models.InsertResourceQueue(req.ToDTO()); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| func UpdateResourceQueue(queueId int64, req models.ResourceQueueReq) error { | |||
| if _, err := models.UpdateResourceQueueById(queueId, models.ResourceQueue{ | |||
| CardsTotalNum: req.CardsTotalNum, | |||
| Remark: req.Remark, | |||
| }); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| func GetResourceQueueList(opts models.SearchResourceQueueOptions) (*models.ResourceQueueListRes, error) { | |||
| n, r, err := models.SearchResourceQueue(opts) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return models.NewResourceQueueListRes(n, r), nil | |||
| } | |||
| func GetResourceQueueCodes(opts models.GetQueueCodesOptions) ([]*models.ResourceQueueCodesRes, error) { | |||
| r, err := models.GetResourceQueueCodes(opts) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return r, nil | |||
| } | |||
| func GetResourceAiCenters() ([]models.ResourceAiCenterRes, error) { | |||
| r, err := models.GetResourceAiCenters() | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return r, nil | |||
| } | |||
| func SyncGrampusQueue(doerId int64) error { | |||
| r, err := grampus.GetAiCenters(1, 100) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| log.Info("SyncGrampusQueue result = %+v", r) | |||
| queueUpdateList := make([]models.ResourceQueue, 0) | |||
| queueInsertList := make([]models.ResourceQueue, 0) | |||
| existIds := make([]int64, 0) | |||
| for _, center := range r.Infos { | |||
| for _, device := range center.AccDevices { | |||
| computeResource := models.ParseComputeResourceFormGrampus(device.Kind) | |||
| accCardType := strings.ToUpper(device.Model) | |||
| if computeResource == "" { | |||
| continue | |||
| } | |||
| //Determine if this quque already exists.if exist,update params | |||
| //if not exist,insert a new record | |||
| oldQueue, err := models.GetResourceQueue(&models.ResourceQueue{ | |||
| Cluster: models.C2NetCluster, | |||
| AiCenterCode: center.Id, | |||
| ComputeResource: computeResource, | |||
| AccCardType: accCardType, | |||
| }) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if oldQueue == nil { | |||
| queueInsertList = append(queueInsertList, models.ResourceQueue{ | |||
| Cluster: models.C2NetCluster, | |||
| AiCenterCode: center.Id, | |||
| AiCenterName: center.Name, | |||
| ComputeResource: computeResource, | |||
| AccCardType: accCardType, | |||
| IsAutomaticSync: true, | |||
| CreatedBy: doerId, | |||
| UpdatedBy: doerId, | |||
| }) | |||
| } else { | |||
| existIds = append(existIds, oldQueue.ID) | |||
| queueUpdateList = append(queueUpdateList, models.ResourceQueue{ | |||
| ID: oldQueue.ID, | |||
| ComputeResource: computeResource, | |||
| AiCenterName: center.Name, | |||
| AccCardType: accCardType, | |||
| UpdatedBy: doerId, | |||
| }) | |||
| } | |||
| } | |||
| } | |||
| return models.SyncGrampusQueues(queueUpdateList, queueInsertList, existIds) | |||
| } | |||
| func SyncGrampusQueueAndSpecs() { | |||
| defer func() { | |||
| if err := recover(); err != nil { | |||
| combinedErr := fmt.Errorf("%s\n%s", err, log.Stack(2)) | |||
| log.Error("PANIC:", combinedErr) | |||
| } | |||
| }() | |||
| log.Info("start to sync grampus queue and specs") | |||
| SyncGrampusQueue(0) | |||
| SyncGrampusSpecs(0) | |||
| log.Info("sync grampus queue and specs finished") | |||
| } | |||
| @@ -0,0 +1,35 @@ | |||
| package resource | |||
| import ( | |||
| "code.gitea.io/gitea/models" | |||
| ) | |||
| func AddResourceScene(req models.ResourceSceneReq) error { | |||
| if err := models.InsertResourceScene(req); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| func UpdateResourceScene(req models.ResourceSceneReq) error { | |||
| if err := models.UpdateResourceScene(req); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| func DeleteResourceScene(id int64) error { | |||
| if err := models.DeleteResourceScene(id); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| func GetResourceSceneList(opts models.SearchResourceSceneOptions) (*models.ResourceSceneListRes, error) { | |||
| n, r, err := models.SearchResourceScene(opts) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return models.NewResourceSceneListRes(n, r), nil | |||
| } | |||
| @@ -0,0 +1,186 @@ | |||
| package resource | |||
| import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/grampus" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/routers/response" | |||
| "code.gitea.io/gitea/services/admin/operate_log" | |||
| "fmt" | |||
| "strings" | |||
| ) | |||
| func AddResourceSpecification(doerId int64, req models.ResourceSpecificationReq) error { | |||
| if req.Status == 0 { | |||
| req.Status = models.SpecNotVerified | |||
| } | |||
| spec := req.ToDTO() | |||
| if _, err := models.InsertResourceSpecification(spec); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| func UpdateSpecUnitPrice(doerId int64, specId int64, unitPrice int) *response.BizError { | |||
| oldSpec, err := models.GetResourceSpecification(&models.ResourceSpecification{ID: specId}) | |||
| if err != nil { | |||
| return response.NewBizError(err) | |||
| } | |||
| if oldSpec == nil { | |||
| return response.SPECIFICATION_NOT_EXIST | |||
| } | |||
| err = models.UpdateSpecUnitPriceById(specId, unitPrice) | |||
| if err != nil { | |||
| return response.NewBizError(err) | |||
| } | |||
| if oldSpec.UnitPrice != unitPrice { | |||
| AddSpecOperateLog(doerId, "edit", operate_log.NewLogValues().Add("unitPrice", unitPrice), operate_log.NewLogValues().Add("unitPrice", oldSpec.UnitPrice), specId, fmt.Sprintf("修改资源规格单价从%d积分到%d积分", oldSpec.UnitPrice, unitPrice)) | |||
| } | |||
| return nil | |||
| } | |||
| func SyncGrampusSpecs(doerId int64) error { | |||
| r, err := grampus.GetResourceSpecs("") | |||
| if err != nil { | |||
| return err | |||
| } | |||
| log.Info("SyncGrampusSpecs result = %+v", r) | |||
| specUpdateList := make([]models.ResourceSpecification, 0) | |||
| specInsertList := make([]models.ResourceSpecification, 0) | |||
| existIds := make([]int64, 0) | |||
| for _, spec := range r.Infos { | |||
| for _, c := range spec.Centers { | |||
| computeResource := models.ParseComputeResourceFormGrampus(spec.SpecInfo.AccDeviceKind) | |||
| if computeResource == "" { | |||
| continue | |||
| } | |||
| accCardType := strings.ToUpper(spec.SpecInfo.AccDeviceModel) | |||
| memGiB, err := models.ParseMemSizeFromGrampus(spec.SpecInfo.MemorySize) | |||
| gpuMemGiB, err := models.ParseMemSizeFromGrampus(spec.SpecInfo.AccDeviceMemory) | |||
| if err != nil { | |||
| log.Error("ParseMemSizeFromGrampus error. MemorySize=%s AccDeviceMemory=%s", spec.SpecInfo.MemorySize, spec.SpecInfo.AccDeviceMemory) | |||
| } | |||
| // get resource queue.if queue not exist,skip it | |||
| r, err := models.GetResourceQueue(&models.ResourceQueue{ | |||
| Cluster: models.C2NetCluster, | |||
| AiCenterCode: c.ID, | |||
| ComputeResource: computeResource, | |||
| AccCardType: accCardType, | |||
| }) | |||
| if err != nil || r == nil { | |||
| continue | |||
| } | |||
| //Determine if this specification already exists.if exist,update params | |||
| //if not exist,insert a new record and status is SpecNotVerified | |||
| oldSpec, err := models.GetResourceSpecification(&models.ResourceSpecification{ | |||
| QueueId: r.ID, | |||
| SourceSpecId: spec.ID, | |||
| }) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if oldSpec == nil { | |||
| specInsertList = append(specInsertList, models.ResourceSpecification{ | |||
| QueueId: r.ID, | |||
| SourceSpecId: spec.ID, | |||
| AccCardsNum: spec.SpecInfo.AccDeviceNum, | |||
| CpuCores: spec.SpecInfo.CpuCoreNum, | |||
| MemGiB: memGiB, | |||
| GPUMemGiB: gpuMemGiB, | |||
| Status: models.SpecNotVerified, | |||
| IsAutomaticSync: true, | |||
| CreatedBy: doerId, | |||
| UpdatedBy: doerId, | |||
| }) | |||
| } else { | |||
| existIds = append(existIds, oldSpec.ID) | |||
| specUpdateList = append(specUpdateList, models.ResourceSpecification{ | |||
| ID: oldSpec.ID, | |||
| AccCardsNum: spec.SpecInfo.AccDeviceNum, | |||
| CpuCores: spec.SpecInfo.CpuCoreNum, | |||
| MemGiB: memGiB, | |||
| GPUMemGiB: gpuMemGiB, | |||
| UpdatedBy: doerId, | |||
| }) | |||
| } | |||
| } | |||
| } | |||
| return models.SyncGrampusSpecs(specUpdateList, specInsertList, existIds) | |||
| } | |||
| //GetResourceSpecificationList returns specification and queue | |||
| func GetResourceSpecificationList(opts models.SearchResourceSpecificationOptions) (*models.ResourceSpecAndQueueListRes, error) { | |||
| n, r, err := models.SearchResourceSpecification(opts) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return models.NewResourceSpecAndQueueListRes(n, r), nil | |||
| } | |||
| func GetResourceSpecificationScenes(specId int64) ([]models.ResourceSceneBriefRes, error) { | |||
| r, err := models.GetSpecScenes(specId) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return r, nil | |||
| } | |||
| func ResourceSpecOnShelf(doerId int64, id int64, unitPrice int) *response.BizError { | |||
| spec, err := models.GetResourceSpecification(&models.ResourceSpecification{ID: id}) | |||
| if err != nil { | |||
| return response.NewBizError(err) | |||
| } | |||
| if spec == nil { | |||
| return response.SPECIFICATION_NOT_EXIST | |||
| } | |||
| if q, err := models.GetResourceQueue(&models.ResourceQueue{ID: spec.QueueId}); err != nil || q == nil { | |||
| return response.RESOURCE_QUEUE_NOT_AVAILABLE | |||
| } | |||
| err = models.ResourceSpecOnShelf(id, unitPrice) | |||
| if err != nil { | |||
| return response.NewBizError(err) | |||
| } | |||
| if spec.UnitPrice != unitPrice { | |||
| AddSpecOperateLog(doerId, "on-shelf", operate_log.NewLogValues().Add("UnitPrice", unitPrice), operate_log.NewLogValues().Add("UnitPrice", spec.UnitPrice), id, fmt.Sprintf("定价上架资源规格,单价为%d", unitPrice)) | |||
| } else { | |||
| AddSpecOperateLog(doerId, "on-shelf", nil, nil, id, "上架资源规格") | |||
| } | |||
| return nil | |||
| } | |||
| func ResourceSpecOffShelf(doerId int64, id int64) *response.BizError { | |||
| _, err := models.ResourceSpecOffShelf(id) | |||
| if err != nil { | |||
| return response.NewBizError(err) | |||
| } | |||
| AddSpecOperateLog(doerId, "off-shelf", nil, nil, id, "下架资源规格") | |||
| return nil | |||
| } | |||
| func AddSpecOperateLog(doerId int64, operateType string, newValue, oldValue *models.LogValues, specId int64, comment string) { | |||
| var newString = "" | |||
| var oldString = "" | |||
| if newValue != nil { | |||
| newString = newValue.JsonString() | |||
| } | |||
| if oldValue != nil { | |||
| oldString = oldValue.JsonString() | |||
| } | |||
| operate_log.Log(models.AdminOperateLog{ | |||
| BizType: "SpecOperate", | |||
| OperateType: operateType, | |||
| OldValue: oldString, | |||
| NewValue: newString, | |||
| RelatedId: fmt.Sprint(specId), | |||
| CreatedBy: doerId, | |||
| Comment: comment, | |||
| }) | |||
| } | |||
| @@ -13,10 +13,9 @@ | |||
| <div class="alert"></div> | |||
| <div class="admin user"> | |||
| {{template "admin/navbar" .}} | |||
| <div id="images-admin"> | |||
| </div> | |||
| <div class="ui container"> | |||
| <div id="images-admin"></div> | |||
| </div> | |||
| </div> | |||
| <!-- 确认模态框 --> | |||
| <div> | |||
| @@ -22,19 +22,19 @@ | |||
| data-all-compute="{{.i18n.Tr "admin.cloudbrain.all_computing_resources"}}" | |||
| data-all-status="{{.i18n.Tr "admin.cloudbrain.all_status"}}"></div> | |||
| {{template "admin/navbar" .}} | |||
| <div class="ui container" style="width: 95%;"> | |||
| <div class="ui container"> | |||
| {{template "base/alert" .}} | |||
| <div class="ui grid"> | |||
| <div class="row" style="border: 1px solid #d4d4d5;margin-top: 15px;padding-top: 0;"> | |||
| <div class="ui grid" style="margin:0"> | |||
| <div class="row" style="border: 1px solid #d4d4d5;margin-top: 0px;padding-top: 0;"> | |||
| {{template "admin/cloudbrain/search" .}} | |||
| <div class="ui six wide column right aligned" style="margin: 1rem 0;"> | |||
| <a class="ui compact blue basic icon button" style="box-shadow: none !important; padding: 0.8em;" | |||
| href="/admin/cloudbrains/download"><i | |||
| class="ri-download-line middle aligned icon"></i>{{.i18n.Tr "admin.cloudbrain.download_report"}}</a> | |||
| </div> | |||
| <div class="ui sixteen wide column"> | |||
| <div class="ui sixteen wide column" style="overflow-x:auto;"> | |||
| <!-- 任务展示 --> | |||
| <div class="dataset list"> | |||
| <div class="dataset list" style="min-width:2100px;margin-top:15px;margin-bottom:15px;"> | |||
| <!-- 表头 --> | |||
| <div class="ui grid stackable" style="background: #f0f0f0;;"> | |||
| <div class="row"> | |||
| @@ -412,17 +412,16 @@ | |||
| </div> | |||
| {{end}} | |||
| {{end}} | |||
| <div id="app" style="margin-top: 2rem;"> | |||
| <div class="center"> | |||
| <el-pagination background @current-change="handleCurrentChange" :current-page="page" | |||
| :page-sizes="[10]" :page-size="10" layout="total, sizes, prev, pager, next, jumper" | |||
| :total="{{.Page.Paginater.Total}}"> | |||
| </el-pagination> | |||
| </div> | |||
| </div> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| <div id="app" style="margin-top: 2rem;width:100%;"> | |||
| <div class="center"> | |||
| <el-pagination background @current-change="handleCurrentChange" :current-page="page" | |||
| :page-sizes="[10]" :page-size="10" layout="total, sizes, prev, pager, next, jumper" | |||
| :total="{{.Page.Paginater.Total}}"> | |||
| </el-pagination> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -1,44 +1,64 @@ | |||
| <div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar"> | |||
| <a class="{{if .PageIsAdminDashboard}}active{{end}} item" href="{{AppSubUrl}}/admin"> | |||
| {{.i18n.Tr "admin.dashboard"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminUsers}}active{{end}} item" href="{{AppSubUrl}}/admin/users"> | |||
| {{.i18n.Tr "admin.users"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminOrganizations}}active{{end}} item" href="{{AppSubUrl}}/admin/orgs"> | |||
| {{.i18n.Tr "admin.organizations"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminRepositories}}active{{end}} item" href="{{AppSubUrl}}/admin/repos"> | |||
| {{.i18n.Tr "admin.repositories"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminDatasets}}active{{end}} item" href="{{AppSubUrl}}/admin/datasets"> | |||
| {{.i18n.Tr "admin.datasets"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminCloudBrains}}active{{end}} item" href="{{AppSubUrl}}/admin/cloudbrains"> | |||
| {{.i18n.Tr "repo.cloudbrain.task"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminImages}}active{{end}} item" href="{{AppSubUrl}}/admin/images"> | |||
| {{.i18n.Tr "explore.images"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks"> | |||
| {{.i18n.Tr "admin.hooks"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminSystemHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/system-hooks"> | |||
| {{.i18n.Tr "admin.systemhooks"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminAuthentications}}active{{end}} item" href="{{AppSubUrl}}/admin/auths"> | |||
| {{.i18n.Tr "admin.authentication"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminEmails}}active{{end}} item" href="{{AppSubUrl}}/admin/emails"> | |||
| {{.i18n.Tr "admin.emails"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminConfig}}active{{end}} item" href="{{AppSubUrl}}/admin/config"> | |||
| {{.i18n.Tr "admin.config"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminNotices}}active{{end}} item" href="{{AppSubUrl}}/admin/notices"> | |||
| {{.i18n.Tr "admin.notices"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminMonitor}}active{{end}} item" href="{{AppSubUrl}}/admin/monitor"> | |||
| {{.i18n.Tr "admin.monitor"}} | |||
| </a> | |||
| <div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar" style="margin-top:0"> | |||
| <div class="item-container"> | |||
| <a class="{{if .PageIsAdminDashboard}}active{{end}} item" href="{{AppSubUrl}}/admin"> | |||
| {{.i18n.Tr "admin.dashboard"}} | |||
| </a> | |||
| <a class="item item-first" href="javascript:void(0);"> | |||
| {{.i18n.Tr "admin.user_management"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminUsers}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/users"> | |||
| {{.i18n.Tr "admin.users"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminEmails}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/emails"> | |||
| {{.i18n.Tr "admin.emails"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminOrganizations}}active{{end}} item" href="{{AppSubUrl}}/admin/orgs"> | |||
| {{.i18n.Tr "admin.organizations"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminRepositories}}active{{end}} item" href="{{AppSubUrl}}/admin/repos"> | |||
| {{.i18n.Tr "admin.repositories"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminDatasets}}active{{end}} item" href="{{AppSubUrl}}/admin/datasets"> | |||
| {{.i18n.Tr "admin.datasets"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminCloudBrains}}active{{end}} item" href="{{AppSubUrl}}/admin/cloudbrains"> | |||
| {{.i18n.Tr "repo.cloudbrain.task"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminImages}}active{{end}} item" href="{{AppSubUrl}}/admin/images"> | |||
| {{.i18n.Tr "explore.images"}} | |||
| </a> | |||
| <a class="item item-first" href="javascript:void(0);"> | |||
| {{.i18n.Tr "admin.resource_management"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminResourcesQueue}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/resources/queue"> | |||
| {{.i18n.Tr "admin.resource_pool"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminResourcesSpecification}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/resources/specification"> | |||
| {{.i18n.Tr "admin.resource_price"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminResourcesScene}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/resources/scene"> | |||
| {{.i18n.Tr "admin.application_scenario"}} | |||
| </a> | |||
| <a class="item item-first" href="javascript:void(0);"> | |||
| {{.i18n.Tr "admin.system_configuration"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminMonitor}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/monitor"> | |||
| {{.i18n.Tr "admin.monitor"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminHooks}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/hooks"> | |||
| {{.i18n.Tr "admin.hooks"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminSystemHooks}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/system-hooks"> | |||
| {{.i18n.Tr "admin.systemhooks"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminAuthentications}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/auths"> | |||
| {{.i18n.Tr "admin.authentication"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminConfig}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/config"> | |||
| {{.i18n.Tr "admin.config"}} | |||
| </a> | |||
| <a class="{{if .PageIsAdminNotices}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/notices"> | |||
| {{.i18n.Tr "admin.notices"}} | |||
| </a> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,10 @@ | |||
| {{template "base/head" .}} | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-resources-queue.css?v={{MD5 AppVer}}" /> | |||
| <div class="admin resource"> | |||
| {{template "admin/navbar" .}} | |||
| <div class="ui container"> | |||
| <div id="__vue-root"></div> | |||
| </duv> | |||
| </div> | |||
| <script src="{{StaticUrlPrefix}}/js/vp-resources-queue.js?v={{MD5 AppVer}}"></script> | |||
| {{template "base/footer" .}} | |||
| @@ -0,0 +1,10 @@ | |||
| {{template "base/head" .}} | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-resources-scene.css?v={{MD5 AppVer}}" /> | |||
| <div class="admin resource"> | |||
| {{template "admin/navbar" .}} | |||
| <div class="ui container"> | |||
| <div id="__vue-root"></div> | |||
| </duv> | |||
| </div> | |||
| <script src="{{StaticUrlPrefix}}/js/vp-resources-scene.js?v={{MD5 AppVer}}"></script> | |||
| {{template "base/footer" .}} | |||
| @@ -0,0 +1,10 @@ | |||
| {{template "base/head" .}} | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-resources-specification.css?v={{MD5 AppVer}}" /> | |||
| <div class="admin resource"> | |||
| {{template "admin/navbar" .}} | |||
| <div class="ui container"> | |||
| <div id="__vue-root"></div> | |||
| </duv> | |||
| </div> | |||
| <script src="{{StaticUrlPrefix}}/js/vp-resources-specification.js?v={{MD5 AppVer}}"></script> | |||
| {{template "base/footer" .}} | |||
| @@ -18,7 +18,7 @@ | |||
| ></i> | |||
| <span id="gpu-nums" style="font-size: 12px" | |||
| >{{.ctx.i18n.Tr "repo.wait_count_start"}} | |||
| {{if .type}} | |||
| {{if .ctx.QueuesDetail}} | |||
| {{ $gpuQueue }} | |||
| {{else}} | |||
| {{.ctx.WaitCount}} | |||
| @@ -256,8 +256,9 @@ | |||
| <div class="ui pointing secondary menu" style="border-bottom: 1px solid rgba(34,36,38,.15);"> | |||
| <a class="active item" | |||
| data-tab="first{{$k}}">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a> | |||
| <a class="item" data-tab="second{{$k}}" | |||
| onclick="loadLog({{.VersionName}})">{{$.i18n.Tr "repo.modelarts.log"}}</a> | |||
| <a class="item log_bottom" data-tab="second{{$k}}" | |||
| data-version="{{.VersionName}}">{{$.i18n.Tr "repo.modelarts.log"}}</a> | |||
| </div> | |||
| <div class="ui tab active" data-tab="first{{$k}}"> | |||
| <div style="padding-top: 10px;"> | |||
| @@ -528,19 +529,42 @@ | |||
| </div> | |||
| <div class="ui tab" data-tab="second{{$k}}"> | |||
| <div> | |||
| <div class="ui message message{{.VersionName}}" style="display: none;"> | |||
| <div id="header"></div> | |||
| </div> | |||
| <div class="ui attached log" id="log{{.VersionName}}" | |||
| style="height: 300px !important; overflow: auto;"> | |||
| <input type="hidden" name="end_line" value> | |||
| <input type="hidden" name="start_line" value> | |||
| <pre id="log_file{{.VersionName}}"></pre> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <a id="{{.VersionName}}-log-down" | |||
| class='{{if $.canDownload}}ti-download-file{{else}}disabled{{end}}' | |||
| href="/api/v1/repos/{{$.RepoRelPath}}/cloudbrain/{{.ID}}/download_log_file"> | |||
| <i class="ri-download-cloud-2-line"></i> | |||
| <span style="margin-left: 0.3rem;">{{$.i18n.Tr "repo.modelarts.download_log"}}</span> | |||
| </a> | |||
| </div> | |||
| <div | |||
| style="position: relative;border: 1px solid rgba(0,0,0,.2);padding: 0 10px;margin-top: 10px;"> | |||
| <span> | |||
| <a title="滚动到顶部" style="position: absolute; right: -32px;cursor: pointer;" | |||
| class="log_top" data-version="{{.VersionName}}"><i class="icon-to-top"></i></a> | |||
| </span> | |||
| <span class="log-info-{{.VersionName}}"> | |||
| <a title="滚动到底部" style="position: absolute; bottom: 10px;right: -32px;cursor: pointer;" | |||
| class="log_bottom" data-version="{{.VersionName}}"><i | |||
| class="icon-to-bottom"></i></a> | |||
| </span> | |||
| <div class="ui message message{{.VersionName}}" style="display: none;"> | |||
| <div id="header"></div> | |||
| </div> | |||
| <div class="ui attached log log-scroll" id="log{{.VersionName}}" data-version="{{.VersionName}}" | |||
| style="height: 300px !important; overflow: auto;"> | |||
| <div class="ui inverted active dimmer"> | |||
| <div class="ui loader"></div> | |||
| </div> | |||
| <input type="hidden" name="end_line" value> | |||
| <input type="hidden" name="start_line" value> | |||
| <pre id="log_file{{.VersionName}}"></pre> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -228,7 +228,7 @@ | |||
| </h4> | |||
| {{with .task}} | |||
| <div class="ui accordion border-according" id="accordion{{.VersionName}}" | |||
| data-repopath="{{$.RepoRelPath}}/cloudbrain/inference-job" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
| data-repopath="{{$.RepoRelPath}}/cloudbrain" data-jobid="{{.ID}}" data-version="{{.VersionName}}"> | |||
| <input type="hidden" id="jobId_input" name="jobId_input" value="{{.JobID}}"> | |||
| <div class="active title padding0"> | |||
| <div class="according-panel-heading"> | |||
| @@ -264,7 +264,8 @@ | |||
| data-tab="first">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a> | |||
| <a class="item" data-tab="second" | |||
| onclick="javascript:parseInfo()">{{$.i18n.Tr "repo.cloudbrain.runinfo"}}</a> | |||
| <a class="item log_bottom" data-tab="third" | |||
| data-version="{{.VersionName}}">{{$.i18n.Tr "repo.modelarts.log"}}</a> | |||
| <a class="item load-model-file" data-tab="four" | |||
| data-gpu-flag="true" data-download-flag="{{$.canDownload}}" data-path="{{$.RepoLink}}/cloudbrain/inference-job/{{.JobID}}/result_list" data-version="{{.VersionName}}" data-parents="" data-filename="" data-init="init" >{{$.i18n.Tr "repo.model_download"}}</a> | |||
| </div> | |||
| @@ -500,7 +501,13 @@ | |||
| <tbody> | |||
| {{range $m ,$n := $.datasetDownload}} | |||
| <tr> | |||
| <td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td> | |||
| <td style="word-wrap: break-word;word-break: break-all;"> | |||
| {{if eq .IsDelete true}} | |||
| {{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}}) | |||
| {{else}} | |||
| <a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a> | |||
| {{end}} | |||
| </td> | |||
| </tr> | |||
| {{end}} | |||
| @@ -518,7 +525,7 @@ | |||
| <div class="ui message message{{.VersionName}}" style="display: none;"> | |||
| <div id="header"></div> | |||
| </div> | |||
| <div class="ui attached log" id="log{{.VersionName}}" | |||
| <div class="ui attached" | |||
| style="height: 390px !important; overflow: auto;"> | |||
| <input type="hidden" id="json_value" value="{{$.result.JobStatus.AppExitDiagnostics}}"> | |||
| <input type="hidden" id="ExitDiagnostics" value="{{$.ExitDiagnostics}}"> | |||
| @@ -531,7 +538,44 @@ | |||
| </div> | |||
| <div class="ui tab" data-tab="third"> | |||
| <div> | |||
| <a id="{{.VersionName}}-log-down" | |||
| class='{{if $.canDownload}}ti-download-file{{else}}disabled{{end}}' | |||
| href="/api/v1/repos/{{$.RepoRelPath}}/cloudbrain/{{.ID}}/download_log_file"> | |||
| <i class="ri-download-cloud-2-line"></i> | |||
| <span style="margin-left: 0.3rem;">{{$.i18n.Tr "repo.modelarts.download_log"}}</span> | |||
| </a> | |||
| </div> | |||
| <div | |||
| style="position: relative;border: 1px solid rgba(0,0,0,.2);padding: 0 10px;margin-top: 10px;"> | |||
| <span> | |||
| <a title="滚动到顶部" style="position: absolute; right: -32px;cursor: pointer;" | |||
| class="log_top" data-version="{{.VersionName}}"><i class="icon-to-top"></i></a> | |||
| </span> | |||
| <span class="log-info-{{.VersionName}}"> | |||
| <a title="滚动到底部" style="position: absolute; bottom: 10px;right: -32px;cursor: pointer;" | |||
| class="log_bottom" data-version="{{.VersionName}}"><i | |||
| class="icon-to-bottom"></i></a> | |||
| </span> | |||
| <div class="ui message message{{.VersionName}}" style="display: none;"> | |||
| <div id="header"></div> | |||
| </div> | |||
| <div class="ui attached log log-scroll" id="log{{.VersionName}}" data-version="{{.VersionName}}" | |||
| style="height: 300px !important; overflow: auto;"> | |||
| <div class="ui inverted active dimmer"> | |||
| <div class="ui loader"></div> | |||
| </div> | |||
| <input type="hidden" name="end_line" value> | |||
| <input type="hidden" name="start_line" value> | |||
| <pre id="log_file{{.VersionName}}"></pre> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="ui tab" data-tab="four"> | |||
| <input type="hidden" name="model{{.VersionName}}" value="-1"> | |||
| @@ -498,7 +498,13 @@ | |||
| <tbody> | |||
| {{range $m ,$n := $.datasetDownload}} | |||
| <tr> | |||
| <td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td> | |||
| <td style="word-wrap: break-word;word-break: break-all;"> | |||
| {{if eq .IsDelete true}} | |||
| {{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}}) | |||
| {{else}} | |||
| <a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a> | |||
| {{end}} | |||
| </td> | |||
| </tr> | |||
| {{end}} | |||
| @@ -464,7 +464,13 @@ | |||
| <tbody> | |||
| {{range $m ,$n := $.datasetDownload}} | |||
| <tr> | |||
| <td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td> | |||
| <td style="word-wrap: break-word;word-break: break-all;"> | |||
| {{if eq .IsDelete true}} | |||
| {{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}}) | |||
| {{else}} | |||
| <a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a> | |||
| {{end}} | |||
| </td> | |||
| </tr> | |||
| {{end}} | |||
| @@ -594,7 +600,7 @@ | |||
| <input type="hidden" name="trainTaskCreate" value="true"> | |||
| <div class="required inline field"> | |||
| <label>{{.i18n.Tr "repo.model.manage.createtrainjob"}}</label> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job"}}</label> | |||
| <input type="hidden" class="width83" id="JobId" name="JobId" readonly required> | |||
| <input type="hidden" id="VersionName" name="VersionName" value="V0001"> | |||
| <input style="width: 45%;" id="JobName" readonly required> | |||
| @@ -417,7 +417,7 @@ | |||
| <div class="bgtask-content"> | |||
| <div class="bgtask-content-txt">{{.i18n.Tr "dataset.dataset_explain"}}</div> | |||
| <div class="bgtask-content-txt">{{.i18n.Tr "dataset.dataset_instructions_for_use"}}<a | |||
| href="https://git.openi.org.cn/zeizei/OpenI_Learning">{{.i18n.Tr "dataset.dataset_camp_course"}}</a></div> | |||
| href="https://git.openi.org.cn/zeizei/OpenI_Learning"> {{.i18n.Tr "dataset.dataset_camp_course"}}</a></div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -419,7 +419,12 @@ | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{range $m ,$n := $.datasetDownload}} | |||
| <a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a> | |||
| {{if eq .IsDelete true}} | |||
| {{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}}) | |||
| {{else}} | |||
| <a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a> | |||
| {{end}} | |||
| {{end}} | |||
| </div> | |||
| </td> | |||
| @@ -551,7 +556,7 @@ | |||
| <input type="hidden" name="trainTaskCreate" value="true"> | |||
| <div class="required inline field"> | |||
| <label>{{.i18n.Tr "repo.model.manage.createtrainjob"}}</label> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job"}}</label> | |||
| <input type="hidden" class="width83" id="JobId" name="JobId" readonly required> | |||
| <input type="hidden" id="VersionName" name="VersionName" value="V0001"> | |||
| <input style="width: 45%;" id="JobName" readonly required> | |||
| @@ -441,7 +441,14 @@ td, th { | |||
| <tbody> | |||
| {{range $m ,$n := $.datasetDownload}} | |||
| <tr> | |||
| <td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td> | |||
| <td style="word-wrap: break-word;word-break: break-all;"> | |||
| {{if eq .IsDelete true}} | |||
| {{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}}) | |||
| {{else}} | |||
| <a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a> | |||
| {{end}} | |||
| </td> | |||
| </tr> | |||
| {{end}} | |||
| @@ -350,7 +350,7 @@ | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w" id="{{.VersionName}}-mirror"> | |||
| <span class="ui poping up clipboard" data-position="top center" id="clipboard-btn" style="cursor:pointer" | |||
| <span class="ui poping up clipboard" data-position="top center" id="clipboard-btn-image" style="cursor:pointer" | |||
| data-clipboard-text="{{.Image}}" | |||
| data-success="{{$.i18n.Tr "repo.copy_link_success"}}" | |||
| data-error="{{$.i18n.Tr "repo.copy_link_error"}}" | |||
| @@ -439,9 +439,16 @@ | |||
| <tbody> | |||
| {{range $.datasetDownload}} | |||
| <tr> | |||
| <td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td> | |||
| <td style="word-wrap: break-word;word-break: break-all;"> | |||
| {{if eq .IsDelete true}} | |||
| {{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}}) | |||
| {{else}} | |||
| <a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a> | |||
| {{end}} | |||
| </td> | |||
| <td style="word-wrap: break-word;word-break: break-all;">{{.DatasetDownloadLink}}</td> | |||
| <td class="center aligned"><a class="ui poping up clipboard" id="clipboard-btn" data-original="{{$.i18n.Tr "repo.copy_link"}}" data-success="{{$.i18n.Tr "repo.copy_link_success"}}" data-error="{{$.i18n.Tr "repo.copy_link_error"}}" data-content="{{$.i18n.Tr "repo.copy_link"}}" data-variation="inverted tiny" data-clipboard-text="{{.DatasetDownloadLink}}">{{$.i18n.Tr "dataset.download_copy"}}</a></td> | |||
| <td class="center aligned"><a class="ui poping up clipboard" id="clipboard-btn-dataset" data-original="{{$.i18n.Tr "repo.copy_link"}}" data-success="{{$.i18n.Tr "repo.copy_link_success"}}" data-error="{{$.i18n.Tr "repo.copy_link_error"}}" data-content="{{$.i18n.Tr "repo.copy_link"}}" data-variation="inverted tiny" data-clipboard-text="{{.DatasetDownloadLink}}">{{$.i18n.Tr "dataset.download_copy"}}</a></td> | |||
| </tr> | |||
| {{end}} | |||
| </tbody> | |||
| @@ -491,5 +498,4 @@ | |||
| $(document).ready(function () { | |||
| $('.secondary.menu .item').tab(); | |||
| }); | |||
| console.log({{$.datasetDownload}}) | |||
| </script> | |||
| @@ -287,8 +287,24 @@ | |||
| id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255" value="1" | |||
| readonly> | |||
| <div class="field" id="trainjob_work_server_num_select" name="work_server_number_select"> | |||
| <select class="ui dropdown width" style='width: 100%;' name="work_server_id"> | |||
| <select class="ui dropdown width" style='width: 100%;' name="work_server_id"> | |||
| {{if .WorkNode}} | |||
| {{range .WorkNode}} | |||
| {{if $.work_server_number}} | |||
| {{if eq . $.work_server_number }} | |||
| <option name="server_id" selected value="{{.}}">{{.}}</option> | |||
| {{else}} | |||
| <option name="server_id" value="{{.}}">{{.}}</option> | |||
| {{end}} | |||
| {{else}} | |||
| <option name="server_id" value="{{.}}">{{.}}</option> | |||
| {{end}} | |||
| {{end}} | |||
| {{else}} | |||
| <option name="server_id" value="1">1</option> | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| @@ -489,7 +489,13 @@ | |||
| {{if eq $k $m}} | |||
| {{range $f ,$g := $n}} | |||
| <tr> | |||
| <td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td> | |||
| <td style="word-wrap: break-word;word-break: break-all;"> | |||
| {{if eq .IsDelete true}} | |||
| {{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}}) | |||
| {{else}} | |||
| <a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a> | |||
| {{end}} | |||
| </td> | |||
| </tr> | |||
| {{end}} | |||
| {{end}} | |||
| @@ -604,7 +610,7 @@ | |||
| <div class="two inline fields "> | |||
| <div class="required ten wide field"> | |||
| <label style="margin-left: -23px;">{{.i18n.Tr "repo.model.manage.createtrainjob"}}</label> | |||
| <label style="margin-left: -23px;">{{.i18n.Tr "repo.modelarts.train_job"}}</label> | |||
| <input type="hidden" class="width83" id="JobId" name="JobId" readonly required> | |||
| <input class="width83" id="JobName" readonly required> | |||
| @@ -73,12 +73,50 @@ | |||
| <input type="hidden" id="ai_engine_name" name="engine_names" value=""> | |||
| <input type="hidden" id="ai_flaver_name" name="flaver_names" value=""> | |||
| <input type="hidden" id="display_job_name" name="display_job_name" value="{{.display_job_name}}"> | |||
| {{template "custom/wait_count_train" Dict "ctx" $}} | |||
| <div style="display: flex;align-items: center;margin-left: 155px;margin-top: 0.5rem;"> | |||
| <h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4> | |||
| <div class="required min_title inline field"> | |||
| <label class="" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_cluster"}}</label> | |||
| <div class="ui blue mini menu compact selectcloudbrain"> | |||
| <a class="active item" href="javascript:void 0;" style="cursor:not-allowed;"> | |||
| <svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||
| {{.i18n.Tr "cloudbrain.resource_cluster_openi"}} | |||
| </a> | |||
| <a class="item" href="javascript:void 0;" style="cursor:not-allowed;background:rgba(0,0,0,.03);"> | |||
| <svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||
| {{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta) | |||
| </a> | |||
| </div> | |||
| </div> | |||
| <div class="required inline min_title field"> | |||
| <label class="" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label> | |||
| <div class="ui blue mini menu compact selectcloudbrain"> | |||
| <a class="item" href="javascript:void 0;" style="cursor:not-allowed;background:rgba(0,0,0,.03);"> | |||
| <svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" | |||
| height="16"> | |||
| <path fill="none" d="M0 0h24v24H0z" /> | |||
| <path | |||
| d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z" /> | |||
| </svg> | |||
| CPU/GPU | |||
| </a> | |||
| <a class="active item" href="javascript:void 0;" style="cursor:not-allowed;"> | |||
| <svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" | |||
| height="16"> | |||
| <path fill="none" d="M0 0h24v24H0z" /> | |||
| <path | |||
| d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z" /> | |||
| </svg> | |||
| Ascend NPU</a> | |||
| </div> | |||
| </div> | |||
| <div style="margin-top:-5px;"> | |||
| {{template "custom/wait_count_train" Dict "ctx" $}} | |||
| </div> | |||
| <div style="display: flex;align-items: center;margin-left: 155px;margin-top: 0.5rem;margin-bottom: 1.5rem;"> | |||
| <i class="ri-error-warning-line" style="color: #f2711c;margin-right: 0.5rem;"></i> | |||
| <span style="color: #888;font-size: 12px;">{{.i18n.Tr "cloudbrain.train_dataset_path_rule" | Safe}}</span> | |||
| </div> | |||
| <h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4> | |||
| <div class="required unite min_title inline field"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label> | |||
| <input type="hidden" style="width: 60%;" name="job_name" id="job_name" value="{{.job_name}}"> | |||
| @@ -50,9 +50,9 @@ | |||
| <div class="bgtask-content-header">{{$.i18n.Tr "repo.modelconvert.notcreate"}}</div> | |||
| <div class="bgtask-content"> | |||
| {{if eq .MODEL_COUNT 0}} | |||
| <div class="bgtask-content-txt">{{$.i18n.Tr "repo.modelconvert.importfirst1"}}<a href="{{.RepoLink}}/modelmanage/show_model">{{$.i18n.Tr "repo.modelconvert.importfirst2"}}</a>{{$.i18n.Tr "repo.modelconvert.importfirst3"}}</div> | |||
| <div class="bgtask-content-txt">{{$.i18n.Tr "repo.modelconvert.importfirst1"}}<a href="{{.RepoLink}}/modelmanage/show_model"> {{$.i18n.Tr "repo.modelconvert.importfirst2"}} </a>{{$.i18n.Tr "repo.modelconvert.importfirst3"}}</div> | |||
| {{end}} | |||
| <div class="bgtask-content-txt">{{$.i18n.Tr "repo.platform_instructions"}}</div> | |||
| <div class="bgtask-content-txt">{{$.i18n.Tr "repo.platform_instructions1"}}<a href="https://git.openi.org.cn/zeizei/OpenI_Learning"> {{$.i18n.Tr "repo.platform_instructions2"}} </a>{{$.i18n.Tr "repo.platform_instructions3"}}</div> | |||
| </div> | |||
| </div> | |||
| @@ -71,9 +71,9 @@ | |||
| {{end}} | |||
| {{if eq $.TRAIN_COUNT 0}} | |||
| <div class="bgtask-content-txt">{{$.i18n.Tr "repo.model.manage.createtrainjob_tip"}}<a | |||
| href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.model.manage.createtrainjob"}}</a>。</div> | |||
| href="{{.RepoLink}}/modelarts/train-job"> {{$.i18n.Tr "repo.model.manage.createtrainjob"}}</a></div> | |||
| {{end}} | |||
| <div class="bgtask-content-txt">{{$.i18n.Tr "repo.platform_instructions"}}</a></div> | |||
| <div class="bgtask-content-txt">{{$.i18n.Tr "repo.platform_instructions1"}}<a href="https://git.openi.org.cn/zeizei/OpenI_Learning"> {{$.i18n.Tr "repo.platform_instructions2"}} </a>{{$.i18n.Tr "repo.platform_instructions3"}}</div> | |||
| </div> | |||
| <div style="display: none;"> | |||
| @@ -330,7 +330,7 @@ | |||
| .modal({ | |||
| centered: false, | |||
| onShow: function () { | |||
| $('#model_header').text("导入新模型") | |||
| $('#model_header').text({{.i18n.Tr "repo.model.manage.import_new_model"}}) | |||
| $('input[name="Version"]').addClass('model_disabled') | |||
| $('.ui.dimmer').css({ "background-color": "rgb(136, 136, 136,0.7)" }) | |||
| $("#job-name").empty() | |||
| @@ -125,7 +125,7 @@ | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.createtrainjob"}}</td> | |||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job"}}</td> | |||
| <td class="ti-text-form-content word-elipsis"> | |||
| <a id="DisplayJobNameHref" class="title" style="font-size: 14px;" target="_blank"> | |||
| <span id="DisplayJobName" class="fitted" style="width: 90%;vertical-align: middle;"></span> | |||
| @@ -197,11 +197,12 @@ export default { | |||
| this.getModelList() | |||
| }, | |||
| showcreateVue(name,version,label){ | |||
| let title= this.i18n.model_create_version_title; | |||
| $('.ui.modal.second') | |||
| .modal({ | |||
| centered: false, | |||
| onShow:function(){ | |||
| $('#model_header').text("创建模型新版本") | |||
| $('#model_header').text(title) | |||
| $('input[name="Name"]').addClass('model_disabled') | |||
| $('input[name="Name"]').attr('readonly','readonly') | |||
| $('input[name="modelSelectedFile"]').attr('readonly','readonly') | |||
| @@ -248,7 +248,7 @@ | |||
| > | |||
| <el-tabs v-model="activeName"> | |||
| <el-tab-pane :label="i18n.public_dataset" name="first"> | |||
| <el-row v-loading="loadingPublicPage"> | |||
| <el-row v-loading="loadingPublicPage" style="min-height: 50px"> | |||
| <el-checkbox-group | |||
| v-model="checkList" | |||
| style="font-size: 14px; line-height: 1" | |||
| @@ -282,6 +282,7 @@ | |||
| :href="`/${item.Repo.OwnerName}/${item.Repo.Name}/datasets`" | |||
| :title="`${item.Repo.OwnerName}/${item.Repo.Alias}`" | |||
| style="font-size: 12px" | |||
| target="_blank" | |||
| >{{ item.Repo.OwnerName }}/{{ item.Repo.Alias }}</a | |||
| > | |||
| </div> | |||
| @@ -338,19 +339,18 @@ | |||
| overflow-y: auto; | |||
| " | |||
| > | |||
| <el-checkbox-group v-model="checkList"> | |||
| <el-checkbox | |||
| v-for="(item, index) in selectDatasetArray" | |||
| :key="index" | |||
| :label="item.ID" | |||
| :title="item.Title" | |||
| @change="(checked) => changeCheckSelected(checked, item)" | |||
| style="display: flex; margin: 0.5rem 0" | |||
| ><span class="select-data-right">{{ | |||
| item.Title | |||
| }}</span></el-checkbox | |||
| > | |||
| </el-checkbox-group> | |||
| <el-checkbox | |||
| v-for="(item, index) in selectDatasetArray" | |||
| :key="index" | |||
| :label="item.ID" | |||
| :title="item.Title" | |||
| :value="item.isChecked" | |||
| @change="(checked) => changeCheckSelected(checked, item)" | |||
| style="display: flex; margin: 0.5rem 0" | |||
| ><span class="select-data-right">{{ | |||
| item.Title | |||
| }}</span></el-checkbox | |||
| > | |||
| </div> | |||
| <div style="text-align: end"> | |||
| <el-button | |||
| @@ -405,7 +405,11 @@ export default { | |||
| methods: { | |||
| openDataset() { | |||
| this.checkList = this.datasetList.map((item) => { | |||
| this.selectDatasetArray.push({ ID: item.ID, Title: item.Title }); | |||
| this.selectDatasetArray.push({ | |||
| ID: item.ID, | |||
| Title: item.Title, | |||
| isChecked: true, | |||
| }); | |||
| return item.ID; | |||
| }); | |||
| this.dialogVisible = true; | |||
| @@ -472,7 +476,11 @@ export default { | |||
| return; | |||
| } | |||
| if (checked) { | |||
| this.selectDatasetArray.push({ ID: item.ID, Title: item.Title }); | |||
| this.selectDatasetArray.push({ | |||
| ID: item.ID, | |||
| Title: item.Title, | |||
| isChecked: true, | |||
| }); | |||
| } else { | |||
| let index = this.selectDatasetArray.findIndex((element) => { | |||
| return element.ID === item.ID; | |||
| @@ -485,6 +493,7 @@ export default { | |||
| return element.ID === item.ID; | |||
| }); | |||
| this.selectDatasetArray.splice(index, 1); | |||
| this.checkList.splice(index, 1); | |||
| }, | |||
| postStar(item, isSigned) { | |||
| if (!isSigned) { | |||
| @@ -11,9 +11,9 @@ | |||
| v-if="benchmarkNew" | |||
| class="label-fix-width" | |||
| style="font-weight: normal" | |||
| >{{i18n.dataset_label}}</label | |||
| >{{ i18n.dataset_label }}</label | |||
| > | |||
| <label v-else>{{i18n.dataset_label}}</label> | |||
| <label v-else>{{ i18n.dataset_label }}</label> | |||
| <span | |||
| :class=" | |||
| benchmarkNew === true ? 'dataset-train-span' : 'dataset-debug-span' | |||
| @@ -59,7 +59,7 @@ | |||
| ? 'select-dataset-button' | |||
| : 'select-dataset-button-color' | |||
| " | |||
| >{{i18n.dataset_select}} | |||
| >{{ i18n.dataset_select }} | |||
| </el-button> | |||
| <el-dialog | |||
| :title="i18n.dataset_select" | |||
| @@ -90,7 +90,11 @@ | |||
| > | |||
| <el-tabs v-model="activeName" @tab-click="handleClick"> | |||
| <!-- 当前项目的数据集 --> | |||
| <el-tab-pane :label="i18n.dataset_current_repo" name="first" v-loading="loadingCurrent"> | |||
| <el-tab-pane | |||
| :label="i18n.dataset_current_repo" | |||
| name="first" | |||
| v-loading="loadingCurrent" | |||
| > | |||
| <el-row> | |||
| <el-tree | |||
| :data="currentDatasetList" | |||
| @@ -172,13 +176,13 @@ | |||
| class="zip-loading" | |||
| v-if="data.DecompressState === 2" | |||
| > | |||
| {{i18n.dataset_unziping}} | |||
| {{ i18n.dataset_unziping }} | |||
| </span> | |||
| <span | |||
| class="unzip-failed" | |||
| v-if="data.DecompressState === 3" | |||
| > | |||
| {{i18n.dataset_unzip_failed}} | |||
| {{ i18n.dataset_unzip_failed }} | |||
| </span> | |||
| </span> | |||
| </span> | |||
| @@ -201,7 +205,11 @@ | |||
| </div> | |||
| </el-tab-pane> | |||
| <!-- 我上传的数据集 --> | |||
| <el-tab-pane :label="i18n.dataset_my_upload" name="second" v-loading="loadingMy"> | |||
| <el-tab-pane | |||
| :label="i18n.dataset_my_upload" | |||
| name="second" | |||
| v-loading="loadingMy" | |||
| > | |||
| <el-row> | |||
| <el-tree | |||
| :data="myDatasetList" | |||
| @@ -274,13 +282,13 @@ | |||
| class="zip-loading" | |||
| v-if="data.DecompressState === 2" | |||
| > | |||
| {{i18n.dataset_unziping}} | |||
| {{ i18n.dataset_unziping }} | |||
| </span> | |||
| <span | |||
| class="unzip-failed" | |||
| v-if="data.DecompressState === 3" | |||
| > | |||
| {{i18n.dataset_unzip_failed}} | |||
| {{ i18n.dataset_unzip_failed }} | |||
| </span> | |||
| </span> | |||
| </span> | |||
| @@ -380,13 +388,13 @@ | |||
| class="zip-loading" | |||
| v-if="data.DecompressState === 2" | |||
| > | |||
| {{i18n.dataset_unziping}} | |||
| {{ i18n.dataset_unziping }} | |||
| </span> | |||
| <span | |||
| class="unzip-failed" | |||
| v-if="data.DecompressState === 3" | |||
| > | |||
| {{i18n.dataset_unzip_failed}} | |||
| {{ i18n.dataset_unzip_failed }} | |||
| </span> | |||
| </span> | |||
| </span> | |||
| @@ -486,13 +494,13 @@ | |||
| class="zip-loading" | |||
| v-if="data.DecompressState === 2" | |||
| > | |||
| {{i18n.dataset_unziping}} | |||
| {{ i18n.dataset_unziping }} | |||
| </span> | |||
| <span | |||
| class="unzip-failed" | |||
| v-if="data.DecompressState === 3" | |||
| > | |||
| {{i18n.dataset_unzip_failed}} | |||
| {{ i18n.dataset_unzip_failed }} | |||
| </span> | |||
| </span> | |||
| </span> | |||
| @@ -536,9 +544,9 @@ | |||
| line-height: 40px; | |||
| " | |||
| > | |||
| {{i18n.dataset_selected}} | |||
| {{ i18n.dataset_selected }} | |||
| </div> | |||
| <div style="flex: 1; margin-top: 1.5rem"> | |||
| <div style="flex: 1; margin: 1.5rem 0; overflow-y: auto"> | |||
| <el-checkbox-group v-model="checkList"> | |||
| <el-checkbox | |||
| v-for="(item, index) in selectDatasetArray" | |||
| @@ -558,7 +566,7 @@ | |||
| color: #fff; | |||
| border: 1px solid #389e0d; | |||
| " | |||
| >{{i18n.dataset_ok}}</el-button | |||
| >{{ i18n.dataset_ok }}</el-button | |||
| > | |||
| </div> | |||
| </el-col> | |||
| @@ -732,7 +740,6 @@ export default { | |||
| .then((res) => { | |||
| this.loadingCurrent = false; | |||
| let data = JSON.parse(res.data.data); | |||
| console.log(data); | |||
| this.currentDatasetList = this.transformeTreeData( | |||
| data, | |||
| "currentTree", | |||
| @@ -978,7 +985,10 @@ export default { | |||
| let hasSelectDatasetName = $(".cloudbrain-type") | |||
| .data("dataset-name") | |||
| .split(";"); | |||
| if (this.hasSelectDatasetList.length !== 0) { | |||
| if ( | |||
| this.hasSelectDatasetList.length !== 0 && | |||
| hasSelectDatasetName[0] !== "" | |||
| ) { | |||
| this.saveStatusList = this.hasSelectDatasetList; | |||
| this.checkList = hasSelectDatasetName; | |||
| this.hasSelectDatasetList.forEach((item, index) => { | |||
| @@ -996,7 +1006,6 @@ export default { | |||
| location.href.indexOf("train-job") !== -1 || | |||
| location.href.indexOf("inference") !== -1 | |||
| ) { | |||
| console.log("this.benchmarkNew"); | |||
| this.benchmarkNew = true; | |||
| } | |||
| if ( | |||
| @@ -1,9 +1,8 @@ | |||
| <template> | |||
| <div> | |||
| <div class="ui container" style="width: 80%;"> | |||
| <div class="ui grid"> | |||
| <div class="row" style="border: 1px solid #d4d4d5;margin-top: 15px;padding-top: 0;"> | |||
| <div class="ui container" style="width: 100% !important;padding-right: 0;"> | |||
| <div class="ui grid" style="margin: 0 !important"> | |||
| <div class="row" style="border: 1px solid #d4d4d5;margin-top:0px;padding-top: 0;"> | |||
| <div class="ui attached segment"> | |||
| <div class="ui form ignore-dirty"> | |||
| <div class="ui fluid action input"> | |||
| @@ -31,8 +30,8 @@ | |||
| <div class="ui six wide column right aligned" style="margin: 1rem 0;"> | |||
| <a class="ui blue small button" href="/admin/images/commit_image">创建云脑镜像</a> | |||
| </div> | |||
| <div class="ui sixteen wide column" style="padding: 0;"> | |||
| <el-table :data="tableDataCustom" style="width: 100%" :header-cell-style="tableHeaderStyle"> | |||
| <div class="ui sixteen wide column" style="padding: 0;overflow-x: auto;"> | |||
| <el-table :data="tableDataCustom" style="width: 100%;min-width:1700px;" :header-cell-style="tableHeaderStyle"> | |||
| <el-table-column label="镜像Tag" min-width="19%" align="left" prop="tag"> | |||
| <template slot-scope="scope"> | |||
| <div style="display: flex;align-items: center;"> | |||
| @@ -56,7 +56,7 @@ export const i18nVue = { | |||
| dataset_link_success: "关联数据集成功!", | |||
| dataset_link_failed: "关联数据集失败!", | |||
| dataset_over_nums: "关联超过?个数据集", | |||
| cancel_link_dataset: "取消?关联数据集成功!", | |||
| cancel_link_dataset: "取消关联数据集?成功!", | |||
| image_label: "镜像", | |||
| image_select_placeholder: "选择镜像或输入镜像地址", | |||
| image_select: "选择镜像", | |||
| @@ -95,6 +95,8 @@ export const i18nVue = { | |||
| model_create_new_ver: "创建新版本", | |||
| model_download: "下载", | |||
| model_delete: "删除", | |||
| model_create_title: "导入新模型", | |||
| model_create_version_title: "创建模型新版本", | |||
| }, | |||
| US: { | |||
| computer_vision: "computer vision", | |||
| @@ -142,21 +144,21 @@ export const i18nVue = { | |||
| disassociate: "Unlink", | |||
| public_dataset: "Public Dataset", | |||
| selected_data_file: "Selected DataSets", | |||
| sure: "Ok", | |||
| sure: "OK", | |||
| search_dataset: "Search dataset name/description ...", | |||
| citations: "Citations", | |||
| downloads: "Downloads", | |||
| not_link_dataset: "No datasets have been associated yet", | |||
| not_link_dataset: "No datasets have been linked yet", | |||
| no_link_dataset_tips1: | |||
| "You can display public datasets on the platform here by clicking the New Linked Dataset button.", | |||
| "You can display public datasets on the platform here by clicking the Linked Datasets button.", | |||
| dataset_instructions_for_use: | |||
| "Instructions for use: You can refer to Openi AI Collaboration Platform ", | |||
| dataset_camp_course: " Newcomer Training Camp Course", | |||
| "Instructions for use: You can refer to OpenI AI Collaboration Platform ", | |||
| dataset_camp_course: " OpenI_Learning", | |||
| dataset_link_success: "Linked dataset succeeded!", | |||
| dataset_link_failed: "Linked dataset Failed!", | |||
| dataset_over_nums: "Linked over ? datasets!", | |||
| cancel_link_dataset: "Cancel ? Linked dataset succeeded!", | |||
| cancel_link_dataset: "Cancel Linked dataset ? succeeded!", | |||
| image_label: "Image", | |||
| image_select_placeholder: "Select image or input image url", | |||
| image_select: "Select Image", | |||
| @@ -196,5 +198,7 @@ export const i18nVue = { | |||
| model_create_new_ver: "New Version", | |||
| model_download: "Download", | |||
| model_delete: "Delete", | |||
| model_create_title: "Import new model", | |||
| model_create_version_title: "Create a new version of the model", | |||
| }, | |||
| }; | |||
| @@ -1,5 +1,5 @@ | |||
| .admin { | |||
| padding-top: 15px; | |||
| padding-top: 15px !important; | |||
| .table.segment { | |||
| padding: 0; | |||
| @@ -75,4 +75,58 @@ | |||
| white-space: pre-wrap; | |||
| word-wrap: break-word; | |||
| } | |||
| display: flex; | |||
| .new-menu.navbar { | |||
| width: 230px !important; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: flex-start !important; | |||
| border-bottom: none !important; | |||
| background-color: transparent !important; | |||
| .item-container { | |||
| display: flex; | |||
| flex-direction: column; | |||
| padding-top: 8px; | |||
| padding-bottom: 8px; | |||
| margin-left: 10px !important; | |||
| margin-right: 10px !important; | |||
| border: 1px solid #d4d4d5; | |||
| border-radius: 4px; | |||
| box-shadow: 0 1px 2px 0 rgb(34 36 38 / 15%); | |||
| background-color: #fafafa !important; | |||
| .item { | |||
| align-self: flex-start !important; | |||
| width: 100%; | |||
| padding-left: 20px; | |||
| &.active { | |||
| color: #40a9ff !important; | |||
| border-right: 4px solid #40a9ff; | |||
| border-radius: 0 !important; | |||
| border-bottom: none !important; | |||
| background-color: rgb(255, 255, 255); | |||
| } | |||
| &:hover { | |||
| background-color: #ffffff !important; | |||
| } | |||
| &:active { | |||
| border-color: transparent !important; | |||
| } | |||
| &.item-next { | |||
| padding-left: 45px; | |||
| } | |||
| &.item-first { | |||
| color: rgba(0,0,0,.87); | |||
| &:hover { | |||
| background-color: transparent !important; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| >.ui.container { | |||
| flex: 1 !important; | |||
| padding-right: 10px; | |||
| } | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| import service from '../service'; | |||
| // 算力积分概要 | |||
| export const getPointAccount = () => { | |||
| return service({ | |||
| url: '/reward/point/account', | |||
| method: 'get', | |||
| params: {}, | |||
| }); | |||
| } | |||
| // 算力积分获取、消耗明细 | |||
| // operate-INCREASE 表示获取明细 DECREASE表示消耗明细, page-当前页, pageSize-每页条数 | |||
| export const getPointList = (params) => { | |||
| return service({ | |||
| url: '/reward/point/record/list', | |||
| method: 'get', | |||
| params, | |||
| }); | |||
| } | |||
| // 管理员充值、扣减用户积分 | |||
| // TargetUserId, OperateType-INCREASE,DECREASE, Amount, Remark, RewardType-POINT | |||
| export const setPointOperate = (data) => { | |||
| return service({ | |||
| url: '/operation/reward/point/account/operate', | |||
| method: 'post', | |||
| data, | |||
| params: {} | |||
| }); | |||
| } | |||
| // 算力积分页面 | |||
| export const getPoint = () => { | |||
| return service({ | |||
| url: '/reward/point', | |||
| method: 'get', | |||
| params: {}, | |||
| data: {}, | |||
| }); | |||
| } | |||
| @@ -0,0 +1,174 @@ | |||
| import service from '../service'; | |||
| // 查询智算列表 | |||
| export const getAiCenterList = () => { | |||
| return service({ | |||
| url: '/admin/resources/queue/centers', | |||
| method: 'get', | |||
| params: {}, | |||
| data: {}, | |||
| }); | |||
| } | |||
| // 查询资源队列列表 | |||
| // page 当前页数,从1开始 | |||
| // cluster 所属集群 :OpenI 启智集群,C2Net 智算集群 | |||
| // center 智算中心:OpenIOne 云脑一,OpenITwo 云脑二, chendu 成都人工智能计算中心, pclcci 鹏城云计算所 ,hefei 合肥类脑类脑智能开放平台, xuchang 中原人工智能计算中心 | |||
| // resource 计算资源: GPU NPU | |||
| // card XPU类型: T4、A100、V100、Ascend 910 | |||
| export const getResQueueList = (params) => { | |||
| return service({ | |||
| url: '/admin/resources/queue/list', | |||
| method: 'get', | |||
| params, | |||
| }); | |||
| } | |||
| // 新增资源队列 | |||
| export const addResQueue = (data) => { // Cluster,QueueCode,AiCenterCode,ComputeResource,AccCardType,CardsTotalNum,Remark | |||
| return service({ | |||
| url: '/admin/resources/queue/add', | |||
| method: 'post', | |||
| params: {}, | |||
| data, | |||
| }); | |||
| } | |||
| // 更新资源队列 | |||
| export const updateResQueue = (data) => { // CardsTotalNum,Remark | |||
| return service({ | |||
| url: `/admin/resources/queue/update/${data.ID}`, | |||
| method: 'post', | |||
| params: {}, | |||
| data, | |||
| }); | |||
| } | |||
| // 查询所有资源队列名称列表 | |||
| export const getResQueueCode = (params) => { // cluster | |||
| return service({ | |||
| url: '/admin/resources/queue/codes', | |||
| method: 'get', | |||
| params, | |||
| data: {}, | |||
| }); | |||
| } | |||
| // 同步智算网络资源池(队列) | |||
| export const syncResQueue = () => { | |||
| return service({ | |||
| url: '/admin/resources/queue/grampus/sync', | |||
| method: 'post', | |||
| params: {}, | |||
| data: {}, | |||
| }); | |||
| } | |||
| // 新增资源规格 | |||
| export const addResSpecification = (data) => { | |||
| return service({ | |||
| url: '/admin/resources/specification/add', | |||
| method: 'post', | |||
| params: {}, | |||
| data, | |||
| }); | |||
| } | |||
| // 查询资源规格所属场景 - 下架时提醒 | |||
| export const getResSpecificationScenes = (data) => { // data => { ID: 1 } | |||
| return service({ | |||
| url: `/admin/resources/specification/scenes/${data.ID}`, | |||
| method: 'get', | |||
| params: {}, | |||
| data: {} | |||
| }); | |||
| } | |||
| // 更新资源规格 | |||
| // params: action edit-编辑 on-shelf 上架 off-shelf 下架 | |||
| // data: UnitPrice | |||
| export const updateResSpecification = (data) => { // data => { ID: 1, action: 'edit|on-shelf|off-shelf', UnitPrice: 1 | undefined } | |||
| return service({ | |||
| url: `/admin/resources/specification/update/${data.ID}`, | |||
| method: 'post', | |||
| params: { action: data.action }, | |||
| data: { UnitPrice: data.action === 'edit' || data.action === 'on-shelf' ? data.UnitPrice : undefined } | |||
| }); | |||
| } | |||
| // 查询资源规格列表 | |||
| // page | |||
| // cluster 所属集群 :OpenI 启智集群,C2Net 智算集群 | |||
| // queue 所属队列id | |||
| // status 状态 : 1 待审核 2已上架 3已下架 | |||
| export const getResSpecificationList = (params) => { | |||
| return service({ | |||
| url: '/admin/resources/specification/list', | |||
| method: 'get', | |||
| params, | |||
| data: {}, | |||
| }); | |||
| } | |||
| // 同步智算网络资源池(队列) | |||
| export const syncResSpecification = () => { | |||
| return service({ | |||
| url: '/admin/resources/specification/grampus/sync', | |||
| method: 'post', | |||
| params: {}, | |||
| data: {}, | |||
| }); | |||
| } | |||
| // 新增资源应用场景 | |||
| /* | |||
| { | |||
| "SceneName":"启智集群调试任务", //应用场景名 | |||
| "JobType":"TRAIN", //任务类型 DEBUG调试任务 BENCHMARK 评测任务 TRAIN 训练 INFERENCE 推理 | |||
| "IsExclusive":true, //是否专属 | |||
| "ExclusiveOrg":"123,456", //专属组织 | |||
| "SpecIds":[2,3] // 资源规格id | |||
| } | |||
| */ | |||
| export const addResScene = (data) => { | |||
| return service({ | |||
| url: '/admin/resources/scene/add', | |||
| method: 'post', | |||
| params: {}, | |||
| data, | |||
| }); | |||
| } | |||
| // 更新资源应用场景 | |||
| // params: action:edit-编辑 delete-删除, | |||
| // data: { | |||
| // "SceneName":"启智集群调试任务", //应用场景名 | |||
| // "IsExclusive":true, //是否专属 | |||
| // "ExclusiveOrg":"123,456", //专属组织 | |||
| // "SpecIds":[2,3] // 资源规格id | |||
| //} | |||
| export const updateResScene = (data) => { | |||
| return service({ | |||
| url: `/admin/resources/scene/update/${data.ID}`, | |||
| method: 'post', | |||
| params: { action: data.action }, | |||
| data: { | |||
| ...data | |||
| }, | |||
| }); | |||
| } | |||
| // 查询资源应用场景 | |||
| // page | |||
| // jobType | |||
| // center | |||
| // queue 所属队列 | |||
| // IsExclusive 是否专属 1 专属 2 非专属 | |||
| export const getResSceneList = (params) => { | |||
| return service({ | |||
| url: '/admin/resources/scene/list', | |||
| method: 'get', | |||
| params, | |||
| data: {}, | |||
| }); | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| import axios from 'axios'; | |||
| const service = axios.create({ | |||
| baseURL: '/', | |||
| timeout: 20000, | |||
| }); | |||
| service.interceptors.request.use((config) => { | |||
| config.data && Object.assign(config.data, { | |||
| _csrf: window.config ? window.config.csrf : '', | |||
| }); | |||
| config.params && Object.assign(config.params, { | |||
| _csrf: window.config ? window.config.csrf : '', | |||
| }); | |||
| return config; | |||
| }, (error) => { | |||
| return Promise.reject(error); | |||
| }); | |||
| service.interceptors.response.use((response) => { | |||
| return response; | |||
| }, (error) => { | |||
| return Promise.reject(error); | |||
| }); | |||
| export default service; | |||
| @@ -0,0 +1,99 @@ | |||
| <template> | |||
| <div class="base-dlg"> | |||
| <el-dialog :visible.sync="dialogShow" :title="title" :width="width" :fullscreen="fullscreen" :top="top" | |||
| :modal="modal" :modal-append-to-body="modalAppendToBody" :append-to-body="appendToBody" :lock-scroll="lockScroll" | |||
| :custom-class="customClass" :close-on-click-modal="closeOnClickModal" :close-on-press-escape="closeOnPressEscape" | |||
| :show-close="showClose" :center="center" :destroy-on-close="destroyOnClose" :before-close="beforeClose" | |||
| @open="open" @opened="opened" @close="close" @closed="closed"> | |||
| <template v-slot:title> | |||
| <slot name="title"></slot> | |||
| </template> | |||
| <template v-slot:default> | |||
| <slot name="default"></slot> | |||
| </template> | |||
| <template v-slot:footer> | |||
| <slot name="footer"></slot> | |||
| </template> | |||
| </el-dialog> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: "BaseDialog", | |||
| props: { | |||
| visible: { type: Boolean, default: false }, | |||
| title: { type: String, default: "" }, | |||
| width: { type: String, default: "" }, | |||
| fullscreen: { type: Boolean, default: false }, | |||
| top: { type: String }, | |||
| modal: { type: Boolean, default: true }, | |||
| modalAppendToBody: { type: Boolean, default: true }, | |||
| appendToBody: { type: Boolean, default: false }, | |||
| lockScroll: { type: Boolean, default: false }, | |||
| customClass: { type: String, default: "" }, | |||
| closeOnClickModal: { type: Boolean, default: false }, | |||
| closeOnPressEscape: { type: Boolean, default: true }, | |||
| showClose: { type: Boolean, default: true }, | |||
| beforeClose: { type: Function }, | |||
| center: { type: Boolean, default: false }, | |||
| destroyOnClose: { type: Boolean, default: false }, | |||
| }, | |||
| data() { | |||
| return { | |||
| dialogShow: false, | |||
| }; | |||
| }, | |||
| watch: { | |||
| visible: function (val) { | |||
| this.dialogShow = val; | |||
| }, | |||
| }, | |||
| methods: { | |||
| open() { | |||
| this.$emit("open"); | |||
| }, | |||
| opened() { | |||
| this.$emit("opened"); | |||
| }, | |||
| close() { | |||
| this.$emit("close"); | |||
| }, | |||
| closed() { | |||
| this.$emit("closed"); | |||
| this.$emit("update:visible", false); | |||
| }, | |||
| }, | |||
| }; | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .base-dlg { | |||
| /deep/ .el-dialog__header { | |||
| text-align: left; | |||
| height: 45px; | |||
| background: rgb(240, 240, 240); | |||
| border-radius: 5px 5px 0px 0px; | |||
| border-bottom: 1px solid rgb(212, 212, 213); | |||
| padding: 0 15px; | |||
| display: flex; | |||
| align-items: center; | |||
| font-weight: 500; | |||
| font-size: 16px; | |||
| color: rgb(16, 16, 16); | |||
| .el-dialog__title { | |||
| font-weight: 500; | |||
| font-size: 16px; | |||
| color: rgb(16, 16, 16); | |||
| } | |||
| .el-dialog__headerbtn { | |||
| top: 15px; | |||
| right: 15px; | |||
| } | |||
| } | |||
| /deep/ .el-dialog__body { | |||
| padding: 15px 15px; | |||
| } | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,16 @@ | |||
| import { i18n } from '~/langs'; | |||
| export const SOURCE_TYPE = [{ k: 'ACCOMPLISH_TASK', v: i18n.t('accomplishTask') }, { k: 'ADMIN_OPERATE', v: i18n.t('adminOperate') }, { k: 'RUN_CLOUDBRAIN_TASK', v: i18n.t('runCloudBrainTask') }]; | |||
| export const CONSUME_STATUS = [{ k: 'OPERATING', v: i18n.t('operating') }, { k: 'SUCCEEDED', v: i18n.t('succeeded') }]; | |||
| export const POINT_ACTIONS = [ | |||
| { k: 1, v: i18n.t('createPublicProject') }, { k: 6, v: i18n.t('dailyPutforwardTasks') }, { k: 7, v: i18n.t('dailyPR') }, { k: 10, v: i18n.t('comment') }, { k: 24, v: i18n.t('uploadDatasetFile') }, { k: 30, v: i18n.t('importNewModel') }, { k: 34, v: i18n.t('completeWechatCodeScanningVerification') }, | |||
| { k: 35, v: i18n.t('dailyRunCloudbrainTasks') }, { k: 36, v: i18n.t('datasetRecommendedByThePlatform') }, { k: 37, v: i18n.t('submitNewPublicImage') }, { k: 38, v: i18n.t('imageRecommendedByThePlatform') }, { k: 39, v: i18n.t('firstChangeofAvatar') }, { k: 40, 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') }]; | |||
| // 资源管理 | |||
| export const CLUSTERS = [{ k: 'OpenI', v: i18n.t('resourcesManagement.OpenI') }, { k: 'C2Net', v: i18n.t('resourcesManagement.C2Net') }]; | |||
| export const AI_CENTER = [{ k: 'OpenIOne', v: i18n.t('resourcesManagement.OpenIOne') }, { k: 'OpenITwo', v: i18n.t('resourcesManagement.OpenITwo') }, { k: 'chendu', v: i18n.t('resourcesManagement.chenduCenter') }, { k: 'pclcci', v: i18n.t('resourcesManagement.pclcci') }, { k: 'hefei', v: i18n.t('resourcesManagement.hefeiCenter') }, { k: 'xuchang', v: i18n.t('resourcesManagement.xuchangCenter') }]; | |||
| export const COMPUTER_RESOURCES = [{ k: 'GPU', v: 'GPU' }, { k: 'NPU', v: 'NPU' }, { k: 'MLU', v: 'MLU' }]; | |||
| 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: 'RTX3080', v: 'RTX3080' }]; | |||
| 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') }]; | |||
| @@ -0,0 +1,156 @@ | |||
| const en = { | |||
| loading: 'Loading...', | |||
| noData: 'No Data', | |||
| date: 'Date', | |||
| confirm: 'Confirm', | |||
| cancel: 'Cancel', | |||
| confirm1: 'Confirm', | |||
| pleaseCompleteTheInformationFirst: 'Please Complete the Information first!', | |||
| submittedSuccessfully: 'Submitted Successfully!', | |||
| submittedFailed: 'Submitted Failed!', | |||
| operation: 'Operation', | |||
| edit: 'Edit', | |||
| delete: 'Delete', | |||
| tips: 'Tips', | |||
| accomplishTask: 'Accomplish Task', | |||
| adminOperate: 'Administrator Operation', | |||
| runCloudBrainTask: 'Run CloudBrain Task', | |||
| operating: 'Operating', | |||
| succeeded: 'Succeeded', | |||
| debugTask: 'Debug Task', | |||
| trainTask: 'Train Task', | |||
| inferenceTask: 'Inference Task', | |||
| benchmarkTask: 'Benchmark Task', | |||
| createPublicProject: 'Create Public Projects', | |||
| dailyPutforwardTasks: 'Daily Put Forward Tasks', | |||
| dailyPR: 'Daily PR', | |||
| comment: 'Comment', | |||
| uploadDatasetFile: 'Upload Dataset Files', | |||
| importNewModel: 'Import New Models', | |||
| completeWechatCodeScanningVerification: 'Complete Wechat Code Scanning Verification', | |||
| dailyRunCloudbrainTasks: 'Daily Run Cloudbrain Tasks', | |||
| datasetRecommendedByThePlatform: 'Dataset Recommended by the Platform', | |||
| submitNewPublicImage: 'Submit New Public Images', | |||
| imageRecommendedByThePlatform: 'Image Recommended by the Platform', | |||
| firstChangeofAvatar: 'First Change of Avatar', | |||
| dailyCommit: 'Daily Commit', | |||
| calcPointDetails: 'Calculation Points Details', | |||
| calcPointAcquisitionInstructions: 'Calculation Points Acquisition Instructions', | |||
| CurrAvailableCalcPoints: 'Currently Available Calculation Points', | |||
| totalGainCalcPoints: 'Total Gain of Calculation Points', | |||
| totalConsumeCalcPoints: 'Total Consume of Calculation Points', | |||
| gainDetail: 'Gain Detail', | |||
| consumeDetail: 'Consume Detail', | |||
| serialNumber: 'Serial Number', | |||
| time: 'Time', | |||
| scene: 'Scene', | |||
| behaviorOfPoint: 'Behavior Of Point', | |||
| explanation: 'Explanation', | |||
| points: 'Points', | |||
| status: 'Status', | |||
| runTime: 'Run Time', | |||
| taskName: 'Task Name', | |||
| createdRepository: 'created repository ', | |||
| openedIssue: 'opened issue ', | |||
| createdPullRequest: 'created pull request ', | |||
| commentedOnIssue: 'commented on issue ', | |||
| uploadDataset: 'upload dataset ', | |||
| createdNewModel: 'created new model ', | |||
| firstBindingWechatRewards: 'first binding wechat rewards', | |||
| created: 'created ', | |||
| type: ' type ', | |||
| dataset: 'dataset ', | |||
| setAsRecommendedDataset: ' was set as recommended dataset', | |||
| committedImage: 'committed image ', | |||
| image: 'image ', | |||
| setAsRecommendedImage: ' was set as recommended image', | |||
| updatedAvatar: 'updated avatar', | |||
| pushedBranch: 'pushed to {branch} at ', | |||
| dailyMaxTips: `can't get full points when reach the daily upper limit`, | |||
| memory: 'Memory', | |||
| sharedMemory: 'Shared Memory', | |||
| ';': ', ', | |||
| noPointGainRecord: 'No Point Earn Record Yet', | |||
| noPointConsumeRecord: 'No Point Consume Record Yet', | |||
| resourcesManagement: { | |||
| OpenI: 'OpenI', | |||
| C2Net: 'C2Net', | |||
| OpenIOne: 'OpenI One', | |||
| OpenITwo: 'OpenI Two', | |||
| chenduCenter: 'ChenDu AI Center', | |||
| pclcci: 'PCL Cloud Computer Institute', | |||
| hefeiCenter: 'HeFei AI Center', | |||
| xuchangCenter: 'XuChang AI Center', | |||
| willOnShelf: 'To Be On Shelf', | |||
| onShelf: 'On Shelf', | |||
| offShelf: 'Off Shelf', | |||
| toOnShelf: 'To On Shelf', | |||
| toOffShelf: 'To Off Shelf', | |||
| toSetPriceAndOnShelf: 'To Set Price and On Shelf', | |||
| status: 'Status', | |||
| allStatus: 'All Status', | |||
| syncAiNetwork: 'Sync AI Network', | |||
| resQueue: 'Resources Queue', | |||
| allResQueue: 'All Resources Queues', | |||
| addResQueue: 'Add Resources Queue', | |||
| addResQueueBtn: 'Add Resources Queue', | |||
| editResQueue: 'Edit Resources Queue', | |||
| resQueueName: 'Resources Queue Name', | |||
| whichCluster: 'Cluster', | |||
| allCluster: 'All Clusters', | |||
| aiCenter: 'AI Center', | |||
| aiCenterID: 'AI Center ID', | |||
| allAiCenter: 'All AI Centers', | |||
| computeResource: 'Compute Resource', | |||
| allComputeResource: 'All Compute Resources', | |||
| accCardType: 'Acc Card Type', | |||
| allAccCardType: 'All Acc Card Type', | |||
| cardsTotalNum: 'Cards Total Number', | |||
| accCardsNum: 'Acc Cards Number', | |||
| remark: 'Remark', | |||
| pleaseEnterRemark: 'Please Enter Remark(The maximum length shall not exceed 255)', | |||
| pleaseEnterPositiveIntegerCardsTotalNum: 'Please Enter Positive Integer Cards Total Number!', | |||
| addResSpecificationAndPriceInfo: 'Add Resources Specification and Price Info', | |||
| addResSpecificationBtn: 'Add Resources Specification', | |||
| editResSpecificationAndPriceInfo: 'Edit Resources Specification and Price Info', | |||
| resSpecificationAndPriceManagement: 'Resources Specification and Price Management', | |||
| sourceSpecCode: 'Source Specification Code', | |||
| sourceSpecCodeTips: 'OpenI Two Should Enter the Source Specification Code', | |||
| sourceSpecId: 'Source Specification ID', | |||
| cpuNum: 'CPU Number', | |||
| gpuMem: 'GPU Memory', | |||
| mem: 'Memory', | |||
| shareMem: 'Share Memory', | |||
| unitPrice: 'Unit Price', | |||
| point_hr: 'Point/hr', | |||
| onShelfConfirm: 'Are you sure to on shelf the resources specification?', | |||
| offShelfConfirm: 'Are you sure to off shelf the resources specification?', | |||
| onShelfCode1001: 'On shelf failed, the resources queues not available.', | |||
| offShelfDlgTip1: 'The resources specification has already used in scene:', | |||
| offShelfDlgTip2: 'Please confirm to off shelf?', | |||
| resSceneManagement: 'Resources Scene Management', | |||
| addResScene: 'Add Resources Scene', | |||
| addResSceneBtn: 'Add Resources Scene', | |||
| editResScene: 'Edit Resources Scene', | |||
| resSceneName: 'Resources Scene Name', | |||
| jobType: 'Job Type', | |||
| allJobType: 'All Job Type', | |||
| isExclusive: 'Is Exclusive?', | |||
| allExclusiveAndCommonUse: 'All Exclusive and Common Use', | |||
| exclusive: 'Exclusive', | |||
| commonUse: 'Common Use', | |||
| exclusiveOrg: 'Exclusive Organization', | |||
| exclusiveOrgTips: 'Multiple organization names are separated by semicolons', | |||
| computeCluster: 'Compute Cluster', | |||
| resourceSpecification: 'Resource Specification', | |||
| lastUpdateTime: 'Last Update Time', | |||
| resSceneDeleteConfirm: 'Are you sure to delete the current Resource Scene?', | |||
| }, | |||
| } | |||
| export default en; | |||
| @@ -0,0 +1,156 @@ | |||
| const zh = { | |||
| loading: '加载中...', | |||
| noData: '暂无数据', | |||
| date: '日期', | |||
| confirm: '确定', | |||
| cancel: '取消', | |||
| confirm1: '确认', | |||
| pleaseCompleteTheInformationFirst: '请先完善信息!', | |||
| submittedSuccessfully: '提交成功!', | |||
| submittedFailed: '提交失败!', | |||
| operation: '操作', | |||
| edit: '修改', | |||
| delete: '删除', | |||
| tips: '提示', | |||
| accomplishTask: '积分任务', | |||
| adminOperate: '管理员操作', | |||
| runCloudBrainTask: '运行云脑任务', | |||
| operating: '消耗中', | |||
| succeeded: '已完成', | |||
| debugTask: '调试任务', | |||
| trainTask: '训练任务', | |||
| inferenceTask: '推理任务', | |||
| benchmarkTask: '评测任务', | |||
| createPublicProject: '创建公开项目', | |||
| dailyPutforwardTasks: '每日提出任务', | |||
| dailyPR: '每日提出PR', | |||
| comment: '发表评论', | |||
| uploadDatasetFile: '上传数据集文件', | |||
| importNewModel: '导入新模型', | |||
| completeWechatCodeScanningVerification: '完成微信扫码验证', | |||
| dailyRunCloudbrainTasks: '每日运行云脑任务', | |||
| datasetRecommendedByThePlatform: '数据集被平台推荐', | |||
| submitNewPublicImage: '提交新公开镜像', | |||
| imageRecommendedByThePlatform: '镜像被平台推荐', | |||
| firstChangeofAvatar: '首次更换头像', | |||
| dailyCommit: '每日commit', | |||
| calcPointDetails: '算力积分明细', | |||
| calcPointAcquisitionInstructions: '积分获取说明', | |||
| CurrAvailableCalcPoints: '当前可用算力积分(分)', | |||
| totalGainCalcPoints: '总获取算力积分(分)', | |||
| totalConsumeCalcPoints: '总消耗算力积分(分)', | |||
| gainDetail: '获取明细', | |||
| consumeDetail: '消耗明细', | |||
| serialNumber: '流水号', | |||
| time: '时间', | |||
| scene: '场景', | |||
| behaviorOfPoint: '积分行为', | |||
| explanation: '说明', | |||
| points: '积分', | |||
| status: '状态', | |||
| runTime: '运行时长', | |||
| taskName: '任务名称', | |||
| createdRepository: '创建了项目', | |||
| openedIssue: '创建了任务', | |||
| createdPullRequest: '创建了合并请求', | |||
| commentedOnIssue: '评论了任务', | |||
| uploadDataset: '上传了数据集文件', | |||
| createdNewModel: '导入了新模型', | |||
| firstBindingWechatRewards: '首次绑定微信奖励', | |||
| created: '创建了', | |||
| type: '类型', | |||
| dataset: '数据集', | |||
| setAsRecommendedDataset: '被设置为推荐数据集', | |||
| committedImage: '提交了镜像', | |||
| image: '镜像', | |||
| setAsRecommendedImage: '被设置为推荐镜像', | |||
| updatedAvatar: '更新了头像', | |||
| pushedBranch: '推送了{branch}分支代码到', | |||
| dailyMaxTips: '达到每日上限积分,不能拿满分', | |||
| memory: '内存', | |||
| sharedMemory: '共享内存', | |||
| ';': ';', | |||
| noPointGainRecord: '还没有积分获取记录', | |||
| noPointConsumeRecord: '还没有积分消耗记录', | |||
| resourcesManagement: { | |||
| OpenI: '启智集群', | |||
| C2Net: '智算集群', | |||
| OpenIOne: '云脑一', | |||
| OpenITwo: '云脑二', | |||
| chenduCenter: '成都人工智能计算中心', | |||
| pclcci: '鹏城云计算所', | |||
| hefeiCenter: '合肥类脑类脑智能开放平台', | |||
| xuchangCenter: '中原人工智能计算中心', | |||
| willOnShelf: '待上架', | |||
| onShelf: '已上架', | |||
| offShelf: '已下架', | |||
| toOnShelf: '上架', | |||
| toOffShelf: '下架', | |||
| toSetPriceAndOnShelf: '定价上架', | |||
| status: '状态', | |||
| allStatus: '全部状态', | |||
| syncAiNetwork: '同步智算网络', | |||
| resQueue: '资源池(队列)', | |||
| allResQueue: '全部资源池(队列)', | |||
| addResQueue: '新建资源池(队列)', | |||
| addResQueueBtn: '新增资源池', | |||
| editResQueue: '修改资源池(队列)', | |||
| resQueueName: '资源池(队列)名称', | |||
| whichCluster: '所属集群', | |||
| allCluster: '全部集群', | |||
| aiCenter: '智算中心', | |||
| aiCenterID: '智算中心ID', | |||
| allAiCenter: '全部智算中心', | |||
| computeResource: '计算资源', | |||
| allComputeResource: '全部计算资源', | |||
| accCardType: '卡类型', | |||
| allAccCardType: '全部卡类型', | |||
| cardsTotalNum: '卡数', | |||
| accCardsNum: '卡数', | |||
| remark: '备注', | |||
| pleaseEnterRemark: '请输入备注(最大长度不超过255)', | |||
| pleaseEnterPositiveIntegerCardsTotalNum: '请输入正整数的卡数!', | |||
| addResSpecificationAndPriceInfo: '新增资源规格和单价信息', | |||
| addResSpecificationBtn: '新增资源规格', | |||
| editResSpecificationAndPriceInfo: '修改资源规格和单价信息', | |||
| resSpecificationAndPriceManagement: '资源规格单价管理', | |||
| sourceSpecCode: '对应资源编码', | |||
| sourceSpecCodeTips: '云脑II需要填写对应的资源编码', | |||
| sourceSpecId: '智算网络资源规格ID', | |||
| cpuNum: 'CPU数', | |||
| gpuMem: '显存', | |||
| mem: '内存', | |||
| shareMem: '共享内存', | |||
| unitPrice: '单价', | |||
| point_hr: '积分/时', | |||
| onShelfConfirm: '请确认上架该规格?', | |||
| offShelfConfirm: '请确认下架该规格?', | |||
| onShelfCode1001: '上架失败,资源池(队列)不可用。', | |||
| offShelfDlgTip1: '当前资源规格已在以下场景中使用:', | |||
| offShelfDlgTip2: '请确认进行下架操作?', | |||
| resSceneManagement: '算力资源应用场景管理', | |||
| addResScene: '新建算力资源应用场景', | |||
| addResSceneBtn: '新增应用场景', | |||
| editResScene: '修改算力资源应用场景', | |||
| resSceneName: '应用场景名称', | |||
| jobType: '任务类型', | |||
| allJobType: '全部任务类型', | |||
| isExclusive: '是否专属', | |||
| allExclusiveAndCommonUse: '全部专属和通用', | |||
| exclusive: '专属', | |||
| commonUse: '通用', | |||
| exclusiveOrg: '专属组织', | |||
| exclusiveOrgTips: '多个组织名之间用英文分号隔开', | |||
| computeCluster: '算力集群', | |||
| resourceSpecification: '资源规格', | |||
| lastUpdateTime: '最后更新时间', | |||
| resSceneDeleteConfirm: '是否确认删除当前应用场景?', | |||
| }, | |||
| } | |||
| export default zh; | |||
| @@ -0,0 +1,16 @@ | |||
| import Vue from 'vue'; | |||
| import VueI18n from 'vue-i18n'; | |||
| import jsCookie from 'js-cookie'; | |||
| import zh from './config/zh-CN'; | |||
| import en from './config/en-US'; | |||
| Vue.use(VueI18n); | |||
| export const lang = jsCookie.get('lang') || 'zh-CN'; | |||
| export const i18n = new VueI18n({ | |||
| locale: lang, | |||
| messages: { | |||
| 'zh-CN': zh, | |||
| 'en-US': en | |||
| }, | |||
| }); | |||
| @@ -0,0 +1,259 @@ | |||
| <template> | |||
| <div class="base-dlg"> | |||
| <BaseDialog :visible.sync="dialogShow" :width="`750px`" | |||
| :title="type === 'add' ? $t('resourcesManagement.addResQueue') : $t('resourcesManagement.editResQueue')" | |||
| @open="open" @opened="opened" @close="close" @closed="closed"> | |||
| <div class="dlg-content"> | |||
| <div class="form"> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.resQueueName') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-input v-model="dataInfo.QueueCode" placeholder="" :disabled="type === 'edit'" maxlength="255"></el-input> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.whichCluster') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-select v-model="dataInfo.Cluster" :disabled="type === 'edit'"> | |||
| <el-option v-for="item in clusterList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.aiCenter') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-select v-model="dataInfo.AiCenterCode" :disabled="type === 'edit'"> | |||
| <el-option v-for="item in computingCenterList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.computeResource') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-select v-model="dataInfo.ComputeResource" :disabled="type === 'edit'"> | |||
| <el-option v-for="item in computingTypeList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.accCardType') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-select v-model="dataInfo.AccCardType" :disabled="type === 'edit'"> | |||
| <el-option v-for="item in cardTypeList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.cardsTotalNum') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-input v-model="dataInfo.CardsTotalNum" type="number" placeholder=""></el-input> | |||
| </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"> | |||
| <el-input type="textarea" :autosize="{ minRows: 3, maxRows: 4 }" maxlength="255" | |||
| :placeholder="$t('resourcesManagement.pleaseEnterRemark')" v-model="dataInfo.Remark"> | |||
| </el-input> | |||
| </div> | |||
| </div> | |||
| <div class="form-row" style="margin-top: 20px"> | |||
| <div class="title"></div> | |||
| <div class="content"> | |||
| <el-button type="primary" class="btn confirm-btn" @click="confirm">{{ $t('confirm') }}</el-button> | |||
| <el-button class="btn" @click="cancel">{{ $t('cancel') }}</el-button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </BaseDialog> | |||
| </div> | |||
| </template> | |||
| <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'; | |||
| export default { | |||
| name: "QueueDialog", | |||
| props: { | |||
| visible: { type: Boolean, default: false }, | |||
| title: { type: String, default: '' }, | |||
| type: { type: String, defalut: 'add' }, | |||
| data: { type: Object, default: () => ({}) }, | |||
| }, | |||
| components: { | |||
| BaseDialog | |||
| }, | |||
| data() { | |||
| return { | |||
| dialogShow: false, | |||
| clusterList: [CLUSTERS[0]], | |||
| computingCenterList: [AI_CENTER[0], AI_CENTER[1]], | |||
| computingTypeList: [...COMPUTER_RESOURCES], | |||
| cardTypeList: [...ACC_CARD_TYPE], | |||
| dataInfo: {}, | |||
| }; | |||
| }, | |||
| watch: { | |||
| visible: function (val) { | |||
| this.dialogShow = val; | |||
| }, | |||
| }, | |||
| methods: { | |||
| resetDataInfo() { | |||
| this.dataInfo = { | |||
| ID: '', | |||
| QueueCode: '', | |||
| Cluster: '', | |||
| AiCenterCode: '', | |||
| ComputeResource: '', | |||
| AccCardType: '', | |||
| CardsTotalNum: '', | |||
| Remark: '', | |||
| } | |||
| }, | |||
| open() { | |||
| this.resetDataInfo(); | |||
| if (this.type === 'add') { | |||
| // | |||
| } else if (this.type === 'edit') { | |||
| this.dataInfo = Object.assign(this.dataInfo, { ...this.data }); | |||
| } | |||
| this.$emit("open"); | |||
| }, | |||
| opened() { | |||
| this.$emit("opened"); | |||
| }, | |||
| close() { | |||
| this.$emit("close"); | |||
| }, | |||
| closed() { | |||
| this.$emit("closed"); | |||
| 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) { | |||
| this.$message({ | |||
| type: 'info', | |||
| message: this.$t('pleaseCompleteTheInformationFirst'), | |||
| }); | |||
| return; | |||
| } | |||
| if (parseInt(this.dataInfo.CardsTotalNum) != Number(this.dataInfo.CardsTotalNum)) { | |||
| this.$message({ | |||
| type: 'info', | |||
| message: this.$t('pleaseEnterPositiveIntegerCardsTotalNum') | |||
| }); | |||
| return; | |||
| } | |||
| const setApi = this.type === 'add' ? addResQueue : updateResQueue; | |||
| setApi({ ...this.dataInfo, CardsTotalNum: Number(this.dataInfo.CardsTotalNum) }).then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| this.$message({ | |||
| type: 'success', | |||
| message: this.$t('submittedSuccessfully') | |||
| }); | |||
| this.$emit("confirm"); | |||
| } else { | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| }) | |||
| }, | |||
| cancel() { | |||
| this.dialogShow = false; | |||
| this.$emit("update:visible", false); | |||
| } | |||
| }, | |||
| mounted() { | |||
| this.resetDataInfo(); | |||
| }, | |||
| }; | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .dlg-content { | |||
| margin: 20px 0 25px 0; | |||
| display: flex; | |||
| justify-content: center; | |||
| .form { | |||
| width: 600px; | |||
| .form-row { | |||
| display: flex; | |||
| min-height: 42px; | |||
| margin-bottom: 4px; | |||
| .title { | |||
| width: 160px; | |||
| display: flex; | |||
| justify-content: flex-end; | |||
| align-items: center; | |||
| margin-right: 20px; | |||
| color: rgb(136, 136, 136); | |||
| font-size: 14px; | |||
| &.required { | |||
| span { | |||
| position: relative; | |||
| } | |||
| span::after { | |||
| position: absolute; | |||
| right: -10px; | |||
| top: -2px; | |||
| vertical-align: top; | |||
| content: '*'; | |||
| color: #db2828; | |||
| } | |||
| } | |||
| } | |||
| .content { | |||
| width: 300px; | |||
| display: flex; | |||
| align-items: center; | |||
| /deep/ .el-select { | |||
| width: 100%; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .btn { | |||
| color: rgb(2, 0, 4); | |||
| background-color: rgb(194, 199, 204); | |||
| border-color: rgb(194, 199, 204); | |||
| &.confirm-btn { | |||
| color: #fff; | |||
| background-color: rgb(56, 158, 13); | |||
| border-color: rgb(56, 158, 13); | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,366 @@ | |||
| <template> | |||
| <div class="base-dlg"> | |||
| <BaseDialog :visible.sync="dialogShow" :width="`750px`" | |||
| :title="type === 'add' ? $t('resourcesManagement.addResScene') : $t('resourcesManagement.editResScene')" | |||
| @open="open" @opened="opened" @close="close" @closed="closed"> | |||
| <div class="dlg-content"> | |||
| <div class="form"> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.resSceneName') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-input v-model="dataInfo.SceneName" placeholder="" maxlength="255"></el-input> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.jobType') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-select v-model="dataInfo.JobType" :disabled="type === 'edit'"> | |||
| <el-option v-for="item in taskTypeList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.isExclusive') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-select v-model="dataInfo.IsExclusive" @change="changeIsExclusive"> | |||
| <el-option v-for="item in isExclusiveList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| </div> | |||
| <div class="form-row" v-if="dataInfo.IsExclusive === '1'"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.exclusiveOrg') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-input v-model="dataInfo.ExclusiveOrg" :placeholder="$t('resourcesManagement.exclusiveOrgTips')" | |||
| maxlength="255"> | |||
| </el-input> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.computeCluster') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-select v-model="dataInfo.Cluster" @change="changeCluster" :disabled="type === 'edit'"> | |||
| <el-option v-for="item in clusterList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title"> | |||
| <span>{{ $t('resourcesManagement.resQueue') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-select v-model="dataInfo.QueueId" @change="changeQueue" :disabled="type === 'edit'"> | |||
| <el-option v-for="item in queueList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.resourceSpecification') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-select v-model="dataInfo.SpecIds" multiple collapse-tags class="specSel"> | |||
| <el-option v-for="item in specsList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| </div> | |||
| <div class="form-row" style="margin-top: 20px"> | |||
| <div class="title"></div> | |||
| <div class="content"> | |||
| <el-button type="primary" class="btn confirm-btn" @click="confirm">{{ $t('confirm') }}</el-button> | |||
| <el-button class="btn" @click="cancel">{{ $t('cancel') }}</el-button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </BaseDialog> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import BaseDialog from '~/components/BaseDialog.vue'; | |||
| import { getResQueueCode, getResSpecificationList, addResScene, updateResScene } from '~/apis/modules/resources'; | |||
| import { JOB_TYPE, CLUSTERS, AI_CENTER, ACC_CARD_TYPE, SPECIFICATION_STATUS } from '~/const'; | |||
| import { getListValueWithKey } from '~/utils'; | |||
| export default { | |||
| name: "SceneDialog", | |||
| props: { | |||
| visible: { type: Boolean, default: false }, | |||
| title: { type: String, default: '' }, | |||
| type: { type: String, defalut: 'add' }, | |||
| data: { type: Object, default: () => ({}) }, | |||
| }, | |||
| components: { | |||
| BaseDialog | |||
| }, | |||
| data() { | |||
| return { | |||
| dialogShow: false, | |||
| dataInfo: {}, | |||
| taskTypeList: [...JOB_TYPE], | |||
| clusterList: [...CLUSTERS], | |||
| accCardTypeList: [...ACC_CARD_TYPE], | |||
| statusList: [...SPECIFICATION_STATUS], | |||
| isExclusiveList: [{ k: '2', v: this.$t('resourcesManagement.commonUse') }, { k: '1', v: this.$t('resourcesManagement.exclusive') }], | |||
| queueList: [], | |||
| specsList: [], | |||
| }; | |||
| }, | |||
| watch: { | |||
| visible: function (val) { | |||
| this.dialogShow = val; | |||
| }, | |||
| }, | |||
| methods: { | |||
| resetDataInfo() { | |||
| this.dataInfo = { | |||
| SceneName: '', | |||
| JobType: '', | |||
| IsExclusive: '2', | |||
| ExclusiveOrg: '', | |||
| Cluster: '', | |||
| QueueId: '', | |||
| SpecIds: [], | |||
| } | |||
| this.queueList.splice(0, Infinity); | |||
| this.specsList.splice(0, Infinity); | |||
| }, | |||
| getQueueList(next) { | |||
| return getResQueueCode({ cluster: this.dataInfo.Cluster }).then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const data = res.Data; | |||
| const list = []; | |||
| for (let i = 0, iLen = data.length; i < iLen; i++) { | |||
| const item = data[i]; | |||
| list.push({ | |||
| k: item.ID, | |||
| v: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${item.AiCenterName})`, | |||
| }); | |||
| } | |||
| list.unshift({ | |||
| k: '-1', | |||
| v: this.$t('resourcesManagement.allResQueue'), | |||
| }); | |||
| this.queueList.splice(0, Infinity, ...list); | |||
| if (next) { | |||
| if (this.type === 'add') { | |||
| this.dataInfo.QueueId = '-1'; | |||
| } | |||
| this.getResSpecificationList(); | |||
| } | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| }); | |||
| }, | |||
| getResSpecificationList() { | |||
| const params = { | |||
| cluster: this.dataInfo.Cluster, | |||
| queue: this.dataInfo.QueueId === '-1' ? '' : this.dataInfo.QueueId, | |||
| status: 2, | |||
| page: 1, | |||
| }; | |||
| return getResSpecificationList(params).then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const list = res.Data.List; | |||
| const data = list.map((item) => { | |||
| const Queue = item.Queue; | |||
| const Spec = item.Spec; | |||
| // const NGPU = `${Queue.ComputeResource}:${Spec.AccCardsNum === 0 ? '0' : Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Queue.AccCardType)}`; | |||
| const NGPU = `${Queue.ComputeResource}:${Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Queue.AccCardType)}`; | |||
| return { | |||
| k: Spec.ID, | |||
| v: `${NGPU}, CPU:${Spec.CpuCores}, ${this.$t('resourcesManagement.gpuMem')}:${Spec.GPUMemGiB}GB, ${this.$t('resourcesManagement.mem')}:${Spec.MemGiB}GB, ${this.$t('resourcesManagement.shareMem')}:${Spec.ShareMemGiB}GB, ${this.$t('resourcesManagement.unitPrice')}:${Spec.UnitPrice}${this.$t('resourcesManagement.point_hr')}`, | |||
| } | |||
| }); | |||
| this.specsList.splice(0, Infinity, ...data); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| }); | |||
| }, | |||
| changeIsExclusive() { | |||
| this.dataInfo.ExclusiveOrg = ''; | |||
| }, | |||
| changeCluster() { | |||
| this.dataInfo.QueueId = ''; | |||
| this.dataInfo.SpecIds = []; | |||
| this.queueList.splice(0, Infinity); | |||
| this.specsList.splice(0, Infinity); | |||
| this.getQueueList(true); | |||
| }, | |||
| changeQueue() { | |||
| this.dataInfo.SpecIds = []; | |||
| this.specsList.splice(0, Infinity); | |||
| this.getResSpecificationList(); | |||
| }, | |||
| open() { | |||
| this.resetDataInfo(); | |||
| if (this.type === 'add') { | |||
| // | |||
| } else if (this.type === 'edit') { | |||
| Object.assign(this.dataInfo, { ...this.data, QueueId: this.data.QueueIds.length === 1 ? this.data.QueueIds[0] : '-1' }); | |||
| this.queueList.splice(0, Infinity); | |||
| this.specsList.splice(0, Infinity); | |||
| this.getQueueList(true); | |||
| } | |||
| this.$emit("open"); | |||
| }, | |||
| opened() { | |||
| this.$emit("opened"); | |||
| }, | |||
| close() { | |||
| this.$emit("close"); | |||
| }, | |||
| closed() { | |||
| this.$emit("closed"); | |||
| this.$emit("update:visible", false); | |||
| }, | |||
| confirm() { | |||
| if (!this.dataInfo.SceneName || !this.dataInfo.JobType || !this.dataInfo.SpecIds.length || (this.dataInfo.IsExclusive === '1' && !this.dataInfo.ExclusiveOrg)) { | |||
| this.$message({ | |||
| type: 'info', | |||
| message: this.$t('pleaseCompleteTheInformationFirst') | |||
| }); | |||
| return; | |||
| } | |||
| const setApi = this.type === 'add' ? addResScene : updateResScene; | |||
| setApi({ | |||
| ...this.dataInfo, | |||
| action: this.type === 'edit' ? 'edit' : undefined, | |||
| IsExclusive: this.dataInfo.IsExclusive === '1', | |||
| }).then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| this.$message({ | |||
| type: 'success', | |||
| message: this.$t('submittedSuccessfully') | |||
| }); | |||
| this.$emit("confirm"); | |||
| } else { | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| }) | |||
| }, | |||
| cancel() { | |||
| this.dialogShow = false; | |||
| this.$emit("update:visible", false); | |||
| } | |||
| }, | |||
| mounted() { | |||
| this.resetDataInfo(); | |||
| }, | |||
| }; | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .dlg-content { | |||
| margin: 20px 0 25px 0; | |||
| display: flex; | |||
| justify-content: center; | |||
| .form { | |||
| width: 600px; | |||
| .form-row { | |||
| display: flex; | |||
| min-height: 42px; | |||
| margin-bottom: 4px; | |||
| .title { | |||
| width: 160px; | |||
| display: flex; | |||
| justify-content: flex-end; | |||
| align-items: center; | |||
| margin-right: 20px; | |||
| color: rgb(136, 136, 136); | |||
| font-size: 14px; | |||
| &.required { | |||
| span { | |||
| position: relative; | |||
| } | |||
| span::after { | |||
| position: absolute; | |||
| right: -10px; | |||
| top: -2px; | |||
| vertical-align: top; | |||
| content: '*'; | |||
| color: #db2828; | |||
| } | |||
| } | |||
| } | |||
| .content { | |||
| width: 300px; | |||
| display: flex; | |||
| align-items: center; | |||
| /deep/ .el-select { | |||
| width: 100%; | |||
| } | |||
| } | |||
| .specSel { | |||
| /deep/ .el-tag.el-tag--info { | |||
| max-width: 81%; | |||
| display: flex; | |||
| align-items: center; | |||
| .el-select__tags-text { | |||
| overflow: hidden; | |||
| text-overflow: ellipsis; | |||
| } | |||
| .el-tag__close { | |||
| flex-shrink: 0; | |||
| right: -5px; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .btn { | |||
| color: rgb(2, 0, 4); | |||
| background-color: rgb(194, 199, 204); | |||
| border-color: rgb(194, 199, 204); | |||
| &.confirm-btn { | |||
| color: #fff; | |||
| background-color: rgb(56, 158, 13); | |||
| border-color: rgb(56, 158, 13); | |||
| } | |||
| } | |||
| } | |||
| .el-select-dropdown__item { | |||
| padding-left: 26px !important; | |||
| } | |||
| .el-select-dropdown__item.selected::after { | |||
| right: 0; | |||
| left: 6px; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,336 @@ | |||
| <template> | |||
| <div class="base-dlg"> | |||
| <BaseDialog :visible.sync="dialogShow" :width="`700px`" | |||
| :title="type === 'add' ? $t('resourcesManagement.addResSpecificationAndPriceInfo') : $t('resourcesManagement.editResSpecificationAndPriceInfo')" | |||
| @open="open" @opened="opened" @close="close" @closed="closed"> | |||
| <div class="dlg-content"> | |||
| <div class="form"> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.resQueue') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-select v-model="dataInfo.QueueId" :disabled="type === 'edit'"> | |||
| <el-option v-for="item in this.queueList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title"> | |||
| <span>{{ $t('resourcesManagement.sourceSpecCode') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-input v-model="dataInfo.SourceSpecId" :placeholder="$t('resourcesManagement.sourceSpecCodeTips')" maxlength="255" | |||
| :disabled="type === 'edit'"> | |||
| </el-input> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.accCardsNum') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-input v-model="dataInfo.AccCardsNum" type="number" placeholder="" :disabled="type === 'edit'"> | |||
| </el-input> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.cpuNum') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-input v-model="dataInfo.CpuCores" type="number" placeholder="" :disabled="type === 'edit'"></el-input> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.gpuMem') }}(GB)</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-input v-model="dataInfo.GPUMemGiB" type="number" placeholder="" :disabled="type === 'edit'"> | |||
| </el-input> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.mem') }}(GB)</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-input v-model="dataInfo.MemGiB" type="number" placeholder="" :disabled="type === 'edit'"></el-input> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.shareMem') }}(GB)</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-input v-model="dataInfo.ShareMemGiB" type="number" placeholder="" :disabled="type === 'edit'"> | |||
| </el-input> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('resourcesManagement.unitPrice') }}({{ $t('resourcesManagement.point_hr') }})</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-input v-model="dataInfo.UnitPrice" type="number" placeholder=""></el-input> | |||
| </div> | |||
| </div> | |||
| <div class="form-row"> | |||
| <div class="title required"> | |||
| <span>{{ $t('status') }}</span> | |||
| </div> | |||
| <div class="content"> | |||
| <el-select v-model="dataInfo.Status" :disabled="type === 'edit'"> | |||
| <el-option v-for="item in this.statusList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| </div> | |||
| <div class="form-row" style="margin-top: 20px"> | |||
| <div class="title"></div> | |||
| <div class="content"> | |||
| <el-button type="primary" class="btn confirm-btn" @click="confirm">{{ $t('confirm') }}</el-button> | |||
| <el-button class="btn" @click="cancel">{{ $t('cancel') }}</el-button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </BaseDialog> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import BaseDialog from '~/components/BaseDialog.vue'; | |||
| import { getResQueueCode, addResSpecification, updateResSpecification, getAiCenterList } from '~/apis/modules/resources'; | |||
| import { SPECIFICATION_STATUS, CLUSTERS } from '~/const'; | |||
| import { getListValueWithKey } from '~/utils'; | |||
| export default { | |||
| name: "SpecificationDialog", | |||
| props: { | |||
| visible: { type: Boolean, default: false }, | |||
| title: { type: String, default: '' }, | |||
| type: { type: String, defalut: 'add' }, | |||
| editOr: { type: Boolean, defalut: false }, | |||
| data: { type: Object, default: () => ({}) }, | |||
| }, | |||
| components: { | |||
| BaseDialog | |||
| }, | |||
| data() { | |||
| return { | |||
| dialogShow: false, | |||
| dataInfo: {}, | |||
| queueList: [], | |||
| statusList: [...SPECIFICATION_STATUS], | |||
| clusterList: [...CLUSTERS], | |||
| aiCenterList: [], | |||
| }; | |||
| }, | |||
| watch: { | |||
| visible: function (val) { | |||
| this.dialogShow = val; | |||
| }, | |||
| }, | |||
| methods: { | |||
| resetDataInfo() { | |||
| this.dataInfo = { | |||
| QueueId: '', | |||
| AccCardsNum: '', | |||
| CpuCores: '', | |||
| MemGiB: '', | |||
| ShareMemGiB: '', | |||
| GPUMemGiB: '', | |||
| UnitPrice: '', | |||
| Status: '1', | |||
| } | |||
| }, | |||
| getAiCenterList() { | |||
| getAiCenterList().then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const list = res.Data; | |||
| const data = list.map(item => { | |||
| return { | |||
| k: item.AiCenterCode, | |||
| v: item.AiCenterName | |||
| }; | |||
| }); | |||
| this.aiCenterList.splice(0, Infinity, ...data); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| }); | |||
| }, | |||
| getQueueList() { | |||
| getResQueueCode({ cluster: this.type === 'add' ? 'OpenI' : undefined }).then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const data = res.Data; | |||
| const list = []; | |||
| for (let i = 0, iLen = data.length; i < iLen; i++) { | |||
| const item = data[i]; | |||
| list.push({ | |||
| k: item.ID, | |||
| v: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${item.AiCenterName})`, | |||
| }); | |||
| } | |||
| this.queueList.splice(0, Infinity, ...list); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| }); | |||
| }, | |||
| open() { | |||
| this.resetDataInfo(); | |||
| this.getQueueList(); | |||
| this.getAiCenterList(); | |||
| if (this.type === 'add') { | |||
| // | |||
| } else if (this.type === 'edit') { | |||
| this.dataInfo = Object.assign(this.dataInfo, { ...this.data, Status: '2' }); | |||
| } | |||
| this.$emit("open"); | |||
| }, | |||
| opened() { | |||
| this.$emit("opened"); | |||
| }, | |||
| close() { | |||
| this.$emit("close"); | |||
| }, | |||
| closed() { | |||
| this.$emit("closed"); | |||
| this.$emit("update:visible", false); | |||
| }, | |||
| confirm() { | |||
| if (this.dataInfo.AccCardsNum === '' || this.dataInfo.CpuCores === '' || this.dataInfo.MemGiB === '' || this.dataInfo.ShareMemGiB === '' || this.dataInfo.GPUMemGiB === '' | |||
| || this.dataInfo.UnitPrice === '' || !this.dataInfo.Status | |||
| ) { | |||
| this.$message({ | |||
| type: 'info', | |||
| message: this.$t('pleaseCompleteTheInformationFirst') | |||
| }); | |||
| return; | |||
| } | |||
| if (parseInt(this.dataInfo.AccCardsNum) != Number(this.dataInfo.AccCardsNum)) { | |||
| this.$message({ | |||
| type: 'info', | |||
| message: this.$t('pleaseEnterPositiveIntegerCardsTotalNum') | |||
| }); | |||
| return; | |||
| } | |||
| const setApi = this.type === 'add' ? addResSpecification : updateResSpecification; | |||
| const action = this.editOr ? 'edit' : this.type === 'edit' ? 'on-shelf' : undefined; | |||
| setApi({ | |||
| ...this.dataInfo, | |||
| action: action, | |||
| AccCardsNum: Number(this.dataInfo.AccCardsNum), | |||
| CpuCores: Number(this.dataInfo.CpuCores), | |||
| MemGiB: Number(this.dataInfo.MemGiB), | |||
| ShareMemGiB: Number(this.dataInfo.ShareMemGiB), | |||
| GPUMemGiB: Number(this.dataInfo.GPUMemGiB), | |||
| UnitPrice: Number(this.dataInfo.UnitPrice), | |||
| Status: Number(this.dataInfo.Status), | |||
| }).then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| this.$message({ | |||
| type: 'success', | |||
| message: this.$t('submittedSuccessfully') | |||
| }); | |||
| this.$emit("confirm"); | |||
| } else { | |||
| if (action === 'on-shelf' && res.Code === 1001) { | |||
| this.$message({ | |||
| type: 'info', | |||
| message: this.$t('resourcesManagement.onShelfCode1001') | |||
| }); | |||
| } else { | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| } | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| }) | |||
| }, | |||
| cancel() { | |||
| this.dialogShow = false; | |||
| this.$emit("update:visible", false); | |||
| } | |||
| }, | |||
| mounted() { | |||
| this.resetDataInfo(); | |||
| }, | |||
| }; | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .dlg-content { | |||
| margin: 20px 0 25px 0; | |||
| display: flex; | |||
| justify-content: center; | |||
| .form { | |||
| width: 600px; | |||
| .form-row { | |||
| display: flex; | |||
| min-height: 42px; | |||
| margin-bottom: 4px; | |||
| .title { | |||
| width: 160px; | |||
| display: flex; | |||
| justify-content: flex-end; | |||
| align-items: center; | |||
| margin-right: 20px; | |||
| color: rgb(136, 136, 136); | |||
| font-size: 14px; | |||
| &.required { | |||
| span { | |||
| position: relative; | |||
| } | |||
| span::after { | |||
| position: absolute; | |||
| right: -10px; | |||
| top: -2px; | |||
| vertical-align: top; | |||
| content: '*'; | |||
| color: #db2828; | |||
| } | |||
| } | |||
| } | |||
| .content { | |||
| width: 300px; | |||
| display: flex; | |||
| align-items: center; | |||
| /deep/ .el-select { | |||
| width: 100%; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .btn { | |||
| color: rgb(2, 0, 4); | |||
| background-color: rgb(194, 199, 204); | |||
| border-color: rgb(194, 199, 204); | |||
| &.confirm-btn { | |||
| color: #fff; | |||
| background-color: rgb(56, 158, 13); | |||
| border-color: rgb(56, 158, 13); | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,284 @@ | |||
| <template> | |||
| <div> | |||
| <div class="title"><span>{{ $t('resourcesManagement.resQueue') }}</span></div> | |||
| <div class="tools-bar"> | |||
| <div> | |||
| <el-select class="select" size="medium" v-model="selCluster" @change="selectChange"> | |||
| <el-option v-for="item in clusterList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| <el-select class="select" size="medium" v-model="selComputingCenter" @change="selectChange"> | |||
| <el-option v-for="item in computingCenterList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| <el-select class="select" size="medium" v-model="selComputingType" @change="selectChange"> | |||
| <el-option v-for="item in computingTypeList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| <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> | |||
| </div> | |||
| <div> | |||
| <el-button size="medium" icon="el-icon-refresh" @click="syncComputerNetwork" v-loading="syncLoading"> | |||
| {{ $t('resourcesManagement.syncAiNetwork') }}</el-button> | |||
| <el-button type="primary" icon="el-icon-plus" size="medium" @click="showDialog('add')"> | |||
| {{ $t('resourcesManagement.addResQueueBtn') }}</el-button> | |||
| </div> | |||
| </div> | |||
| <div class="table-container"> | |||
| <div style="min-height:600px;"> | |||
| <el-table border :data="tableData" style="width: 100%" v-loading="loading" stripe> | |||
| <el-table-column prop="ID" label="ID" align="center" header-align="center" width="80"></el-table-column> | |||
| <el-table-column prop="QueueCode" :label="$t('resourcesManagement.resQueueName')" align="center" | |||
| header-align="center"></el-table-column> | |||
| <el-table-column prop="ClusterName" :label="$t('resourcesManagement.whichCluster')" align="center" | |||
| header-align="center"> | |||
| <template slot-scope="scope"> | |||
| <span :title="scope.row.Cluster">{{ scope.row.ClusterName }}</span> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column prop="AiCenterCode" :label="$t('resourcesManagement.aiCenterID')" align="center" | |||
| header-align="center"></el-table-column> | |||
| <el-table-column prop="AiCenterName" :label="$t('resourcesManagement.aiCenter')" align="center" | |||
| header-align="center"></el-table-column> | |||
| <el-table-column prop="ComputeResourceName" :label="$t('resourcesManagement.computeResource')" align="center" | |||
| header-align="center"> | |||
| </el-table-column> | |||
| <el-table-column prop="AccCardTypeName" :label="$t('resourcesManagement.accCardType')" align="center" | |||
| 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="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" | |||
| min-width="160"> | |||
| </el-table-column> | |||
| <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') | |||
| }}</span> | |||
| <span v-else class="op-btn" style="color:rgb(187, 187, 187);cursor:not-allowed">{{ | |||
| $t('edit') | |||
| }}</span> | |||
| </template> | |||
| </el-table-column> | |||
| <template slot="empty"> | |||
| <span style="font-size: 12px">{{ | |||
| loading ? $t('loading') : $t('noData') | |||
| }}</span> | |||
| </template> | |||
| </el-table> | |||
| </div> | |||
| <div class="__r_p_pagination"> | |||
| <div style="margin-top: 2rem"> | |||
| <div class="center"> | |||
| <el-pagination background @current-change="currentChange" :current-page="pageInfo.curpage" | |||
| :page-sizes="pageInfo.pageSizes" :page-size="pageInfo.pageSize" | |||
| layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total"> | |||
| </el-pagination> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <QueueDialog :visible.sync="queueDialogShow" :type="queueDialogType" :data="queueDialogData" | |||
| @confirm="queueDialogConfirm"></QueueDialog> | |||
| </div> | |||
| </template> | |||
| <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 { getListValueWithKey } from '~/utils'; | |||
| import { formatDate } from 'element-ui/lib/utils/date-util'; | |||
| export default { | |||
| data() { | |||
| return { | |||
| selCluster: '', | |||
| clusterList: [{ k: '', v: this.$t('resourcesManagement.allCluster') }, ...CLUSTERS], | |||
| selComputingCenter: '', | |||
| computingCenterList: [{ k: '', v: this.$t('resourcesManagement.allAiCenter') }], | |||
| selComputingType: '', | |||
| computingTypeList: [{ k: '', v: this.$t('resourcesManagement.allComputeResource') }, ...COMPUTER_RESOURCES], | |||
| selCardType: '', | |||
| cardTypeList: [{ k: '', v: this.$t('resourcesManagement.allAccCardType') }, ...ACC_CARD_TYPE], | |||
| syncLoading: false, | |||
| loading: false, | |||
| tableData: [], | |||
| pageInfo: { | |||
| curpage: 1, | |||
| pageSize: 10, | |||
| pageSizes: [10], | |||
| total: 0, | |||
| }, | |||
| queueDialogShow: false, | |||
| queueDialogType: 'add', | |||
| queueDialogData: {}, | |||
| }; | |||
| }, | |||
| components: { QueueDialog }, | |||
| methods: { | |||
| getAiCenterList() { | |||
| getAiCenterList().then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const list = res.Data; | |||
| const data = list.map(item => { | |||
| return { | |||
| k: item.AiCenterCode, | |||
| v: item.AiCenterName | |||
| }; | |||
| }); | |||
| this.computingCenterList.splice(1, Infinity, ...data); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| }); | |||
| }, | |||
| getTableData() { | |||
| const params = { | |||
| cluster: this.selCluster, | |||
| center: this.selComputingCenter, | |||
| resource: this.selComputingType, | |||
| card: this.selCardType, | |||
| page: this.pageInfo.curpage, | |||
| pagesize: this.pageInfo.pageSize, | |||
| }; | |||
| this.loading = true; | |||
| getResQueueList(params).then(res => { | |||
| this.loading = false; | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const list = res.Data.List; | |||
| const data = list.map((item) => { | |||
| return { | |||
| ...item, | |||
| QueueCode: item.QueueCode || '--', | |||
| ClusterName: getListValueWithKey(this.clusterList, item.Cluster), | |||
| ComputeResourceName: getListValueWithKey(this.computingTypeList, item.ComputeResource), | |||
| AccCardTypeName: getListValueWithKey(this.cardTypeList, item.AccCardType), | |||
| UpdatedTimeStr: formatDate(new Date(item.UpdatedTime * 1000), 'yyyy-MM-dd HH:mm:ss'), | |||
| } | |||
| }); | |||
| this.tableData = data; | |||
| this.pageInfo.total = res.Data.TotalSize; | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| this.loading = false; | |||
| }); | |||
| }, | |||
| syncComputerNetwork() { | |||
| this.syncLoading = true; | |||
| syncResQueue().then(res => { | |||
| this.syncLoading = false; | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| this.$message({ | |||
| type: 'success', | |||
| message: this.$t('submittedSuccessfully') | |||
| }); | |||
| this.getAiCenterList(); | |||
| this.getTableData(); | |||
| } else { | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| this.syncLoading = false; | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| }); | |||
| }, | |||
| selectChange() { | |||
| this.pageInfo.curpage = 1; | |||
| this.getTableData(); | |||
| }, | |||
| currentChange(val) { | |||
| this.pageInfo.curpage = val; | |||
| this.getTableData(); | |||
| }, | |||
| showDialog(type, data) { | |||
| this.queueDialogType = type; | |||
| this.queueDialogData = data ? { ...data } : {}; | |||
| this.queueDialogShow = true; | |||
| }, | |||
| queueDialogConfirm() { | |||
| this.queueDialogShow = false; | |||
| this.getAiCenterList(); | |||
| this.getTableData(); | |||
| } | |||
| }, | |||
| mounted() { | |||
| this.getAiCenterList(); | |||
| this.getTableData(); | |||
| }, | |||
| beforeDestroy() { | |||
| }, | |||
| }; | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .title { | |||
| height: 30px; | |||
| display: flex; | |||
| align-items: center; | |||
| margin-bottom: 5px; | |||
| span { | |||
| font-weight: 700; | |||
| font-size: 16px; | |||
| color: rgb(16, 16, 16); | |||
| } | |||
| } | |||
| .tools-bar { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: space-between; | |||
| margin-bottom: 10px; | |||
| .select { | |||
| margin-right: 10px; | |||
| /deep/ .el-input__inner { | |||
| border-radius: 0; | |||
| } | |||
| } | |||
| } | |||
| .table-container { | |||
| margin-bottom: 16px; | |||
| /deep/ .el-table__header { | |||
| th { | |||
| background: rgb(245, 245, 246); | |||
| font-size: 12px; | |||
| color: rgb(36, 36, 36); | |||
| } | |||
| } | |||
| /deep/ .el-table__body { | |||
| td { | |||
| font-size: 12px; | |||
| } | |||
| } | |||
| .op-btn { | |||
| cursor: pointer; | |||
| font-size: 12px; | |||
| color: rgb(25, 103, 252); | |||
| margin: 0 5px; | |||
| } | |||
| } | |||
| .center { | |||
| display: flex; | |||
| justify-content: center; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,17 @@ | |||
| import Vue from 'vue'; | |||
| import ElementUI from 'element-ui'; | |||
| import 'element-ui/lib/theme-chalk/index.css'; | |||
| import localeEn from 'element-ui/lib/locale/lang/en'; | |||
| import localeZh from 'element-ui/lib/locale/lang/zh-CN'; | |||
| import { i18n, lang } from '~/langs'; | |||
| import App from './index.vue'; | |||
| Vue.use(ElementUI, { | |||
| locale: lang === 'zh-CN' ? localeZh : localeEn, | |||
| size: 'small', | |||
| }); | |||
| new Vue({ | |||
| i18n, | |||
| render: (h) => h(App), | |||
| }).$mount('#__vue-root'); | |||
| @@ -0,0 +1,361 @@ | |||
| <template> | |||
| <div> | |||
| <div class="title"><span>{{ $t('resourcesManagement.resSceneManagement') }}</span></div> | |||
| <div class="tools-bar"> | |||
| <div> | |||
| <el-select class="select" size="medium" v-model="selTaskType" @change="selectChange"> | |||
| <el-option v-for="item in taskTypeList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| <el-select class="select" size="medium" v-model="selIsExclusive" @change="selectChange"> | |||
| <el-option v-for="item in isExclusiveList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| <el-select class="select" size="medium" v-model="selAiCenter" @change="selectChange"> | |||
| <el-option v-for="item in aiCenterList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| <el-select class="select" size="medium" v-model="selQueue" @change="selectChange"> | |||
| <el-option v-for="item in queueList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| </div> | |||
| <div> | |||
| <el-button type="primary" icon="el-icon-plus" size="medium" @click="showDialog('add')"> | |||
| {{ $t('resourcesManagement.addResSceneBtn') }}</el-button> | |||
| </div> | |||
| </div> | |||
| <div class="table-container"> | |||
| <div style="min-height:600px;"> | |||
| <el-table border :data="tableData" style="width: 100%" v-loading="loading" stripe> | |||
| <el-table-column prop="ID" label="ID" align="center" header-align="center" width="60"></el-table-column> | |||
| <el-table-column prop="SceneName" :label="$t('resourcesManagement.resSceneName')" align="center" | |||
| header-align="center"></el-table-column> | |||
| <el-table-column prop="JobTypeStr" :label="$t('resourcesManagement.jobType')" align="center" | |||
| header-align="center" width="120"> | |||
| </el-table-column> | |||
| <el-table-column prop="IsExclusiveStr" :label="$t('resourcesManagement.isExclusive')" align="center" | |||
| header-align="center" width="120"> | |||
| <template slot-scope="scope"> | |||
| <span :style="{ color: scope.row.IsExclusive ? 'red' : '' }">{{ scope.row.IsExclusiveStr }}</span> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column prop="ExclusiveOrg" :label="$t('resourcesManagement.exclusiveOrg')" align="center" | |||
| header-align="center"> | |||
| <template slot-scope="scope"> | |||
| <span>{{ scope.row.IsExclusive ? scope.row.ExclusiveOrg : '--' }}</span> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column prop="AiCenterStr" :label="$t('resourcesManagement.aiCenter')" align="center" | |||
| header-align="center"> | |||
| <template slot-scope="scope"> | |||
| <div v-if="!scope.row.Queues.length">--</div> | |||
| <div v-for="item in scope.row.Queues" :key="item.QueueId"> | |||
| <span>{{ item.AiCenterName }}</span> | |||
| </div> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column prop="QueueStr" :label="$t('resourcesManagement.resQueue')" align="center" | |||
| header-align="center"> | |||
| <template slot-scope="scope"> | |||
| <div v-if="!scope.row.Queues.length">--</div> | |||
| <div v-for="item in scope.row.Queues" :key="item.QueueId"> | |||
| <span>{{ item.QueueStr }}</span> | |||
| </div> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column prop="SpecsList" :label="$t('resourcesManagement.resourceSpecification')" align="left" | |||
| header-align="center" min-width="180"> | |||
| <template slot-scope="scope"> | |||
| <div v-for="item in scope.row.SpecsList" :key="item.k"> | |||
| <span>{{ item.v }}</span> | |||
| </div> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column :label="$t('operation')" align="center" header-align="center" width="100"> | |||
| <template slot-scope="scope"> | |||
| <span class="op-btn" @click="showDialog('edit', scope.row)">{{ $t('edit') }}</span> | |||
| <span class="op-btn" style="color:red" @click="deleteRow(scope.row)">{{ $t('delete') }}</span> | |||
| </template> | |||
| </el-table-column> | |||
| <template slot="empty"> | |||
| <span style="font-size: 12px">{{ | |||
| loading ? $t('loading') : $t('noData') | |||
| }}</span> | |||
| </template> | |||
| </el-table> | |||
| </div> | |||
| <div class="__r_p_pagination"> | |||
| <div style="margin-top: 2rem"> | |||
| <div class="center"> | |||
| <el-pagination background @current-change="currentChange" :current-page="pageInfo.curpage" | |||
| :page-sizes="pageInfo.pageSizes" :page-size="pageInfo.pageSize" | |||
| layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total"> | |||
| </el-pagination> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <SceneDialog :visible.sync="sceneDialogShow" :type="sceneDialogType" :data="sceneDialogData" | |||
| @confirm="sceneDialogConfirm"></SceneDialog> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import SceneDialog from '../components/SceneDialog.vue'; | |||
| import { getQueueList, getResQueueCode, getResSceneList, updateResScene, getAiCenterList } from '~/apis/modules/resources'; | |||
| import { JOB_TYPE, CLUSTERS, ACC_CARD_TYPE } from '~/const'; | |||
| import { getListValueWithKey } from '~/utils'; | |||
| import { formatDate } from 'element-ui/lib/utils/date-util'; | |||
| export default { | |||
| data() { | |||
| return { | |||
| selTaskType: '', | |||
| taskTypeList: [{ k: '', v: this.$t('resourcesManagement.allJobType') }, ...JOB_TYPE], | |||
| selIsExclusive: '', | |||
| isExclusiveList: [{ k: '', v: this.$t('resourcesManagement.allExclusiveAndCommonUse') }, { k: '1', v: this.$t('resourcesManagement.exclusive') }, { k: '2', v: this.$t('resourcesManagement.commonUse') }], | |||
| selQueue: '', | |||
| queueList: [{ k: '', v: this.$t('resourcesManagement.allResQueue') }], | |||
| clusterList: [...CLUSTERS], | |||
| selAiCenter: '', | |||
| aiCenterList: [{ k: '', v: this.$t('resourcesManagement.allAiCenter') }], | |||
| accCardTypeList: [...ACC_CARD_TYPE], | |||
| loading: false, | |||
| tableData: [], | |||
| pageInfo: { | |||
| curpage: 1, | |||
| pageSize: 10, | |||
| pageSizes: [10], | |||
| total: 0, | |||
| }, | |||
| sceneDialogShow: false, | |||
| sceneDialogType: 'add', | |||
| sceneDialogData: {}, | |||
| }; | |||
| }, | |||
| components: { SceneDialog }, | |||
| methods: { | |||
| getAiCenterList() { | |||
| getAiCenterList().then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const list = res.Data; | |||
| const data = list.map(item => { | |||
| return { | |||
| k: item.AiCenterCode, | |||
| v: item.AiCenterName | |||
| }; | |||
| }); | |||
| this.aiCenterList.splice(1, Infinity, ...data); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| }); | |||
| }, | |||
| getQueueList() { | |||
| getResQueueCode().then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const data = res.Data; | |||
| const list = []; | |||
| for (let i = 0, iLen = data.length; i < iLen; i++) { | |||
| const item = data[i]; | |||
| list.push({ | |||
| k: item.ID, | |||
| v: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${item.AiCenterName})`, | |||
| }); | |||
| } | |||
| this.queueList.push(...list); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| }); | |||
| }, | |||
| getTableData() { | |||
| const params = { | |||
| jobType: this.selTaskType, | |||
| IsExclusive: this.selIsExclusive, | |||
| queue: this.selQueue, | |||
| center: this.selAiCenter, | |||
| page: this.pageInfo.curpage, | |||
| pagesize: this.pageInfo.pageSize, | |||
| }; | |||
| this.loading = true; | |||
| getResSceneList(params).then(res => { | |||
| this.loading = false; | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const list = res.Data.List; | |||
| const data = list.map((item) => { | |||
| const Specs = item.Specs; | |||
| const specsList = []; | |||
| const queues = []; | |||
| const queueIds = []; | |||
| let cluster = ''; | |||
| for (let i = 0, iLen = Specs.length; i < iLen; i++) { | |||
| const Spec = Specs[i]; | |||
| // const NGPU = `${Spec.ComputeResource}:${Spec.AccCardsNum === 0 ? '0' : Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Spec.AccCardType)}`; | |||
| const NGPU = `${Spec.ComputeResource}:${Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Spec.AccCardType)}`; | |||
| specsList.push({ | |||
| k: Spec.ID, | |||
| v: `${NGPU}, CPU:${Spec.CpuCores}, ${this.$t('resourcesManagement.gpuMem')}:${Spec.GPUMemGiB}GB, ${this.$t('resourcesManagement.mem')}:${Spec.MemGiB}GB, ${this.$t('resourcesManagement.shareMem')}:${Spec.ShareMemGiB}GB, ${this.$t('resourcesManagement.unitPrice')}:${Spec.UnitPrice}${this.$t('resourcesManagement.point_hr')}`, | |||
| }); | |||
| cluster = Spec.Cluster; | |||
| if (queueIds.indexOf(Spec.QueueId) < 0) { | |||
| queues.push({ | |||
| QueueId: Spec.QueueId, | |||
| QueueCode: Spec.QueueCode, | |||
| AiCenterCode: Spec.AiCenterCode, | |||
| AiCenterName: Spec.AiCenterName, | |||
| QueueStr: `${Spec.QueueCode}(${getListValueWithKey(this.clusterList, Spec.Cluster)} - ${Spec.AiCenterName})`, | |||
| }); | |||
| queueIds.push(Spec.QueueId); | |||
| } | |||
| } | |||
| return { | |||
| ID: item.ID, | |||
| SceneName: item.SceneName, | |||
| JobType: item.JobType, | |||
| JobTypeStr: getListValueWithKey(this.taskTypeList, item.JobType), | |||
| IsExclusive: item.IsExclusive, | |||
| IsExclusiveStr: getListValueWithKey(this.isExclusiveList, item.IsExclusive ? '1' : '2'), | |||
| ExclusiveOrg: item.ExclusiveOrg, | |||
| Cluster: cluster, | |||
| QueueIds: queueIds, | |||
| Queues: queues, | |||
| SpecsList: specsList, | |||
| } | |||
| }); | |||
| this.tableData = data; | |||
| this.pageInfo.total = res.Data.TotalSize; | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| this.loading = false; | |||
| }); | |||
| }, | |||
| selectChange() { | |||
| this.pageInfo.curpage = 1; | |||
| this.getTableData(); | |||
| }, | |||
| currentChange(val) { | |||
| this.pageInfo.curpage = val; | |||
| this.getTableData(); | |||
| }, | |||
| deleteRow(row) { | |||
| this.$confirm(this.$t('resourcesManagement.resSceneDeleteConfirm'), this.$t('tips'), { | |||
| confirmButtonText: this.$t('confirm1'), | |||
| cancelButtonText: this.$t('cancel'), | |||
| type: 'warning', | |||
| lockScroll: false, | |||
| }).then(() => { | |||
| updateResScene({ | |||
| action: 'delete', | |||
| ID: row.ID, | |||
| }).then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| this.$message({ | |||
| type: 'success', | |||
| message: this.$t('submittedSuccessfully') | |||
| }); | |||
| this.getTableData(); | |||
| } else { | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| } | |||
| }).catch(err => { | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| }); | |||
| }).catch(() => { }); | |||
| }, | |||
| showDialog(type, data) { | |||
| this.sceneDialogType = type; | |||
| this.sceneDialogData = data ? { | |||
| ID: data.ID, | |||
| SceneName: data.SceneName, | |||
| JobType: data.JobType, | |||
| IsExclusive: data.IsExclusive ? '1' : '2', | |||
| ExclusiveOrg: data.ExclusiveOrg, | |||
| Cluster: data.Cluster, | |||
| QueueIds: data.QueueIds, | |||
| SpecIds: data.SpecsList.map((item) => item.k), | |||
| } : {}; | |||
| this.sceneDialogShow = true; | |||
| }, | |||
| sceneDialogConfirm() { | |||
| this.sceneDialogShow = false; | |||
| this.getTableData(); | |||
| } | |||
| }, | |||
| mounted() { | |||
| this.getAiCenterList(); | |||
| this.getQueueList(); | |||
| this.getTableData(); | |||
| }, | |||
| beforeDestroy() { | |||
| }, | |||
| }; | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .title { | |||
| height: 30px; | |||
| display: flex; | |||
| align-items: center; | |||
| margin-bottom: 5px; | |||
| span { | |||
| font-weight: 700; | |||
| font-size: 16px; | |||
| color: rgb(16, 16, 16); | |||
| } | |||
| } | |||
| .tools-bar { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: space-between; | |||
| margin-bottom: 10px; | |||
| .select { | |||
| margin-right: 10px; | |||
| /deep/ .el-input__inner { | |||
| border-radius: 0; | |||
| } | |||
| } | |||
| } | |||
| .table-container { | |||
| margin-bottom: 16px; | |||
| /deep/ .el-table__header { | |||
| th { | |||
| background: rgb(245, 245, 246); | |||
| font-size: 12px; | |||
| color: rgb(36, 36, 36); | |||
| } | |||
| } | |||
| /deep/ .el-table__body { | |||
| td { | |||
| font-size: 12px; | |||
| } | |||
| } | |||
| .op-btn { | |||
| cursor: pointer; | |||
| font-size: 12px; | |||
| color: rgb(25, 103, 252); | |||
| margin: 0 5px; | |||
| } | |||
| } | |||
| .center { | |||
| display: flex; | |||
| justify-content: center; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,17 @@ | |||
| import Vue from 'vue'; | |||
| import ElementUI from 'element-ui'; | |||
| import 'element-ui/lib/theme-chalk/index.css'; | |||
| import localeEn from 'element-ui/lib/locale/lang/en'; | |||
| import localeZh from 'element-ui/lib/locale/lang/zh-CN'; | |||
| import { i18n, lang } from '~/langs'; | |||
| import App from './index.vue'; | |||
| Vue.use(ElementUI, { | |||
| locale: lang === 'zh-CN' ? localeZh : localeEn, | |||
| size: 'small', | |||
| }); | |||
| new Vue({ | |||
| i18n, | |||
| render: (h) => h(App), | |||
| }).$mount('#__vue-root'); | |||
| @@ -0,0 +1,451 @@ | |||
| <template> | |||
| <div> | |||
| <div class="title"><span>{{ $t('resourcesManagement.resSpecificationAndPriceManagement') }}</span></div> | |||
| <div class="tools-bar"> | |||
| <div> | |||
| <el-select class="select" size="medium" v-model="selQueue" @change="selectChange"> | |||
| <el-option v-for="item in queueList" :key="item.k" :label="item.v" :value="item.k" /> | |||
| </el-select> | |||
| <el-select class="select" size="medium" v-model="selStatus" @change="selectChange"> | |||
| <el-option v-for="item in statusList" :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"> | |||
| {{ $t('resourcesManagement.syncAiNetwork') }}</el-button> | |||
| <el-button type="primary" icon="el-icon-plus" size="medium" @click="showDialog('add')"> | |||
| {{ $t('resourcesManagement.addResSpecificationBtn') }}</el-button> | |||
| </div> | |||
| </div> | |||
| <div class="table-container"> | |||
| <div style="min-height:600px;"> | |||
| <el-table border :data="tableData" style="width: 100%" v-loading="loading" stripe> | |||
| <el-table-column prop="ID" label="ID" align="center" header-align="center" width="60"></el-table-column> | |||
| <el-table-column prop="SpecStr" :label="$t('resourcesManagement.resourceSpecification')" align="left" | |||
| header-align="center" min-width="160"> | |||
| </el-table-column> | |||
| <el-table-column prop="QueueInfo" :label="$t('resourcesManagement.resQueue')" align="center" | |||
| header-align="center" min-width="100"> | |||
| </el-table-column> | |||
| <el-table-column prop="SourceSpecId" :label="$t('resourcesManagement.sourceSpecCode')" align="center" | |||
| header-align="center"> | |||
| </el-table-column> | |||
| <el-table-column prop="AccCardsNum" :label="$t('resourcesManagement.accCardsNum')" align="center" | |||
| header-align="center"></el-table-column> | |||
| <el-table-column prop="CpuCores" :label="$t('resourcesManagement.cpuNum')" align="center" | |||
| header-align="center"></el-table-column> | |||
| <el-table-column prop="GPUMemGiB" :label="`${$t('resourcesManagement.gpuMem')}(GB)`" align="center" | |||
| header-align="center"></el-table-column> | |||
| <el-table-column prop="MemGiB" :label="`${$t('resourcesManagement.mem')}(GB)`" align="center" | |||
| header-align="center"></el-table-column> | |||
| <el-table-column prop="ShareMemGiB" :label="`${$t('resourcesManagement.shareMem')}(GB)`" 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="UnitPrice" | |||
| :label="`${$t('resourcesManagement.unitPrice')}(${$t('resourcesManagement.point_hr')})`" align="center" | |||
| header-align="center"> | |||
| <template slot-scope="scope"> | |||
| <span style="font-weight:600;font-size:14px;">{{ scope.row.UnitPrice }}</span> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column prop="StatusStr" :label="$t('resourcesManagement.status')" align="center" | |||
| header-align="center" width="100"> | |||
| <template slot-scope="scope"> | |||
| <span :style="{ color: scope.row.Status == '2' ? 'rgb(82, 196, 26)' : 'rgb(245, 34, 45)' }">{{ | |||
| scope.row.StatusStr | |||
| }}</span> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column :label="$t('operation')" align="center" header-align="center" width="100"> | |||
| <template slot-scope="scope"> | |||
| <span v-if="scope.row.Status == '1' && !scope.row.UnitPrice"> | |||
| <span class="op-btn" @click="showDialog('edit', scope.row)">{{ | |||
| $t('resourcesManagement.toSetPriceAndOnShelf') | |||
| }}</span> | |||
| </span> | |||
| <span v-if="scope.row.Status == '2'"> | |||
| <span class="op-btn" @click="showDialog('edit', scope.row, true)">{{ $t('edit') }}</span> | |||
| <span class="op-btn" @click="offShelfPrev(scope.row)">{{ | |||
| $t('resourcesManagement.toOffShelf') | |||
| }}</span> | |||
| </span> | |||
| <span v-if="scope.row.Status == '3' || scope.row.Status == '1' && scope.row.UnitPrice"> | |||
| <span class="op-btn" @click="onShelf(scope.row)">{{ | |||
| $t('resourcesManagement.toOnShelf') | |||
| }}</span> | |||
| </span> | |||
| </template> | |||
| </el-table-column> | |||
| <template slot="empty"> | |||
| <span style="font-size: 12px">{{ | |||
| loading ? $t('loading') : $t('noData') | |||
| }}</span> | |||
| </template> | |||
| </el-table> | |||
| </div> | |||
| <div class="__r_p_pagination"> | |||
| <div style="margin-top: 2rem"> | |||
| <div class="center"> | |||
| <el-pagination background @current-change="currentChange" :current-page="pageInfo.curpage" | |||
| :page-sizes="pageInfo.pageSizes" :page-size="pageInfo.pageSize" | |||
| layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total"> | |||
| </el-pagination> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <SpecificationDialog :visible.sync="specificationDialogShow" :type="specificationDialogType" | |||
| :editOr="specificationDialogEditOr" :data="specificationDialogData" @confirm="specificationDialogConfirm"> | |||
| </SpecificationDialog> | |||
| <BaseDialog :visible.sync="offShelfDialogShow" :width="`600px`" :title="$t('tips')"> | |||
| <div class="form"> | |||
| <div class="form-row" style="flex-direction:column;"> | |||
| <div class="content" style="margin:8px 0">{{ $t('resourcesManagement.offShelfDlgTip1') }}</div> | |||
| <div class="content" style="margin:8px 0;font-weight: bold;">{{ offSelfDialogContent }}</div> | |||
| <div class="content" style="margin:8px 0">{{ $t('resourcesManagement.offShelfDlgTip2') }}</div> | |||
| </div> | |||
| <div class="form-row" style="margin-top: 20px"> | |||
| <div class="content"> | |||
| <el-button type="primary" class="btn confirm-btn" @click="offShelf">{{ $t('confirm') }}</el-button> | |||
| <el-button class="btn" @click="offShelfDialogShow = false">{{ $t('cancel') }}</el-button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </BaseDialog> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| 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 } from '~/const'; | |||
| import { getListValueWithKey } from '~/utils'; | |||
| import { formatDate } from 'element-ui/lib/utils/date-util'; | |||
| export default { | |||
| data() { | |||
| return { | |||
| selQueue: '', | |||
| queueList: [{ k: '', v: this.$t('resourcesManagement.allResQueue') }], | |||
| selStatus: '', | |||
| statusList: [{ k: '', v: this.$t('resourcesManagement.allStatus') }, ...SPECIFICATION_STATUS], | |||
| clusterList: [...CLUSTERS], | |||
| accCardTypeList: [...ACC_CARD_TYPE], | |||
| syncLoading: false, | |||
| loading: false, | |||
| tableData: [], | |||
| pageInfo: { | |||
| curpage: 1, | |||
| pageSize: 10, | |||
| pageSizes: [10], | |||
| total: 0, | |||
| }, | |||
| specificationDialogShow: false, | |||
| specificationDialogType: 'add', | |||
| specificationDialogEditOr: false, | |||
| specificationDialogData: {}, | |||
| offShelfDialogShow: false, | |||
| offShelfDialogData: {}, | |||
| offSelfDialogContent: '', | |||
| }; | |||
| }, | |||
| components: { BaseDialog, SpecificationDialog }, | |||
| methods: { | |||
| getQueueList() { | |||
| getResQueueCode().then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const data = res.Data; | |||
| const list = []; | |||
| for (let i = 0, iLen = data.length; i < iLen; i++) { | |||
| const item = data[i]; | |||
| list.push({ | |||
| k: item.ID, | |||
| v: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${item.AiCenterName})`, | |||
| }); | |||
| } | |||
| this.queueList.push(...list); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| }); | |||
| }, | |||
| getTableData() { | |||
| const params = { | |||
| queue: this.selQueue, | |||
| status: this.selStatus, | |||
| page: this.pageInfo.curpage, | |||
| pagesize: this.pageInfo.pageSize, | |||
| }; | |||
| this.loading = true; | |||
| getResSpecificationList(params).then(res => { | |||
| this.loading = false; | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const list = res.Data.List; | |||
| const data = list.map((item) => { | |||
| const Queue = item.Queue; | |||
| const Spec = item.Spec; | |||
| // const NGPU = `${Queue.ComputeResource}:${Spec.AccCardsNum === 0 ? '0' : Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Queue.AccCardType)}`; | |||
| const NGPU = `${Queue.ComputeResource}:${Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Queue.AccCardType)}`; | |||
| return { | |||
| ...Spec, | |||
| SourceSpecId: Spec.SourceSpecId || '--', | |||
| SpecStr: `${NGPU}, CPU:${Spec.CpuCores}, ${this.$t('resourcesManagement.gpuMem')}:${Spec.GPUMemGiB}GB, ${this.$t('resourcesManagement.mem')}:${Spec.MemGiB}GB, ${this.$t('resourcesManagement.shareMem')}:${Spec.ShareMemGiB}GB`, | |||
| QueueId: Queue.ID, | |||
| QueueInfo: `${Queue.QueueCode}(${getListValueWithKey(this.clusterList, Queue.Cluster)} - ${Queue.AiCenterName})`, | |||
| UpdatedTimeStr: formatDate(new Date(Spec.UpdatedTime * 1000), 'yyyy-MM-dd HH:mm:ss'), | |||
| Status: Spec.Status.toString(), | |||
| StatusStr: getListValueWithKey(this.statusList, Spec.Status.toString()), | |||
| } | |||
| }); | |||
| this.tableData = data; | |||
| this.pageInfo.total = res.Data.TotalSize; | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| this.loading = false; | |||
| }); | |||
| }, | |||
| syncComputerNetwork() { | |||
| this.syncLoading = true; | |||
| syncResSpecification().then(res => { | |||
| this.syncLoading = false; | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| this.$message({ | |||
| type: 'success', | |||
| message: this.$t('submittedSuccessfully') | |||
| }); | |||
| this.getTableData(); | |||
| } else { | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| this.syncLoading = false; | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| }); | |||
| }, | |||
| selectChange() { | |||
| this.pageInfo.curpage = 1; | |||
| this.getTableData(); | |||
| }, | |||
| currentChange(val) { | |||
| this.pageInfo.curpage = val; | |||
| this.getTableData(); | |||
| }, | |||
| showDialog(type, data, editOr) { | |||
| this.specificationDialogType = type; | |||
| this.specificationDialogEditOr = !!editOr; | |||
| this.specificationDialogData = data ? { ...data } : {}; | |||
| this.specificationDialogShow = true; | |||
| }, | |||
| specificationDialogConfirm() { | |||
| this.specificationDialogShow = false; | |||
| this.getTableData(); | |||
| }, | |||
| onShelf(data) { | |||
| const type = 'on-shelf'; | |||
| this.$confirm(type === 'on-shelf' ? this.$t('resourcesManagement.onShelfConfirm') : this.$t('resourcesManagement.offShelfConfirm'), this.$t('tips'), { | |||
| confirmButtonText: this.$t('confirm1'), | |||
| cancelButtonText: this.$t('cancel'), | |||
| type: 'warning', | |||
| lockScroll: false, | |||
| }).then(() => { | |||
| updateResSpecification({ | |||
| ID: data.ID, | |||
| action: type | |||
| }).then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| this.$message({ | |||
| type: 'success', | |||
| message: this.$t('submittedSuccessfully') | |||
| }); | |||
| this.getTableData(); | |||
| } else { | |||
| if (type === 'on-shelf' && res.Code === 1001) { | |||
| this.$message({ | |||
| type: 'info', | |||
| message: this.$t('resourcesManagement.onShelfCode1001') | |||
| }); | |||
| } else { | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| } | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| }); | |||
| }).catch(() => { }); | |||
| }, | |||
| offShelfPrev(data) { | |||
| this.$confirm(this.$t('resourcesManagement.offShelfConfirm'), this.$t('tips'), { | |||
| confirmButtonText: this.$t('confirm1'), | |||
| cancelButtonText: this.$t('cancel'), | |||
| type: 'warning', | |||
| lockScroll: false, | |||
| }).then(() => { | |||
| this.offShelfDialogData = data; | |||
| getResSpecificationScenes({ ID: data.ID }).then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| if (res.Data.List.length) { | |||
| this.offShelfDialogShow = true; | |||
| this.offSelfDialogContent = res.Data.List.map((item) => `[${item.ID}]${item.SceneName}`).join(', '); | |||
| } else { | |||
| this.offShelf(); | |||
| } | |||
| } else { | |||
| console.log(res); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| }); | |||
| }).catch(() => { }); | |||
| }, | |||
| offShelf() { | |||
| updateResSpecification({ | |||
| ID: this.offShelfDialogData.ID, | |||
| action: 'off-shelf' | |||
| }).then(res => { | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| this.$message({ | |||
| type: 'success', | |||
| message: this.$t('submittedSuccessfully') | |||
| }); | |||
| this.offShelfDialogShow = false; | |||
| this.getTableData(); | |||
| } else { | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| this.$message({ | |||
| type: 'error', | |||
| message: this.$t('submittedFailed') | |||
| }); | |||
| }); | |||
| }, | |||
| }, | |||
| mounted: function () { | |||
| this.getQueueList(); | |||
| this.getTableData(); | |||
| }, | |||
| beforeDestroy: function () { | |||
| }, | |||
| }; | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .title { | |||
| height: 30px; | |||
| display: flex; | |||
| align-items: center; | |||
| margin-bottom: 5px; | |||
| span { | |||
| font-weight: 700; | |||
| font-size: 16px; | |||
| color: rgb(16, 16, 16); | |||
| } | |||
| } | |||
| .tools-bar { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: space-between; | |||
| margin-bottom: 10px; | |||
| .select { | |||
| margin-right: 10px; | |||
| /deep/ .el-input__inner { | |||
| border-radius: 0; | |||
| } | |||
| } | |||
| } | |||
| .table-container { | |||
| margin-bottom: 16px; | |||
| /deep/ .el-table__header { | |||
| th { | |||
| background: rgb(245, 245, 246); | |||
| font-size: 12px; | |||
| color: rgb(36, 36, 36); | |||
| } | |||
| } | |||
| /deep/ .el-table__body { | |||
| td { | |||
| font-size: 12px; | |||
| } | |||
| } | |||
| .op-btn { | |||
| cursor: pointer; | |||
| font-size: 12px; | |||
| color: rgb(25, 103, 252); | |||
| margin-right: 4px; | |||
| } | |||
| } | |||
| .center { | |||
| display: flex; | |||
| justify-content: center; | |||
| } | |||
| .form { | |||
| margin: 5px 0 5px 0; | |||
| display: flex; | |||
| justify-content: center; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| } | |||
| .form-row { | |||
| display: flex; | |||
| min-height: 42px; | |||
| margin-bottom: 4px; | |||
| .content { | |||
| width: 500px; | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| } | |||
| .btn { | |||
| color: rgb(2, 0, 4); | |||
| background-color: rgb(194, 199, 204); | |||
| border-color: rgb(194, 199, 204); | |||
| &.confirm-btn { | |||
| color: #fff; | |||
| background-color: rgb(56, 158, 13); | |||
| border-color: rgb(56, 158, 13); | |||
| } | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,17 @@ | |||
| import Vue from 'vue'; | |||
| import ElementUI from 'element-ui'; | |||
| import 'element-ui/lib/theme-chalk/index.css'; | |||
| import localeEn from 'element-ui/lib/locale/lang/en'; | |||
| import localeZh from 'element-ui/lib/locale/lang/zh-CN'; | |||
| import { i18n, lang } from '~/langs'; | |||
| import App from './index.vue'; | |||
| Vue.use(ElementUI, { | |||
| locale: lang === 'zh-CN' ? localeZh : localeEn, | |||
| size: 'small', | |||
| }); | |||
| new Vue({ | |||
| i18n, | |||
| render: (h) => h(App), | |||
| }).$mount('#__vue-root'); | |||
| @@ -0,0 +1,148 @@ | |||
| import { formatDate } from 'element-ui/lib/utils/date-util'; | |||
| import { SOURCE_TYPE, CONSUME_STATUS, POINT_ACTIONS, JOB_TYPE } from '~/const'; | |||
| import { i18n } from '~/langs'; | |||
| const getSourceType = (key) => { | |||
| const find = SOURCE_TYPE.filter(item => item.k === key); | |||
| return find.length ? find[0].v : key; | |||
| }; | |||
| const getConsumeStatus = (key) => { | |||
| const find = CONSUME_STATUS.filter(item => item.k === key); | |||
| return find.length ? find[0].v : key; | |||
| }; | |||
| const getPointAction = (key) => { | |||
| const find = POINT_ACTIONS.filter(item => item.k === key); | |||
| return find.length ? find[0].v : key; | |||
| }; | |||
| const getJobType = (key) => { | |||
| const find = JOB_TYPE.filter(item => item.k === key); | |||
| return find.length ? find[0].v : key; | |||
| }; | |||
| const getJobTypeLink = (record, type) => { | |||
| let link = type === 'INCREASE' ? record.Action.RepoLink : '/' + record.Cloudbrain.RepoFullName; | |||
| const cloudbrain = type === 'INCREASE' ? record.Action?.Cloudbrain : record.Cloudbrain; | |||
| switch (cloudbrain?.JobType) { | |||
| case 'DEBUG': | |||
| if (cloudbrain.ComputeResource === 'CPU/GPU') { | |||
| link += `/cloudbrain/${cloudbrain.ID}`; | |||
| } else { | |||
| link += `/modelarts/notebook/${cloudbrain.ID}`; | |||
| } | |||
| break; | |||
| case 'TRAIN': | |||
| if (cloudbrain.Type === 1) { | |||
| link += `/modelarts/train-job/${cloudbrain.JobID}`; | |||
| } else if (cloudbrain.Type === 0) { | |||
| link += `/cloudbrain/train-job/${cloudbrain.JobID}`; | |||
| } else if (cloudbrain.Type === 2) { | |||
| link += `/grampus/train-job/${cloudbrain.JobID}`; | |||
| } | |||
| break; | |||
| case 'INFERENCE': | |||
| link += `/modelarts/inference-job/${cloudbrain.JobID}`; | |||
| break; | |||
| case 'BENCHMARK': | |||
| link += `/cloudbrain/benchmark/${cloudbrain.ID}`; | |||
| break; | |||
| default: | |||
| break; | |||
| }; | |||
| return link; | |||
| }; | |||
| export const getRewardPointRecordInfo = (record) => { | |||
| const out = { | |||
| sn: record.SerialNo, | |||
| date: formatDate(new Date(record.LastOperateDate * 1000), 'yyyy-MM-dd HH:mm:ss'), | |||
| _status: record.Status, | |||
| status: getConsumeStatus(record.Status) || '--', | |||
| statusColor: record.Status === 'OPERATING' ? 'rgb(33, 186, 69)' : '', | |||
| _sourceType: record.SourceType, | |||
| sourceType: getSourceType(record.SourceType), | |||
| duration: record?.Cloudbrain?.Duration || '--', | |||
| taskName: record?.Cloudbrain?.DisplayJobName || '--', | |||
| taskId: record?.Cloudbrain?.ID, | |||
| action: record?.Action?.OpType ? getPointAction(record.Action.OpType) : '--', | |||
| remark: record.Remark, | |||
| amount: record.Amount, | |||
| }; | |||
| if (record.OperateType === 'INCREASE') { | |||
| if (record.SourceType === 'ADMIN_OPERATE') { | |||
| out.remark = record.Remark; | |||
| } else if (record.SourceType === 'ACCOMPLISH_TASK') { | |||
| switch (record?.Action?.OpType) { | |||
| case 1: // 创建公开项目 - 创建了项目OpenI/aiforge | |||
| out.remark = `${i18n.t('createdRepository')}<a href="${record.Action.RepoLink}" rel="nofollow">${record.Action.ShortRepoFullDisplayName}</a>`; | |||
| break; | |||
| case 6: // 每日提出任务 - 创建了任务PCL-Platform.Intelligence/AISynergy#19 | |||
| out.remark = `${i18n.t('openedIssue')}<a href="${record.Action.RepoLink}/issues/${record.Action.IssueInfos[0]}" rel="nofollow">${record.Action.ShortRepoFullDisplayName}#${record.Action.IssueInfos[0]}</a>`; | |||
| break; | |||
| case 7: // 每日提出PR - 创建了合并请求OpenI/aiforge#1 | |||
| out.remark = `${i18n.t('createdPullRequest')}<a href="${record.Action.RepoLink}/pulls/${record.Action.IssueInfos[0]}" rel="nofollow">${record.Action.ShortRepoFullDisplayName}#${record.Action.IssueInfos[0]}</a>`; | |||
| break; | |||
| case 10: // 发表评论 - 评论了任务PCL-Platform.Intelligence/AISynergy#19 | |||
| out.remark = `${i18n.t('commentedOnIssue')}<a href="${record.Action.CommentLink}" rel="nofollow">${record.Action.ShortRepoFullDisplayName}#${record.Action.IssueInfos[0]}</a>`; | |||
| break; | |||
| case 24: // 上传数据集文件 - 上传了数据集文件MMISTData.zip | |||
| out.remark = `${i18n.t('uploadDataset')}<a href="${record.Action.RepoLink}/datasets" rel="nofollow">${record.Action.RefName}</a>`; | |||
| break; | |||
| case 30: // 导入新模型 - 导入了新模型resnet50_qx7l | |||
| out.remark = `${i18n.t('createdNewModel')}<a href="${record.Action.RepoLink}/modelmanage/show_model_info?name=${record.Action.RefName}" rel="nofollow">${record.Action.RefName}</a>`; | |||
| break; | |||
| case 34: // 完成微信扫码验证 - 首次绑定微信奖励 | |||
| out.remark = `${i18n.t('firstBindingWechatRewards')}`; | |||
| break; | |||
| case 35: // 每日运行云脑任务 - 创建了(CPU/GPU/NPU)类型(调试/训练/推理/评测)任务tangl202204131431995 | |||
| out.remark = `${i18n.t('created')}${record.Action?.Cloudbrain?.ComputeResource}${i18n.t('type')}${getJobType(record.Action?.Cloudbrain?.JobType)} <a href="${getJobTypeLink(record, 'INCREASE')}" rel="nofollow">${record.Action.RefName}</a>`; | |||
| break; | |||
| case 36: // 数据集被平台推荐 - 数据集XXX被设置为推荐数据集 | |||
| out.remark = `${i18n.t('dataset')}<a href="${record.Action.RepoLink}/datasets" rel="nofollow">${record.Action.Content && record.Action.Content.split('|')[1]}</a>${i18n.t('setAsRecommendedDataset')}`; | |||
| break; | |||
| case 37: // 提交新公开镜像 - 提交了镜像jiangxiang_ceshi_tang03 | |||
| out.remark = `${i18n.t('committedImage')}<span style="font-weight:bold;">${record.Action.Content && record.Action.Content.split('|')[1]}</span>`; | |||
| break; | |||
| case 38: // 镜像被平台推荐 - 镜像XXX被设置为推荐镜像 | |||
| out.remark = `${i18n.t('image')}<span style="font-weight:bold;">${record.Action.Content && record.Action.Content.split('|')[1]}</span>${i18n.t('setAsRecommendedImage')}`; | |||
| break; | |||
| case 39: // 首次更换头像 - 更新了头像 | |||
| out.remark = `${i18n.t('updatedAvatar')}`; | |||
| break; | |||
| case 40: // 每日commit - 推送了xxxx分支的代码到OpenI/aiforge | |||
| const words = record.Action.RefName.split('/'); | |||
| const branch = words[words.length - 1]; | |||
| out.remark = `${i18n.t('pushedBranch', { | |||
| branch: `<a href="${record.Action.RepoLink}/src/branch/${branch}" rel="nofollow">${branch}</a>` | |||
| })}<a href="${record.Action.RepoLink}" rel="nofollow">${record.Action.ShortRepoFullDisplayName}</a>`; | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } else if (record.SourceType === 'RUN_CLOUDBRAIN_TASK') { | |||
| // | |||
| } | |||
| if (record.LossAmount !== 0) { | |||
| out.amount = record.Amount; | |||
| out.remark += `${out.remark ? i18n.t(';') : ''}${i18n.t('dailyMaxTips')}`; | |||
| } | |||
| } else if (record.OperateType === 'DECREASE') { | |||
| if (record.SourceType === 'ADMIN_OPERATE') { | |||
| out.remark = record.Remark; | |||
| } else if (record.SourceType === 'ACCOMPLISH_TASK') { | |||
| // | |||
| } else if (record.SourceType === 'RUN_CLOUDBRAIN_TASK') { | |||
| out.taskName = `<a href="${getJobTypeLink(record, 'DECREASE')}" rel="nofollow">${record?.Cloudbrain?.DisplayJobName}</a>`; | |||
| if (record?.Cloudbrain?.ComputeResource === 'CPU/GPU') { | |||
| const resourceSpec = record?.Cloudbrain?.ResourceSpec?.ResourceSpec; | |||
| out.remark = `【${getJobType(record?.Cloudbrain?.JobType)}】【${record?.Cloudbrain?.ComputeResource}】【GPU: ${resourceSpec?.gpu}, CPU: ${resourceSpec?.cpu}, ${i18n.t('memory')}: ${(resourceSpec?.memMiB / 1024).toFixed(2)}GB, ${i18n.t('sharedMemory')}: ${(resourceSpec?.shareMemMiB / 1024).toFixed(2)}GB】`; | |||
| } else { | |||
| out.remark = `【${getJobType(record?.Cloudbrain?.JobType)}】【${record?.Cloudbrain?.ComputeResource}】【${record?.Cloudbrain?.ResourceSpec.FlavorInfo.desc}】`; | |||
| } | |||
| } | |||
| } | |||
| return out; | |||
| }; | |||
| @@ -0,0 +1,16 @@ | |||
| import Vue from 'vue'; | |||
| import ElementUI from 'element-ui'; | |||
| import 'element-ui/lib/theme-chalk/index.css'; | |||
| import localeEn from 'element-ui/lib/locale/lang/en'; | |||
| import localeZh from 'element-ui/lib/locale/lang/zh-CN'; | |||
| import { i18n, lang } from '~/langs'; | |||
| import App from './vp-point.vue'; | |||
| Vue.use(ElementUI, { | |||
| locale: lang === 'zh-CN' ? localeZh : localeEn | |||
| }); | |||
| new Vue({ | |||
| i18n, | |||
| render: (h) => h(App), | |||
| }).$mount('#__vue-root'); | |||
| @@ -0,0 +1,308 @@ | |||
| <template> | |||
| <div class="__reward-pointer-c"> | |||
| <div class="ui container" style="width:80%;min-width:1200px;"> | |||
| <div class="__r_p_header"> | |||
| <div> | |||
| <p class="__title">{{ $t('calcPointDetails') }}</p> | |||
| </div> | |||
| <div style="padding: 0 5px; font-size: 14px"> | |||
| <span> | |||
| <i class="question circle icon link" style="color: rgba(3, 102, 214, 1)" data-position="right center" | |||
| data-variation="mini"></i> | |||
| <a href="/reward/point/rule" target="_blank" style="color: rgba(3, 102, 214, 1)">{{ | |||
| $t('calcPointAcquisitionInstructions') | |||
| }}</a> | |||
| </span> | |||
| </div> | |||
| </div> | |||
| <div class="__r_p_summary"> | |||
| <div class="__r_p_summary_item-c __flex-1"> | |||
| <div class="__val">{{ summaryInfo.available }}</div> | |||
| <div class="__exp">{{ $t('CurrAvailableCalcPoints') }}</div> | |||
| </div> | |||
| <div class="__r_p_summary_line"></div> | |||
| <div class="__r_p_summary_item-c __flex-1"> | |||
| <div class="__val">{{ summaryInfo.gain }}</div> | |||
| <div class="__exp">{{ $t('totalGainCalcPoints') }}</div> | |||
| </div> | |||
| <div class="__r_p_summary_item-c __flex-1"> | |||
| <div class="__val">{{ summaryInfo.used }}</div> | |||
| <div class="__exp">{{ $t('totalConsumeCalcPoints') }}</div> | |||
| </div> | |||
| </div> | |||
| <div class="__r_p_tab"> | |||
| <div class="__r_p_tab-item" :class="tabIndex === 0 ? '__focus' : ''" style="border-radius: 5px 0px 0px 5px" | |||
| @click="tabChange(0)"> | |||
| {{ $t('gainDetail') }} | |||
| </div> | |||
| <div class="__r_p_tab-item" :class="tabIndex === 1 ? '__focus' : ''" style="border-radius: 0px 5px 5px 0px" | |||
| @click="tabChange(1)"> | |||
| {{ $t('consumeDetail') }} | |||
| </div> | |||
| </div> | |||
| <div class="__r_p_table"> | |||
| <div v-show="tabIndex === 0"> | |||
| <el-table :data="tableData" row-key="sn" style="width: 100%" v-loading="loading" stripe | |||
| v-if="tableData.length"> | |||
| <el-table-column column-key="sn" prop="sn" :label="$t('serialNumber')" align="center" header-align="center" | |||
| width="180"> | |||
| </el-table-column> | |||
| <el-table-column column-key="date" prop="date" :label="$t('time')" align="center" header-align="center" | |||
| width="180"> | |||
| </el-table-column> | |||
| <el-table-column column-key="sourceType" prop="sourceType" :label="$t('scene')" align="center" | |||
| header-align="center" width="180"></el-table-column> | |||
| <el-table-column column-key="action" prop="action" :label="$t('behaviorOfPoint')" align="center" | |||
| header-align="center" width="200"></el-table-column> | |||
| <el-table-column column-key="remark" prop="remark" :label="$t('explanation')" align="left" min-width="200" | |||
| header-align="center"> | |||
| <template slot-scope="scope"> | |||
| <span v-html="scope.row.remark"></span> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column column-key="amount" prop="amount" :label="$t('points')" align="center" | |||
| header-align="center" width="120"></el-table-column> | |||
| <template slot="empty"> | |||
| <span>{{ loading ? $t('loading') : $t('noData') }}</span> | |||
| </template> | |||
| </el-table> | |||
| <el-empty v-else :image-size="140" :description="$t('noPointGainRecord')"></el-empty> | |||
| </div> | |||
| <div v-show="tabIndex === 1"> | |||
| <el-table :data="tableData" row-key="sn" style="width: 100%" v-loading="loading" stripe | |||
| v-if="tableData.length"> | |||
| <el-table-column column-key="sn" prop="sn" :label="$t('serialNumber')" align="center" header-align="center" | |||
| width="180"> | |||
| </el-table-column> | |||
| <el-table-column column-key="date" prop="date" :label="$t('time')" align="center" header-align="center" | |||
| width="180"> | |||
| </el-table-column> | |||
| <el-table-column column-key="status" prop="status" :label="$t('status')" align="center" | |||
| header-align="center" width="120"> | |||
| <template slot-scope="scope"> | |||
| <span :style="{ color: scope.row.statusColor }">{{ scope.row.status }}</span> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column column-key="sourceType" prop="sourceType" :label="$t('scene')" align="center" | |||
| header-align="center" width="180"></el-table-column> | |||
| <el-table-column column-key="duration" prop="duration" :label="$t('runTime')" align="center" | |||
| header-align="center" width="120"></el-table-column> | |||
| <el-table-column column-key="remark" prop="remark" :label="$t('explanation')" align="left" min-width="200" | |||
| header-align="center"> | |||
| <template slot-scope="scope"> | |||
| <span v-html="scope.row.remark"></span> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column column-key="taskName" prop="taskName" :label="$t('taskName')" align="center" | |||
| header-align="center" width="180"> | |||
| <template slot-scope="scope"> | |||
| <span v-html="scope.row.taskName"></span> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column column-key="amount" prop="amount" :label="$t('points')" align="center" | |||
| header-align="center" width="120"></el-table-column> | |||
| <template slot="empty"> | |||
| <span>{{ loading ? $t('loading') : $t('noData') }}</span> | |||
| </template> | |||
| </el-table> | |||
| <el-empty v-else :image-size="140" :description="$t('noPointConsumeRecord')"></el-empty> | |||
| </div> | |||
| <div class="__r_p_pagination" v-if="tableData.length"> | |||
| <div style="margin-top: 2rem"> | |||
| <div class="center"> | |||
| <el-pagination background @current-change="currentChange" :current-page="pageInfo.curpage" | |||
| :page-sizes="pageInfo.pageSizes" :page-size="pageInfo.pageSize" | |||
| layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total"> | |||
| </el-pagination> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import { getPoint, getPointAccount, getPointList } from "~/apis/modules/point"; | |||
| import { getRewardPointRecordInfo } from './utils'; | |||
| export default { | |||
| data() { | |||
| return { | |||
| loading: false, | |||
| summaryInfo: { | |||
| available: 0, | |||
| gain: 0, | |||
| used: 0, | |||
| }, | |||
| tabIndex: 0, | |||
| tableData: [], | |||
| pageInfo: { | |||
| curpage: 1, | |||
| pageSize: 10, | |||
| pageSizes: [10], | |||
| total: 0, | |||
| }, | |||
| eventSource: null, | |||
| }; | |||
| }, | |||
| components: {}, | |||
| methods: { | |||
| currentChange: function (val) { | |||
| this.pageInfo.curpage = val; | |||
| this.getTableData(); | |||
| }, | |||
| tabChange: function (index) { | |||
| if (this.tabIndex === index) return; | |||
| this.tabIndex = index; | |||
| this.pageInfo.curpage = 1; | |||
| this.pageInfo.total = 0; | |||
| this.getTableData(); | |||
| }, | |||
| getSummaryInfo: function () { | |||
| getPointAccount().then(res => { | |||
| if (res.data && res.data.Code === 0) { | |||
| const data = res.data.Data; | |||
| this.summaryInfo.available = data.Balance; | |||
| this.summaryInfo.gain = data.TotalEarned; | |||
| this.summaryInfo.used = data.TotalConsumed; | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| }) | |||
| }, | |||
| getTableData: function () { | |||
| this.loading = true; | |||
| getPointList({ | |||
| Operate: this.tabIndex === 0 ? 'INCREASE' : 'DECREASE', | |||
| Page: this.pageInfo.curpage, | |||
| // pageSize: this.pageInfo.pageSize, | |||
| }).then((res) => { | |||
| this.loading = false; | |||
| const tableData = []; | |||
| if (res.data && res.data.Code === 0) { | |||
| const data = res.data.Data; | |||
| const records = data.Records; | |||
| for (let i = 0, iLen = records.length; i < iLen; i++) { | |||
| const record = records[i]; | |||
| tableData.push(getRewardPointRecordInfo(record)); | |||
| } | |||
| this.tableData.splice(0, Infinity, ...tableData); | |||
| this.pageInfo.total = data.Total; | |||
| } | |||
| }) | |||
| .catch((err) => { | |||
| console.log(err); | |||
| this.loading = false; | |||
| this.tableData.splice(0, Infinity); | |||
| }); | |||
| }, | |||
| }, | |||
| mounted: function () { | |||
| this.getSummaryInfo(); | |||
| this.getTableData(); | |||
| const { AppSubUrl, csrf, NotificationSettings } = window.config; | |||
| if (NotificationSettings.EventSourceUpdateTime > 0 && !!window.EventSource) { | |||
| const source = new EventSource(`${AppSubUrl}/user/events`); | |||
| source.addEventListener('reward-operation', (e) => { | |||
| try { | |||
| this.getSummaryInfo(); | |||
| this.getTableData(); | |||
| } catch (err) { | |||
| console.error(err); | |||
| } | |||
| }); | |||
| this.eventSource = source; | |||
| } | |||
| }, | |||
| beforeDestroy: function () { | |||
| this.eventSource && this.eventSource.close(); | |||
| }, | |||
| }; | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .__flex-1 { | |||
| flex: 1; | |||
| } | |||
| .__reward-pointer-c { | |||
| .__r_p_header { | |||
| height: 30px; | |||
| margin: 10px 0; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: space-between; | |||
| .__title { | |||
| font-weight: 400; | |||
| font-size: 18px; | |||
| color: rgb(16, 16, 16); | |||
| line-height: 26px; | |||
| } | |||
| } | |||
| .__r_p_summary { | |||
| display: flex; | |||
| align-items: center; | |||
| height: 100px; | |||
| background-color: rgb(245, 245, 246); | |||
| .__r_p_summary_item-c { | |||
| .__val { | |||
| text-align: center; | |||
| margin: 12px 0; | |||
| font-weight: 400; | |||
| font-size: 28px; | |||
| color: rgb(16, 16, 16); | |||
| } | |||
| .__exp { | |||
| text-align: center; | |||
| font-weight: 400; | |||
| font-size: 14px; | |||
| color: rgba(54, 56, 64, 1); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .__r_p_summary_line { | |||
| width: 1px; | |||
| height: 80%; | |||
| background-color: rgb(212, 212, 213); | |||
| } | |||
| .__r_p_tab { | |||
| display: flex; | |||
| margin: 18px 0; | |||
| .__r_p_tab-item { | |||
| width: 115px; | |||
| height: 38px; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| border: 1px solid rgb(225, 227, 230); | |||
| color: #101010; | |||
| box-sizing: border-box; | |||
| cursor: pointer; | |||
| &.__focus { | |||
| border-color: rgb(50, 145, 248); | |||
| color: rgb(50, 145, 248); | |||
| cursor: default; | |||
| } | |||
| } | |||
| } | |||
| .__r_p_table { | |||
| /deep/ .el-table__header { | |||
| th { | |||
| background: rgb(245, 245, 246); | |||
| color: rgb(96, 98, 102); | |||
| font-weight: 400; | |||
| font-size: 14px; | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,7 @@ | |||
| export const getListValueWithKey = (list, key, k = 'k', v = 'v') => { | |||
| for (let i = 0, iLen = list.length; i < iLen; i++) { | |||
| const listI = list[i]; | |||
| if (listI[k] === key) return listI[v]; | |||
| } | |||
| return ''; | |||
| }; | |||
| @@ -29,6 +29,11 @@ for (const path of stadalonePaths) { | |||
| standalone[parse(path).name] = [path]; | |||
| } | |||
| const vuePages = {}; | |||
| for (const path of glob('web_src/vuepages/**/vp-*.js')) { | |||
| vuePages[parse(path).name] = [path]; | |||
| } | |||
| const isProduction = process.env.NODE_ENV !== 'development'; | |||
| module.exports = { | |||
| @@ -44,6 +49,7 @@ module.exports = { | |||
| icons: glob('node_modules/@primer/octicons/build/svg/**/*.svg'), | |||
| ...standalone, | |||
| ...themes, | |||
| ...vuePages, | |||
| }, | |||
| devtool: false, | |||
| output: { | |||
| @@ -267,6 +273,7 @@ module.exports = { | |||
| symlinks: false, | |||
| alias: { | |||
| vue$: 'vue/dist/vue.esm.js', // needed because vue's default export is the runtime only | |||
| '~': resolve(__dirname, 'web_src/vuepages'), | |||
| }, | |||
| extensions: ['.tsx', '.ts', '.js'] | |||
| }, | |||
| @@ -29,6 +29,11 @@ for (const path of stadalonePaths) { | |||
| standalone[parse(path).name] = [path]; | |||
| } | |||
| const vuePages = {}; | |||
| for (const path of glob('web_src/vuepages/**/vp-*.js')) { | |||
| vuePages[parse(path).name] = [path]; | |||
| } | |||
| const isProduction = process.env.NODE_ENV !== 'development'; | |||
| module.exports = { | |||
| @@ -44,6 +49,7 @@ module.exports = { | |||
| icons: glob('node_modules/@primer/octicons/build/svg/**/*.svg'), | |||
| ...standalone, | |||
| ...themes, | |||
| ...vuePages | |||
| }, | |||
| devtool: false, | |||
| output: { | |||
| @@ -267,6 +273,7 @@ module.exports = { | |||
| symlinks: false, | |||
| alias: { | |||
| vue$: 'vue/dist/vue.esm.js', // needed because vue's default export is the runtime only | |||
| '~': resolve(__dirname, 'web_src/vuepages'), | |||
| }, | |||
| extensions: ['.tsx', '.ts', '.js'] | |||
| }, | |||