| @@ -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" //停止中 | ModelArtsStopping ModelArtsJobStatus = "STOPPING" //停止中 | ||||
| ModelArtsStopped ModelArtsJobStatus = "STOPPED" //停止 | ModelArtsStopped ModelArtsJobStatus = "STOPPED" //停止 | ||||
| ModelArtsUnavailable ModelArtsJobStatus = "UNAVAILABLE" //故障 | ModelArtsUnavailable ModelArtsJobStatus = "UNAVAILABLE" //故障 | ||||
| ModelArtsDeleting ModelArtsJobStatus = "DELETING" //删除中 | |||||
| ModelArtsDeleted ModelArtsJobStatus = "DELETED" //已删除 | ModelArtsDeleted ModelArtsJobStatus = "DELETED" //已删除 | ||||
| ModelArtsResizing ModelArtsJobStatus = "RESIZING" //规格变更中 | ModelArtsResizing ModelArtsJobStatus = "RESIZING" //规格变更中 | ||||
| ModelArtsResizFailed ModelArtsJobStatus = "RESIZE_FAILED" //规格变更失败 | ModelArtsResizFailed ModelArtsJobStatus = "RESIZE_FAILED" //规格变更失败 | ||||
| @@ -111,6 +112,16 @@ const ( | |||||
| GrampusStatusWaiting = "WAITING" | GrampusStatusWaiting = "WAITING" | ||||
| ) | ) | ||||
| const ( | |||||
| //cluster | |||||
| OpenICluster = "OpenI" | |||||
| C2NetCluster = "C2Net" | |||||
| //AI center | |||||
| AICenterOfCloudBrainOne = "OpenIOne" | |||||
| AICenterOfCloudBrainTwo = "OpenITwo" | |||||
| ) | |||||
| type Cloudbrain struct { | type Cloudbrain struct { | ||||
| ID int64 `xorm:"pk autoincr"` | ID int64 `xorm:"pk autoincr"` | ||||
| JobID string `xorm:"INDEX NOT NULL"` | JobID string `xorm:"INDEX NOT NULL"` | ||||
| @@ -1082,6 +1093,7 @@ type DatasetDownload struct { | |||||
| DatasetName string `json:"dataset_name"` | DatasetName string `json:"dataset_name"` | ||||
| DatasetDownloadLink string `json:"dataset_download_link"` | DatasetDownloadLink string `json:"dataset_download_link"` | ||||
| RepositoryLink string `json:"repository_link"` | RepositoryLink string `json:"repository_link"` | ||||
| IsDelete bool `json:"is_delete"` | |||||
| } | } | ||||
| type DataSource struct { | type DataSource struct { | ||||
| @@ -1338,6 +1350,34 @@ type GrampusSpec struct { | |||||
| Name string `json:"name"` | Name string `json:"name"` | ||||
| ProcessorType string `json:"processorType"` | ProcessorType string `json:"processorType"` | ||||
| Centers []Center `json:"centers"` | 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 { | type GetGrampusResourceSpecsResult struct { | ||||
| @@ -1345,6 +1385,12 @@ type GetGrampusResourceSpecsResult struct { | |||||
| Infos []GrampusSpec `json:"resourceSpecs"` | Infos []GrampusSpec `json:"resourceSpecs"` | ||||
| } | } | ||||
| type GetGrampusAiCentersResult struct { | |||||
| GrampusResult | |||||
| Infos []GrampusAiCenter `json:"aiCenterInfos"` | |||||
| TotalSize int `json:"totalSize"` | |||||
| } | |||||
| type GrampusImage struct { | type GrampusImage struct { | ||||
| CreatedAt int64 `json:"createdAt"` | CreatedAt int64 `json:"createdAt"` | ||||
| UpdatedAt int64 `json:"updatedAt"` | UpdatedAt int64 `json:"updatedAt"` | ||||
| @@ -1889,9 +1935,9 @@ func GetCloudbrainCountByUserID(userID int64, jobType string) (int, error) { | |||||
| func GetCloudbrainRunCountByRepoID(repoID int64) (int, error) { | func GetCloudbrainRunCountByRepoID(repoID int64) (int, error) { | ||||
| count, err := x.In("status", JobWaiting, JobRunning, ModelArtsCreateQueue, ModelArtsCreating, ModelArtsStarting, | 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 | return int(count), err | ||||
| } | } | ||||
| @@ -121,22 +121,20 @@ func (datasets DatasetList) loadAttachmentAttributes(opts *SearchDatasetOptions) | |||||
| for i := range datasets { | for i := range datasets { | ||||
| if attachment.DatasetID == datasets[i].ID { | 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] | permission, ok := permissionMap[datasets[i].ID] | ||||
| if !ok { | if !ok { | ||||
| permission = false | permission = false | ||||
| datasets[i].Repo.GetOwner() | 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 { | if !permission { | ||||
| isCollaborator, _ := datasets[i].Repo.IsCollaborator(opts.User.ID) | 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.") | log.Info("Collaborator user may visit the attach.") | ||||
| permission = true | permission = true | ||||
| } | } | ||||
| @@ -147,11 +145,7 @@ func (datasets DatasetList) loadAttachmentAttributes(opts *SearchDatasetOptions) | |||||
| if permission { | if permission { | ||||
| datasets[i].Attachments = append(datasets[i].Attachments, attachment) | 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 { | 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 | ListOptions | ||||
| SearchOrderBy | SearchOrderBy | ||||
| IsOwner bool | IsOwner bool | ||||
| @@ -240,6 +235,10 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond { | |||||
| cond = cond.And(builder.Eq{"dataset.repo_id": opts.RepoID}) | 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 { | if opts.PublicOnly { | ||||
| cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | ||||
| cond = cond.And(builder.Eq{"attachment.is_private": false}) | cond = cond.And(builder.Eq{"attachment.is_private": false}) | ||||
| @@ -145,6 +145,11 @@ func init() { | |||||
| new(OrgStatistic), | new(OrgStatistic), | ||||
| new(SearchRecord), | new(SearchRecord), | ||||
| new(AiModelConvert), | new(AiModelConvert), | ||||
| new(ResourceQueue), | |||||
| new(ResourceSpecification), | |||||
| new(ResourceScene), | |||||
| new(ResourceSceneSpec), | |||||
| new(AdminOperateLog), | |||||
| new(CloudbrainTemp), | new(CloudbrainTemp), | ||||
| new(DatasetReference), | new(DatasetReference), | ||||
| ) | ) | ||||
| @@ -130,6 +130,20 @@ func (repo *Repository) IsCollaborator(userID int64) (bool, error) { | |||||
| return repo.isCollaborator(x, userID) | 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 { | func (repo *Repository) changeCollaborationAccessMode(e Engine, uid int64, mode AccessMode) error { | ||||
| // Discard invalid input | // Discard invalid input | ||||
| if mode <= AccessModeNone || mode > AccessModeOwner { | 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 ( | 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"` | //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";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" | CodeMountPath = "/code" | ||||
| DataSetMountPath = "/dataset" | DataSetMountPath = "/dataset" | ||||
| ModelMountPath = "/model" | ModelMountPath = "/model" | ||||
| @@ -30,8 +30,8 @@ const ( | |||||
| Snn4imagenetMountPath = "/snn4imagenet" | Snn4imagenetMountPath = "/snn4imagenet" | ||||
| BrainScoreMountPath = "/brainscore" | BrainScoreMountPath = "/brainscore" | ||||
| TaskInfoName = "/taskInfo" | 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" | SubTaskName = "task1" | ||||
| @@ -93,7 +93,7 @@ sendjob: | |||||
| return nil, fmt.Errorf("resty get queues detail failed: %s", err) | 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++ | retry++ | ||||
| _ = loginCloudbrain() | _ = loginCloudbrain() | ||||
| goto sendjob | goto sendjob | ||||
| @@ -5,6 +5,7 @@ | |||||
| package cron | package cron | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/services/cloudbrain/resource" | |||||
| "code.gitea.io/gitea/modules/modelarts" | "code.gitea.io/gitea/modules/modelarts" | ||||
| "context" | "context" | ||||
| "time" | "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() { | func registerSyncModelArtsTempJobs() { | ||||
| RegisterTaskFatal("sync_model_arts_temp_jobs", &BaseConfig{ | RegisterTaskFatal("sync_model_arts_temp_jobs", &BaseConfig{ | ||||
| Enabled: true, | Enabled: true, | ||||
| @@ -239,5 +251,6 @@ func initBasicTasks() { | |||||
| registerSyncCloudbrainStatus() | registerSyncCloudbrainStatus() | ||||
| registerHandleOrgStatistic() | registerHandleOrgStatistic() | ||||
| registerSyncResourceSpecs() | |||||
| registerSyncModelArtsTempJobs() | registerSyncModelArtsTempJobs() | ||||
| } | } | ||||
| @@ -24,6 +24,7 @@ const ( | |||||
| urlGetToken = urlOpenApiV1 + "token" | urlGetToken = urlOpenApiV1 + "token" | ||||
| urlTrainJob = urlOpenApiV1 + "trainjob" | urlTrainJob = urlOpenApiV1 + "trainjob" | ||||
| urlGetResourceSpecs = urlOpenApiV1 + "resourcespec" | urlGetResourceSpecs = urlOpenApiV1 + "resourcespec" | ||||
| urlGetAiCenter = urlOpenApiV1 + "sharescreen/aicenter" | |||||
| urlGetImages = urlOpenApiV1 + "image" | urlGetImages = urlOpenApiV1 + "image" | ||||
| errorIllegalToken = 1005 | errorIllegalToken = 1005 | ||||
| @@ -276,3 +277,35 @@ sendjob: | |||||
| return &result, nil | 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 | FlavorInfos *models.FlavorInfos | ||||
| ImageInfos *models.ImageInfosModelArts | ImageInfos *models.ImageInfosModelArts | ||||
| TrainFlavorInfos *Flavor | TrainFlavorInfos *Flavor | ||||
| SpecialPools *models.SpecialPools | |||||
| SpecialPools *models.SpecialPools | |||||
| MultiNodeConfig *MultiNodes | |||||
| ) | ) | ||||
| type GenerateTrainJobReq struct { | type GenerateTrainJobReq struct { | ||||
| @@ -166,6 +167,14 @@ type ResourcePool struct { | |||||
| } `json:"resource_pool"` | } `json:"resource_pool"` | ||||
| } | } | ||||
| type MultiNodes struct{ | |||||
| Info []OrgMultiNode `json:"multinode"` | |||||
| } | |||||
| type OrgMultiNode struct{ | |||||
| Org string `json:"org"` | |||||
| Node []int `json:"node"` | |||||
| } | |||||
| // type Parameter struct { | // type Parameter struct { | ||||
| // Label string `json:"label"` | // Label string `json:"label"` | ||||
| // Value string `json:"value"` | // 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 { | func HandleTrainJobInfo(task *models.Cloudbrain) error { | ||||
| result, err := GetTrainJob(task.JobID, strconv.FormatInt(task.VersionID, 10)) | result, err := GetTrainJob(task.JobID, strconv.FormatInt(task.VersionID, 10)) | ||||
| @@ -547,6 +547,7 @@ var ( | |||||
| FlavorInfos string | FlavorInfos string | ||||
| TrainJobFLAVORINFOS string | TrainJobFLAVORINFOS string | ||||
| ModelArtsSpecialPools string | ModelArtsSpecialPools string | ||||
| ModelArtsMultiNode string | |||||
| //grampus config | //grampus config | ||||
| Grampus = struct { | Grampus = struct { | ||||
| @@ -1432,6 +1433,7 @@ func NewContext() { | |||||
| FlavorInfos = sec.Key("FLAVOR_INFOS").MustString("") | FlavorInfos = sec.Key("FLAVOR_INFOS").MustString("") | ||||
| TrainJobFLAVORINFOS = sec.Key("TrainJob_FLAVOR_INFOS").MustString("") | TrainJobFLAVORINFOS = sec.Key("TrainJob_FLAVOR_INFOS").MustString("") | ||||
| ModelArtsSpecialPools = sec.Key("SPECIAL_POOL").MustString("") | ModelArtsSpecialPools = sec.Key("SPECIAL_POOL").MustString("") | ||||
| ModelArtsMultiNode=sec.Key("MULTI_NODE").MustString("") | |||||
| sec = Cfg.Section("elk") | sec = Cfg.Section("elk") | ||||
| ElkUrl = sec.Key("ELKURL").MustString("") | 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_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_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_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 = Upload | ||||
| dataset_upload_status= Upload Status | dataset_upload_status= Upload Status | ||||
| dataset_file_name = File Name | dataset_file_name = File Name | ||||
| @@ -959,6 +959,7 @@ unfavorite=Unlike | |||||
| favorite=Like | favorite=Like | ||||
| disassociate=Disassociate | disassociate=Disassociate | ||||
| benchmark_dataset_tip=Note: first use the dataset function to upload the model, and then select the model from the dataset list. | 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] | [repo] | ||||
| owner = Owner | owner = Owner | ||||
| @@ -1140,7 +1141,7 @@ modelarts.train_job.compute_node=Compute Node | |||||
| modelarts.create_model = Create Model | modelarts.create_model = Create Model | ||||
| modelarts.model_label=Model Label | modelarts.model_label=Model Label | ||||
| modelarts.infer_dataset = Inference Dataset | 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.basic_info=Basic Info | ||||
| modelarts.train_job.job_status=Job Status | 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.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.infer_job.tooltip = The model has been deleted and cannot be viewed. | ||||
| modelarts.download_log=Download log file | 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 | 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 ; | 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; | 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; | 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 ; | 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 | benchmark_leaderboards = Benchmark leaderboards | ||||
| @@ -1244,11 +1249,11 @@ model.convert=Model Transformation | |||||
| model.list=Model List | model.list=Model List | ||||
| model.manage.create_new_convert_task=Create Model Transformation Task | 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.init1=Code version: You have not initialized the code repository, please | ||||
| model.manage.init2=initialized first ; | 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_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=Delete Model | ||||
| model.manage.delete_confirm=Are you sure to delete this model? Once this model is deleted, it cannot be restored. | 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 | model.manage.select.trainjob=Select train task | ||||
| @@ -1260,9 +1265,9 @@ model.manage.modellabel=Model label | |||||
| model.manage.modeldesc=Model description | model.manage.modeldesc=Model description | ||||
| model.manage.baseinfo=Base Information | model.manage.baseinfo=Base Information | ||||
| modelconvert.notcreate=No model conversion task has been created. | 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.download=Download | ||||
| modelconvert.taskname=Task name | modelconvert.taskname=Task name | ||||
| modelconvert.modelname=Model name | modelconvert.modelname=Model name | ||||
| @@ -3024,6 +3029,13 @@ notices.desc = Description | |||||
| notices.op = Op. | notices.op = Op. | ||||
| notices.delete_success = The system notices have been deleted. | 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] | [action] | ||||
| create_repo = created repository <a href="%s">%s</a> | 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> | rename_repo = renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a> | ||||
| @@ -965,6 +965,7 @@ unfavorite=取消收藏 | |||||
| favorite=收藏 | favorite=收藏 | ||||
| disassociate=取消关联 | disassociate=取消关联 | ||||
| benchmark_dataset_tip=说明:先使用数据集功能上传模型,然后从数据集列表选模型。 | benchmark_dataset_tip=说明:先使用数据集功能上传模型,然后从数据集列表选模型。 | ||||
| file_deleted=文件已经被删除 | |||||
| [repo] | [repo] | ||||
| owner=拥有者 | 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.boot_file_helper=启动文件是您程序执行的入口文件,必须是以.py结尾的文件。比如inference.py、main.py、example/inference.py、case/main.py。 | ||||
| modelarts.infer_job.tooltip = 该模型已删除,无法查看。 | modelarts.infer_job.tooltip = 该模型已删除,无法查看。 | ||||
| modelarts.download_log=下载日志文件 | modelarts.download_log=下载日志文件 | ||||
| modelarts.no_node_right = 计算节点数的值配置错误,您没有权限使用当前配置的计算节点数。 | |||||
| debug_task_not_created = 未创建过调试任务 | debug_task_not_created = 未创建过调试任务 | ||||
| @@ -1235,6 +1237,10 @@ repo_not_initialized = 代码版本:您还没有初始化代码仓库,请先 | |||||
| debug_task_running_limit = 运行时长:最长不超过4个小时,超过4个小时将自动停止; | debug_task_running_limit = 运行时长:最长不超过4个小时,超过4个小时将自动停止; | ||||
| dataset_desc = 数据集:云脑1提供 CPU / GPU 资源,云脑2提供 Ascend NPU 资源,调试使用的数据集也需要上传到对应的环境; | dataset_desc = 数据集:云脑1提供 CPU / GPU 资源,云脑2提供 Ascend NPU 资源,调试使用的数据集也需要上传到对应的环境; | ||||
| platform_instructions = 使用说明:可以参考启智AI协作平台<a href="https://git.openi.org.cn/zeizei/OpenI_Learning">小白训练营课程</a>。 | 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> ; | model_not_exist = 模型文件:您还没有模型文件,请先通过<a href="%s/modelarts/train-job">训练任务</a>产生并 <a href="%s/modelmanage/show_model">导出模型</a> ; | ||||
| benchmark_leaderboards = 基准测试排行榜 | benchmark_leaderboards = 基准测试排行榜 | ||||
| @@ -1261,7 +1267,7 @@ model.manage.notcreatemodel=未创建过模型 | |||||
| model.manage.init1=代码版本:您还没有初始化代码仓库,请先 | model.manage.init1=代码版本:您还没有初始化代码仓库,请先 | ||||
| model.manage.init2=创建代码版本; | model.manage.init2=创建代码版本; | ||||
| model.manage.createtrainjob_tip=训练任务:您还没创建过训练任务,请先创建 | model.manage.createtrainjob_tip=训练任务:您还没创建过训练任务,请先创建 | ||||
| model.manage.createtrainjob=训练任务 | |||||
| model.manage.createtrainjob=训练任务。 | |||||
| model.manage.delete=删除模型 | model.manage.delete=删除模型 | ||||
| model.manage.delete_confirm=你确认删除该模型么?此模型一旦删除不可恢复。 | model.manage.delete_confirm=你确认删除该模型么?此模型一旦删除不可恢复。 | ||||
| model.manage.select.trainjob=选择训练任务 | model.manage.select.trainjob=选择训练任务 | ||||
| @@ -1274,7 +1280,7 @@ model.manage.modeldesc=模型描述 | |||||
| model.manage.baseinfo=基本信息 | model.manage.baseinfo=基本信息 | ||||
| modelconvert.notcreate=未创建过模型转换任务 | modelconvert.notcreate=未创建过模型转换任务 | ||||
| modelconvert.importfirst1=请您先导入 | modelconvert.importfirst1=请您先导入 | ||||
| modelconvert.importfirst2=模型下载 | |||||
| modelconvert.importfirst2=模型 | |||||
| modelconvert.importfirst3=,然后再对其进行转换。 | modelconvert.importfirst3=,然后再对其进行转换。 | ||||
| modelconvert.download=下载 | modelconvert.download=下载 | ||||
| modelconvert.taskname=任务名称 | modelconvert.taskname=任务名称 | ||||
| @@ -3040,6 +3046,13 @@ notices.desc=提示描述 | |||||
| notices.op=操作 | notices.op=操作 | ||||
| notices.delete_success=系统通知已被删除。 | notices.delete_success=系统通知已被删除。 | ||||
| user_management = 用户管理 | |||||
| resource_management = 资源管理 | |||||
| resource_pool = 资源池(队列) | |||||
| resource_price = 资源规格单价 | |||||
| application_scenario = 应用场景 | |||||
| system_configuration = 系统配置 | |||||
| [action] | [action] | ||||
| create_repo=创建了项目 <a href="%s">%s</a> | create_repo=创建了项目 <a href="%s">%s</a> | ||||
| rename_repo=重命名项目 <code>%[1]s</code> 为 <a href="%[2]s">%[3]s</a> | rename_repo=重命名项目 <code>%[1]s</code> 为 <a href="%[2]s">%[3]s</a> | ||||
| @@ -54,6 +54,7 @@ | |||||
| "vue": "2.6.11", | "vue": "2.6.11", | ||||
| "vue-bar-graph": "1.2.0", | "vue-bar-graph": "1.2.0", | ||||
| "vue-calendar-heatmap": "0.8.4", | "vue-calendar-heatmap": "0.8.4", | ||||
| "vue-i18n": "6.1.3", | |||||
| "vue-loader": "15.9.2", | "vue-loader": "15.9.2", | ||||
| "vue-router": "3.3.4", | "vue-router": "3.3.4", | ||||
| "vue-template-compiler": "2.6.11", | "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) { | func CloudbrainGetLog(ctx *context.Context) { | ||||
| ID := ctx.Params(":id") | 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) | job, err := models.GetCloudbrainByID(ID) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetCloudbrainByJobName failed: %v", err, ctx.Data["MsgID"]) | log.Error("GetCloudbrainByJobName failed: %v", err, ctx.Data["MsgID"]) | ||||
| ctx.ServerError(err.Error(), err) | ctx.ServerError(err.Error(), err) | ||||
| return | 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{}{ | re := map[string]interface{}{ | ||||
| "JobID": ID, | "JobID": ID, | ||||
| "LogFileName": result["FileName"], | "LogFileName": result["FileName"], | ||||
| "StartLine": startLine, | |||||
| "EndLine": result["endLine"], | |||||
| "StartLine": result["StartLine"], | |||||
| "EndLine": result["EndLine"], | |||||
| "Content": result["Content"], | "Content": result["Content"], | ||||
| "Lines": result["lines"], | |||||
| "Lines": result["Lines"], | |||||
| "CanLogDownload": result["FileName"] != "", | "CanLogDownload": result["FileName"] != "", | ||||
| } | } | ||||
| //result := CloudbrainGetLogByJobId(job.JobID, job.JobName) | //result := CloudbrainGetLogByJobId(job.JobID, job.JobName) | ||||
| ctx.JSON(http.StatusOK, re) | 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, "") | files, err := storage.GetOneLevelAllObjectUnderDirMinio(setting.Attachment.Minio.Bucket, prefix, "") | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("query cloudbrain model failed: %v", err) | log.Error("query cloudbrain model failed: %v", err) | ||||
| return nil | 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 := "" | re := "" | ||||
| fileName := "" | fileName := "" | ||||
| count := 0 | count := 0 | ||||
| @@ -458,7 +565,7 @@ func getLogFromModelDir(jobName string, startLine int, endLine int) map[string]i | |||||
| for _, file := range files { | for _, file := range files { | ||||
| if strings.HasSuffix(file.FileName, "log.txt") { | if strings.HasSuffix(file.FileName, "log.txt") { | ||||
| fileName = file.FileName | fileName = file.FileName | ||||
| path := storage.GetMinioPath(jobName+"/model/", file.FileName) | |||||
| path := storage.GetMinioPath(jobName+resultPath+"/", file.FileName) | |||||
| log.Info("path=" + path) | log.Info("path=" + path) | ||||
| reader, err := os.Open(path) | reader, err := os.Open(path) | ||||
| defer reader.Close() | defer reader.Close() | ||||
| @@ -467,7 +574,6 @@ func getLogFromModelDir(jobName string, startLine int, endLine int) map[string]i | |||||
| for i := 0; i < endLine; i++ { | for i := 0; i < endLine; i++ { | ||||
| line, error := r.ReadString('\n') | line, error := r.ReadString('\n') | ||||
| log.Info("line=" + line) | log.Info("line=" + line) | ||||
| fileEndLine = i | |||||
| if error == io.EOF { | if error == io.EOF { | ||||
| log.Info("read file completed.") | log.Info("read file completed.") | ||||
| break | break | ||||
| @@ -478,11 +584,13 @@ func getLogFromModelDir(jobName string, startLine int, endLine int) map[string]i | |||||
| } | } | ||||
| if error == nil { | if error == nil { | ||||
| if i >= startLine { | if i >= startLine { | ||||
| fileEndLine = i | |||||
| re = re + line | re = re + line | ||||
| count++ | count++ | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| fileEndLine = fileEndLine + 1 | |||||
| } else { | } else { | ||||
| log.Info("error:" + err.Error()) | log.Info("error:" + err.Error()) | ||||
| } | } | ||||
| @@ -491,11 +599,12 @@ func getLogFromModelDir(jobName string, startLine int, endLine int) map[string]i | |||||
| } | } | ||||
| return map[string]interface{}{ | 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 | ctx.Data["task"] = task | ||||
| labelName := strings.Fields(task.LabelName) | labelName := strings.Fields(task.LabelName) | ||||
| ctx.Data["LabelName"] = 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) | ctx.RenderWithErr(ctx.Tr("cloudbrain.error.dataset_select"), tplCloudBrainBenchmarkNew, &form) | ||||
| return | return | ||||
| } | } | ||||
| log.Info("Command=" + command) | |||||
| log.Info("ModelPath=" + storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/")) | |||||
| req := cloudbrain.GenerateCloudBrainTaskReq{ | req := cloudbrain.GenerateCloudBrainTaskReq{ | ||||
| Ctx: ctx, | Ctx: ctx, | ||||
| DisplayJobName: displayJobName, | DisplayJobName: displayJobName, | ||||
| @@ -2560,7 +2561,8 @@ func ModelBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm) | |||||
| ctx.RenderWithErr(ctx.Tr("cloudbrain.error.dataset_select"), tpl, &form) | ctx.RenderWithErr(ctx.Tr("cloudbrain.error.dataset_select"), tpl, &form) | ||||
| return | return | ||||
| } | } | ||||
| log.Info("Command=" + command) | |||||
| log.Info("ModelPath=" + storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/")) | |||||
| req := cloudbrain.GenerateCloudBrainTaskReq{ | req := cloudbrain.GenerateCloudBrainTaskReq{ | ||||
| Ctx: ctx, | Ctx: ctx, | ||||
| DisplayJobName: displayJobName, | DisplayJobName: displayJobName, | ||||
| @@ -2689,7 +2691,7 @@ func getInferenceJobCommand(form auth.CreateCloudBrainInferencForm) (string, err | |||||
| param += " --modelname" + "=" + form.CkptName | 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 | return command, nil | ||||
| } | } | ||||
| @@ -45,15 +45,10 @@ func newFilterPrivateAttachments(ctx *context.Context, list []*models.Attachment | |||||
| repo.GetOwner() | repo.GetOwner() | ||||
| } | } | ||||
| permission := false | 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 { | if !permission && ctx.User != nil { | ||||
| isCollaborator, _ := repo.IsCollaborator(ctx.User.ID) | 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.") | log.Info("Collaborator user may visit the attach.") | ||||
| permission = true | permission = true | ||||
| } | } | ||||
| @@ -529,6 +524,10 @@ func ReferenceDatasetAvailable(ctx *context.Context) { | |||||
| NeedAttachment: false, | NeedAttachment: false, | ||||
| CloudBrainType: models.TypeCloudBrainAll, | CloudBrainType: models.TypeCloudBrainAll, | ||||
| } | } | ||||
| dataset, _ := models.GetDatasetByRepo(&models.Repository{ID: ctx.Repo.Repository.ID}) | |||||
| if dataset != nil { | |||||
| opts.ExcludeDatasetId = dataset.ID | |||||
| } | |||||
| datasetMultiple(ctx, opts) | datasetMultiple(ctx, opts) | ||||
| } | } | ||||
| @@ -713,7 +713,7 @@ func GrampusTrainJobShow(ctx *context.Context) { | |||||
| taskList := make([]*models.Cloudbrain, 0) | taskList := make([]*models.Cloudbrain, 0) | ||||
| taskList = append(taskList, task) | taskList = append(taskList, task) | ||||
| ctx.Data["version_list_task"] = taskList | 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["canDownload"] = cloudbrain.CanModifyJob(ctx, task) | ||||
| ctx.Data["displayJobName"] = task.DisplayJobName | ctx.Data["displayJobName"] = task.DisplayJobName | ||||
| @@ -285,7 +285,7 @@ func NotebookShow(ctx *context.Context) { | |||||
| datasetDownload := make([]models.DatasetDownload, 0) | datasetDownload := make([]models.DatasetDownload, 0) | ||||
| if ctx.IsSigned { | if ctx.IsSigned { | ||||
| if task.Uuid != "" && task.UserID == ctx.User.ID { | 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) | user, err := models.GetUserByID(task.UserID) | ||||
| @@ -331,36 +331,52 @@ func NotebookShow(ctx *context.Context) { | |||||
| ctx.HTML(200, tplModelArtsNotebookShow) | 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) | datasetDownload := make([]models.DatasetDownload, 0) | ||||
| if len(uuid) == 0 { | |||||
| return datasetDownload | |||||
| } | |||||
| uuidList := strings.Split(uuid, ";") | 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) | attachment, err := models.GetAttachmentByUUID(uuidStr) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetAttachmentByUUID failed:%v", err.Error()) | 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{ | datasetDownload = append(datasetDownload, models.DatasetDownload{ | ||||
| DatasetName: attachment.Name, | |||||
| DatasetName: name, | |||||
| DatasetDownloadLink: url, | DatasetDownloadLink: url, | ||||
| RepositoryLink: repo.Link() + "/datasets", | |||||
| RepositoryLink: link, | |||||
| IsDelete: isDelete, | |||||
| }) | }) | ||||
| } | } | ||||
| log.Info("dataset length=" + fmt.Sprint(len(datasetDownload))) | |||||
| return datasetDownload | return datasetDownload | ||||
| } | } | ||||
| @@ -747,9 +763,23 @@ func trainJobNewDataPrepare(ctx *context.Context) error { | |||||
| waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") | waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") | ||||
| ctx.Data["WaitCount"] = waitCount | ctx.Data["WaitCount"] = waitCount | ||||
| setMultiNodeIfConfigureMatch(ctx) | |||||
| return nil | 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) { | func setSpecBySpecialPoolConfig(ctx *context.Context, jobType string) { | ||||
| modelarts.InitSpecialPool() | modelarts.InitSpecialPool() | ||||
| @@ -864,6 +894,7 @@ func trainJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModelArts | |||||
| ctx.Data["datasetType"] = models.TypeCloudBrainTwo | ctx.Data["datasetType"] = models.TypeCloudBrainTwo | ||||
| waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") | waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") | ||||
| ctx.Data["WaitCount"] = waitCount | ctx.Data["WaitCount"] = waitCount | ||||
| setMultiNodeIfConfigureMatch(ctx) | |||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -949,14 +980,17 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error { | |||||
| } | } | ||||
| _, _, datasetNames, _, err := getDatasUrlListByUUIDS(task.Uuid) | _, _, datasetNames, _, err := getDatasUrlListByUUIDS(task.Uuid) | ||||
| if err != nil { | 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["branches"] = branches | ||||
| ctx.Data["branch_name"] = task.BranchName | ctx.Data["branch_name"] = task.BranchName | ||||
| ctx.Data["description"] = task.Description | ctx.Data["description"] = task.Description | ||||
| ctx.Data["boot_file"] = task.BootFile | ctx.Data["boot_file"] = task.BootFile | ||||
| ctx.Data["dataset_name"] = datasetNames | |||||
| ctx.Data["work_server_number"] = task.WorkServerNumber | ctx.Data["work_server_number"] = task.WorkServerNumber | ||||
| ctx.Data["flavor_name"] = task.FlavorName | ctx.Data["flavor_name"] = task.FlavorName | ||||
| ctx.Data["engine_name"] = task.EngineName | ctx.Data["engine_name"] = task.EngineName | ||||
| @@ -1096,6 +1130,13 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) | |||||
| VersionCount := modelarts.VersionCountOne | VersionCount := modelarts.VersionCountOne | ||||
| EngineName := form.EngineName | 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) | count, err := models.GetCloudbrainTrainJobCountByUserID(ctx.User.ID) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetCloudbrainTrainJobCountByUserID failed:%v", err, ctx.Data["MsgID"]) | log.Error("GetCloudbrainTrainJobCountByUserID failed:%v", err, ctx.Data["MsgID"]) | ||||
| @@ -1126,7 +1167,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) | |||||
| return | return | ||||
| } | } | ||||
| errStr := checkModelArtsSpecialPool(ctx, flavorCode, string(models.JobTypeTrain)) | |||||
| errStr = checkModelArtsSpecialPool(ctx, flavorCode, string(models.JobTypeTrain)) | |||||
| if errStr != "" { | if errStr != "" { | ||||
| trainJobErrorNewDataPrepare(ctx, form) | trainJobErrorNewDataPrepare(ctx, form) | ||||
| ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsTrainJobNew, &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") | 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) { | func getUserCommand(engineId int, req *modelarts.GenerateTrainJobReq) (string, string) { | ||||
| userImageUrl := "" | userImageUrl := "" | ||||
| userCommand := "" | userCommand := "" | ||||
| @@ -1364,6 +1447,13 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ | |||||
| ctx.Data["PageIsTrainJob"] = true | ctx.Data["PageIsTrainJob"] = true | ||||
| var jobID = ctx.Params(":jobid") | 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) | count, err := models.GetCloudbrainTrainJobCountByUserID(ctx.User.ID) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetCloudbrainTrainJobCountByUserID failed:%v", err, ctx.Data["MsgID"]) | log.Error("GetCloudbrainTrainJobCountByUserID failed:%v", err, ctx.Data["MsgID"]) | ||||
| @@ -1431,7 +1521,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ | |||||
| return | return | ||||
| } | } | ||||
| errStr := checkModelArtsSpecialPool(ctx, flavorCode, string(models.JobTypeTrain)) | |||||
| errStr = checkModelArtsSpecialPool(ctx, flavorCode, string(models.JobTypeTrain)) | |||||
| if errStr != "" { | if errStr != "" { | ||||
| versionErrorDataPrepare(ctx, form) | versionErrorDataPrepare(ctx, form) | ||||
| ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsTrainJobVersionNew, &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)) | log.Error("the boot file(%s) must be a python file", strings.TrimSpace(form.BootFile)) | ||||
| return errors.New("启动文件必须是python文件") | 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 == "" { | if form.BranchName == "" { | ||||
| log.Error("the branch must not be null!", form.BranchName) | log.Error("the branch must not be null!", form.BranchName) | ||||
| return errors.New("代码分支不能为空!") | return errors.New("代码分支不能为空!") | ||||
| @@ -1810,7 +1896,7 @@ func TrainJobShow(ctx *context.Context) { | |||||
| } else { | } else { | ||||
| VersionListTasks[i].Parameters = "" | 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].CanDel = cloudbrain.CanDeleteJob(ctx, &task.Cloudbrain) | ||||
| VersionListTasks[i].CanModify = cloudbrain.CanModifyJob(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 | ckptUrl := "/" + form.TrainUrl + form.CkptName | ||||
| log.Info("ckpt url:" + ckptUrl) | 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) | count, err := models.GetCloudbrainInferenceJobCountByUserID(ctx.User.ID) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetCloudbrainInferenceJobCountByUserID failed:%v", err, ctx.Data["MsgID"]) | 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 != "" { | if errStr != "" { | ||||
| inferenceJobErrorNewDataPrepare(ctx, form) | inferenceJobErrorNewDataPrepare(ctx, form) | ||||
| ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsInferenceJobNew, &form) | ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsInferenceJobNew, &form) | ||||
| @@ -2526,7 +2619,7 @@ func InferenceJobShow(ctx *context.Context) { | |||||
| ctx.Data["displayJobName"] = task.DisplayJobName | ctx.Data["displayJobName"] = task.DisplayJobName | ||||
| ctx.Data["task"] = task | ctx.Data["task"] = task | ||||
| ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, 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 := []int64{} | ||||
| tempUids = append(tempUids, task.UserID) | tempUids = append(tempUids, task.UserID) | ||||
| JobCreater, err := models.GetUserNamesByIDs(tempUids) | 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} | 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 { | 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 { | func ErrorWithData(code int, msg string, data interface{}) *AiforgeResponse { | ||||
| return &AiforgeResponse{Code: code, Msg: msg, Data: data} | 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("/delete", admin.DeleteNotices) | ||||
| m.Post("/empty", admin.EmptyNotices) | 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) | }, adminReq) | ||||
| // ***** END: Admin ***** | // ***** 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="alert"></div> | ||||
| <div class="admin user"> | <div class="admin user"> | ||||
| {{template "admin/navbar" .}} | {{template "admin/navbar" .}} | ||||
| <div id="images-admin"> | |||||
| </div> | |||||
| <div class="ui container"> | |||||
| <div id="images-admin"></div> | |||||
| </div> | |||||
| </div> | </div> | ||||
| <!-- 确认模态框 --> | <!-- 确认模态框 --> | ||||
| <div> | <div> | ||||
| @@ -22,19 +22,19 @@ | |||||
| data-all-compute="{{.i18n.Tr "admin.cloudbrain.all_computing_resources"}}" | data-all-compute="{{.i18n.Tr "admin.cloudbrain.all_computing_resources"}}" | ||||
| data-all-status="{{.i18n.Tr "admin.cloudbrain.all_status"}}"></div> | data-all-status="{{.i18n.Tr "admin.cloudbrain.all_status"}}"></div> | ||||
| {{template "admin/navbar" .}} | {{template "admin/navbar" .}} | ||||
| <div class="ui container" style="width: 95%;"> | |||||
| <div class="ui container"> | |||||
| {{template "base/alert" .}} | {{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" .}} | {{template "admin/cloudbrain/search" .}} | ||||
| <div class="ui six wide column right aligned" style="margin: 1rem 0;"> | <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;" | <a class="ui compact blue basic icon button" style="box-shadow: none !important; padding: 0.8em;" | ||||
| href="/admin/cloudbrains/download"><i | href="/admin/cloudbrains/download"><i | ||||
| class="ri-download-line middle aligned icon"></i>{{.i18n.Tr "admin.cloudbrain.download_report"}}</a> | class="ri-download-line middle aligned icon"></i>{{.i18n.Tr "admin.cloudbrain.download_report"}}</a> | ||||
| </div> | </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="ui grid stackable" style="background: #f0f0f0;;"> | ||||
| <div class="row"> | <div class="row"> | ||||
| @@ -412,17 +412,16 @@ | |||||
| </div> | </div> | ||||
| {{end}} | {{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> | ||||
| </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> | </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> | ></i> | ||||
| <span id="gpu-nums" style="font-size: 12px" | <span id="gpu-nums" style="font-size: 12px" | ||||
| >{{.ctx.i18n.Tr "repo.wait_count_start"}} | >{{.ctx.i18n.Tr "repo.wait_count_start"}} | ||||
| {{if .type}} | |||||
| {{if .ctx.QueuesDetail}} | |||||
| {{ $gpuQueue }} | {{ $gpuQueue }} | ||||
| {{else}} | {{else}} | ||||
| {{.ctx.WaitCount}} | {{.ctx.WaitCount}} | ||||
| @@ -256,8 +256,9 @@ | |||||
| <div class="ui pointing secondary menu" style="border-bottom: 1px solid rgba(34,36,38,.15);"> | <div class="ui pointing secondary menu" style="border-bottom: 1px solid rgba(34,36,38,.15);"> | ||||
| <a class="active item" | <a class="active item" | ||||
| data-tab="first{{$k}}">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a> | 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> | ||||
| <div class="ui tab active" data-tab="first{{$k}}"> | <div class="ui tab active" data-tab="first{{$k}}"> | ||||
| <div style="padding-top: 10px;"> | <div style="padding-top: 10px;"> | ||||
| @@ -528,19 +529,42 @@ | |||||
| </div> | </div> | ||||
| <div class="ui tab" data-tab="second{{$k}}"> | <div class="ui tab" data-tab="second{{$k}}"> | ||||
| <div> | <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> | ||||
| </div> | </div> | ||||
| @@ -228,7 +228,7 @@ | |||||
| </h4> | </h4> | ||||
| {{with .task}} | {{with .task}} | ||||
| <div class="ui accordion border-according" id="accordion{{.VersionName}}" | <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}}"> | <input type="hidden" id="jobId_input" name="jobId_input" value="{{.JobID}}"> | ||||
| <div class="active title padding0"> | <div class="active title padding0"> | ||||
| <div class="according-panel-heading"> | <div class="according-panel-heading"> | ||||
| @@ -264,7 +264,8 @@ | |||||
| data-tab="first">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a> | data-tab="first">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a> | ||||
| <a class="item" data-tab="second" | <a class="item" data-tab="second" | ||||
| onclick="javascript:parseInfo()">{{$.i18n.Tr "repo.cloudbrain.runinfo"}}</a> | 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" | <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> | 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> | </div> | ||||
| @@ -500,7 +501,13 @@ | |||||
| <tbody> | <tbody> | ||||
| {{range $m ,$n := $.datasetDownload}} | {{range $m ,$n := $.datasetDownload}} | ||||
| <tr> | <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> | </tr> | ||||
| {{end}} | {{end}} | ||||
| @@ -518,7 +525,7 @@ | |||||
| <div class="ui message message{{.VersionName}}" style="display: none;"> | <div class="ui message message{{.VersionName}}" style="display: none;"> | ||||
| <div id="header"></div> | <div id="header"></div> | ||||
| </div> | </div> | ||||
| <div class="ui attached log" id="log{{.VersionName}}" | |||||
| <div class="ui attached" | |||||
| style="height: 390px !important; overflow: auto;"> | style="height: 390px !important; overflow: auto;"> | ||||
| <input type="hidden" id="json_value" value="{{$.result.JobStatus.AppExitDiagnostics}}"> | <input type="hidden" id="json_value" value="{{$.result.JobStatus.AppExitDiagnostics}}"> | ||||
| <input type="hidden" id="ExitDiagnostics" value="{{$.ExitDiagnostics}}"> | <input type="hidden" id="ExitDiagnostics" value="{{$.ExitDiagnostics}}"> | ||||
| @@ -531,7 +538,44 @@ | |||||
| </div> | </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"> | <div class="ui tab" data-tab="four"> | ||||
| <input type="hidden" name="model{{.VersionName}}" value="-1"> | <input type="hidden" name="model{{.VersionName}}" value="-1"> | ||||
| @@ -498,7 +498,13 @@ | |||||
| <tbody> | <tbody> | ||||
| {{range $m ,$n := $.datasetDownload}} | {{range $m ,$n := $.datasetDownload}} | ||||
| <tr> | <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> | </tr> | ||||
| {{end}} | {{end}} | ||||
| @@ -464,7 +464,13 @@ | |||||
| <tbody> | <tbody> | ||||
| {{range $m ,$n := $.datasetDownload}} | {{range $m ,$n := $.datasetDownload}} | ||||
| <tr> | <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> | </tr> | ||||
| {{end}} | {{end}} | ||||
| @@ -594,7 +600,7 @@ | |||||
| <input type="hidden" name="trainTaskCreate" value="true"> | <input type="hidden" name="trainTaskCreate" value="true"> | ||||
| <div class="required inline field"> | <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" class="width83" id="JobId" name="JobId" readonly required> | ||||
| <input type="hidden" id="VersionName" name="VersionName" value="V0001"> | <input type="hidden" id="VersionName" name="VersionName" value="V0001"> | ||||
| <input style="width: 45%;" id="JobName" readonly required> | <input style="width: 45%;" id="JobName" readonly required> | ||||
| @@ -417,7 +417,7 @@ | |||||
| <div class="bgtask-content"> | <div class="bgtask-content"> | ||||
| <div class="bgtask-content-txt">{{.i18n.Tr "dataset.dataset_explain"}}</div> | <div class="bgtask-content-txt">{{.i18n.Tr "dataset.dataset_explain"}}</div> | ||||
| <div class="bgtask-content-txt">{{.i18n.Tr "dataset.dataset_instructions_for_use"}}<a | <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> | </div> | ||||
| </div> | </div> | ||||
| @@ -419,7 +419,12 @@ | |||||
| <td class="ti-text-form-content"> | <td class="ti-text-form-content"> | ||||
| <div class="text-span text-span-w"> | <div class="text-span text-span-w"> | ||||
| {{range $m ,$n := $.datasetDownload}} | {{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}} | {{end}} | ||||
| </div> | </div> | ||||
| </td> | </td> | ||||
| @@ -551,7 +556,7 @@ | |||||
| <input type="hidden" name="trainTaskCreate" value="true"> | <input type="hidden" name="trainTaskCreate" value="true"> | ||||
| <div class="required inline field"> | <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" class="width83" id="JobId" name="JobId" readonly required> | ||||
| <input type="hidden" id="VersionName" name="VersionName" value="V0001"> | <input type="hidden" id="VersionName" name="VersionName" value="V0001"> | ||||
| <input style="width: 45%;" id="JobName" readonly required> | <input style="width: 45%;" id="JobName" readonly required> | ||||
| @@ -441,7 +441,14 @@ td, th { | |||||
| <tbody> | <tbody> | ||||
| {{range $m ,$n := $.datasetDownload}} | {{range $m ,$n := $.datasetDownload}} | ||||
| <tr> | <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> | </tr> | ||||
| {{end}} | {{end}} | ||||
| @@ -350,7 +350,7 @@ | |||||
| <td class="ti-text-form-content"> | <td class="ti-text-form-content"> | ||||
| <div class="text-span text-span-w" id="{{.VersionName}}-mirror"> | <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-clipboard-text="{{.Image}}" | ||||
| data-success="{{$.i18n.Tr "repo.copy_link_success"}}" | data-success="{{$.i18n.Tr "repo.copy_link_success"}}" | ||||
| data-error="{{$.i18n.Tr "repo.copy_link_error"}}" | data-error="{{$.i18n.Tr "repo.copy_link_error"}}" | ||||
| @@ -439,9 +439,16 @@ | |||||
| <tbody> | <tbody> | ||||
| {{range $.datasetDownload}} | {{range $.datasetDownload}} | ||||
| <tr> | <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 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> | </tr> | ||||
| {{end}} | {{end}} | ||||
| </tbody> | </tbody> | ||||
| @@ -491,5 +498,4 @@ | |||||
| $(document).ready(function () { | $(document).ready(function () { | ||||
| $('.secondary.menu .item').tab(); | $('.secondary.menu .item').tab(); | ||||
| }); | }); | ||||
| console.log({{$.datasetDownload}}) | |||||
| </script> | </script> | ||||
| @@ -287,8 +287,24 @@ | |||||
| id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255" value="1" | id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255" value="1" | ||||
| readonly> | readonly> | ||||
| <div class="field" id="trainjob_work_server_num_select" name="work_server_number_select"> | <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> | <option name="server_id" value="1">1</option> | ||||
| {{end}} | |||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| @@ -489,7 +489,13 @@ | |||||
| {{if eq $k $m}} | {{if eq $k $m}} | ||||
| {{range $f ,$g := $n}} | {{range $f ,$g := $n}} | ||||
| <tr> | <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> | </tr> | ||||
| {{end}} | {{end}} | ||||
| {{end}} | {{end}} | ||||
| @@ -604,7 +610,7 @@ | |||||
| <div class="two inline fields "> | <div class="two inline fields "> | ||||
| <div class="required ten wide field"> | <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 type="hidden" class="width83" id="JobId" name="JobId" readonly required> | ||||
| <input class="width83" id="JobName" 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_engine_name" name="engine_names" value=""> | ||||
| <input type="hidden" id="ai_flaver_name" name="flaver_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}}"> | <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> | <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> | <span style="color: #888;font-size: 12px;">{{.i18n.Tr "cloudbrain.train_dataset_path_rule" | Safe}}</span> | ||||
| </div> | </div> | ||||
| <h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4> | |||||
| <div class="required unite min_title inline field"> | <div class="required unite min_title inline field"> | ||||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label> | <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}}"> | <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-header">{{$.i18n.Tr "repo.modelconvert.notcreate"}}</div> | ||||
| <div class="bgtask-content"> | <div class="bgtask-content"> | ||||
| {{if eq .MODEL_COUNT 0}} | {{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}} | {{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> | ||||
| </div> | </div> | ||||
| @@ -71,9 +71,9 @@ | |||||
| {{end}} | {{end}} | ||||
| {{if eq $.TRAIN_COUNT 0}} | {{if eq $.TRAIN_COUNT 0}} | ||||
| <div class="bgtask-content-txt">{{$.i18n.Tr "repo.model.manage.createtrainjob_tip"}}<a | <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}} | {{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> | ||||
| <div style="display: none;"> | <div style="display: none;"> | ||||
| @@ -330,7 +330,7 @@ | |||||
| .modal({ | .modal({ | ||||
| centered: false, | centered: false, | ||||
| onShow: function () { | onShow: function () { | ||||
| $('#model_header').text("导入新模型") | |||||
| $('#model_header').text({{.i18n.Tr "repo.model.manage.import_new_model"}}) | |||||
| $('input[name="Version"]').addClass('model_disabled') | $('input[name="Version"]').addClass('model_disabled') | ||||
| $('.ui.dimmer').css({ "background-color": "rgb(136, 136, 136,0.7)" }) | $('.ui.dimmer').css({ "background-color": "rgb(136, 136, 136,0.7)" }) | ||||
| $("#job-name").empty() | $("#job-name").empty() | ||||
| @@ -125,7 +125,7 @@ | |||||
| </td> | </td> | ||||
| </tr> | </tr> | ||||
| <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"> | <td class="ti-text-form-content word-elipsis"> | ||||
| <a id="DisplayJobNameHref" class="title" style="font-size: 14px;" target="_blank"> | <a id="DisplayJobNameHref" class="title" style="font-size: 14px;" target="_blank"> | ||||
| <span id="DisplayJobName" class="fitted" style="width: 90%;vertical-align: middle;"></span> | <span id="DisplayJobName" class="fitted" style="width: 90%;vertical-align: middle;"></span> | ||||
| @@ -197,11 +197,12 @@ export default { | |||||
| this.getModelList() | this.getModelList() | ||||
| }, | }, | ||||
| showcreateVue(name,version,label){ | showcreateVue(name,version,label){ | ||||
| let title= this.i18n.model_create_version_title; | |||||
| $('.ui.modal.second') | $('.ui.modal.second') | ||||
| .modal({ | .modal({ | ||||
| centered: false, | centered: false, | ||||
| onShow:function(){ | onShow:function(){ | ||||
| $('#model_header').text("创建模型新版本") | |||||
| $('#model_header').text(title) | |||||
| $('input[name="Name"]').addClass('model_disabled') | $('input[name="Name"]').addClass('model_disabled') | ||||
| $('input[name="Name"]').attr('readonly','readonly') | $('input[name="Name"]').attr('readonly','readonly') | ||||
| $('input[name="modelSelectedFile"]').attr('readonly','readonly') | $('input[name="modelSelectedFile"]').attr('readonly','readonly') | ||||
| @@ -248,7 +248,7 @@ | |||||
| > | > | ||||
| <el-tabs v-model="activeName"> | <el-tabs v-model="activeName"> | ||||
| <el-tab-pane :label="i18n.public_dataset" name="first"> | <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 | <el-checkbox-group | ||||
| v-model="checkList" | v-model="checkList" | ||||
| style="font-size: 14px; line-height: 1" | style="font-size: 14px; line-height: 1" | ||||
| @@ -282,6 +282,7 @@ | |||||
| :href="`/${item.Repo.OwnerName}/${item.Repo.Name}/datasets`" | :href="`/${item.Repo.OwnerName}/${item.Repo.Name}/datasets`" | ||||
| :title="`${item.Repo.OwnerName}/${item.Repo.Alias}`" | :title="`${item.Repo.OwnerName}/${item.Repo.Alias}`" | ||||
| style="font-size: 12px" | style="font-size: 12px" | ||||
| target="_blank" | |||||
| >{{ item.Repo.OwnerName }}/{{ item.Repo.Alias }}</a | >{{ item.Repo.OwnerName }}/{{ item.Repo.Alias }}</a | ||||
| > | > | ||||
| </div> | </div> | ||||
| @@ -338,19 +339,18 @@ | |||||
| overflow-y: auto; | 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> | ||||
| <div style="text-align: end"> | <div style="text-align: end"> | ||||
| <el-button | <el-button | ||||
| @@ -405,7 +405,11 @@ export default { | |||||
| methods: { | methods: { | ||||
| openDataset() { | openDataset() { | ||||
| this.checkList = this.datasetList.map((item) => { | 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; | return item.ID; | ||||
| }); | }); | ||||
| this.dialogVisible = true; | this.dialogVisible = true; | ||||
| @@ -472,7 +476,11 @@ export default { | |||||
| return; | return; | ||||
| } | } | ||||
| if (checked) { | if (checked) { | ||||
| this.selectDatasetArray.push({ ID: item.ID, Title: item.Title }); | |||||
| this.selectDatasetArray.push({ | |||||
| ID: item.ID, | |||||
| Title: item.Title, | |||||
| isChecked: true, | |||||
| }); | |||||
| } else { | } else { | ||||
| let index = this.selectDatasetArray.findIndex((element) => { | let index = this.selectDatasetArray.findIndex((element) => { | ||||
| return element.ID === item.ID; | return element.ID === item.ID; | ||||
| @@ -485,6 +493,7 @@ export default { | |||||
| return element.ID === item.ID; | return element.ID === item.ID; | ||||
| }); | }); | ||||
| this.selectDatasetArray.splice(index, 1); | this.selectDatasetArray.splice(index, 1); | ||||
| this.checkList.splice(index, 1); | |||||
| }, | }, | ||||
| postStar(item, isSigned) { | postStar(item, isSigned) { | ||||
| if (!isSigned) { | if (!isSigned) { | ||||
| @@ -11,9 +11,9 @@ | |||||
| v-if="benchmarkNew" | v-if="benchmarkNew" | ||||
| class="label-fix-width" | class="label-fix-width" | ||||
| style="font-weight: normal" | 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 | <span | ||||
| :class=" | :class=" | ||||
| benchmarkNew === true ? 'dataset-train-span' : 'dataset-debug-span' | benchmarkNew === true ? 'dataset-train-span' : 'dataset-debug-span' | ||||
| @@ -59,7 +59,7 @@ | |||||
| ? 'select-dataset-button' | ? 'select-dataset-button' | ||||
| : 'select-dataset-button-color' | : 'select-dataset-button-color' | ||||
| " | " | ||||
| >{{i18n.dataset_select}} | |||||
| >{{ i18n.dataset_select }} | |||||
| </el-button> | </el-button> | ||||
| <el-dialog | <el-dialog | ||||
| :title="i18n.dataset_select" | :title="i18n.dataset_select" | ||||
| @@ -90,7 +90,11 @@ | |||||
| > | > | ||||
| <el-tabs v-model="activeName" @tab-click="handleClick"> | <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-row> | ||||
| <el-tree | <el-tree | ||||
| :data="currentDatasetList" | :data="currentDatasetList" | ||||
| @@ -172,13 +176,13 @@ | |||||
| class="zip-loading" | class="zip-loading" | ||||
| v-if="data.DecompressState === 2" | v-if="data.DecompressState === 2" | ||||
| > | > | ||||
| {{i18n.dataset_unziping}} | |||||
| {{ i18n.dataset_unziping }} | |||||
| </span> | </span> | ||||
| <span | <span | ||||
| class="unzip-failed" | class="unzip-failed" | ||||
| v-if="data.DecompressState === 3" | v-if="data.DecompressState === 3" | ||||
| > | > | ||||
| {{i18n.dataset_unzip_failed}} | |||||
| {{ i18n.dataset_unzip_failed }} | |||||
| </span> | </span> | ||||
| </span> | </span> | ||||
| </span> | </span> | ||||
| @@ -201,7 +205,11 @@ | |||||
| </div> | </div> | ||||
| </el-tab-pane> | </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-row> | ||||
| <el-tree | <el-tree | ||||
| :data="myDatasetList" | :data="myDatasetList" | ||||
| @@ -274,13 +282,13 @@ | |||||
| class="zip-loading" | class="zip-loading" | ||||
| v-if="data.DecompressState === 2" | v-if="data.DecompressState === 2" | ||||
| > | > | ||||
| {{i18n.dataset_unziping}} | |||||
| {{ i18n.dataset_unziping }} | |||||
| </span> | </span> | ||||
| <span | <span | ||||
| class="unzip-failed" | class="unzip-failed" | ||||
| v-if="data.DecompressState === 3" | v-if="data.DecompressState === 3" | ||||
| > | > | ||||
| {{i18n.dataset_unzip_failed}} | |||||
| {{ i18n.dataset_unzip_failed }} | |||||
| </span> | </span> | ||||
| </span> | </span> | ||||
| </span> | </span> | ||||
| @@ -380,13 +388,13 @@ | |||||
| class="zip-loading" | class="zip-loading" | ||||
| v-if="data.DecompressState === 2" | v-if="data.DecompressState === 2" | ||||
| > | > | ||||
| {{i18n.dataset_unziping}} | |||||
| {{ i18n.dataset_unziping }} | |||||
| </span> | </span> | ||||
| <span | <span | ||||
| class="unzip-failed" | class="unzip-failed" | ||||
| v-if="data.DecompressState === 3" | v-if="data.DecompressState === 3" | ||||
| > | > | ||||
| {{i18n.dataset_unzip_failed}} | |||||
| {{ i18n.dataset_unzip_failed }} | |||||
| </span> | </span> | ||||
| </span> | </span> | ||||
| </span> | </span> | ||||
| @@ -486,13 +494,13 @@ | |||||
| class="zip-loading" | class="zip-loading" | ||||
| v-if="data.DecompressState === 2" | v-if="data.DecompressState === 2" | ||||
| > | > | ||||
| {{i18n.dataset_unziping}} | |||||
| {{ i18n.dataset_unziping }} | |||||
| </span> | </span> | ||||
| <span | <span | ||||
| class="unzip-failed" | class="unzip-failed" | ||||
| v-if="data.DecompressState === 3" | v-if="data.DecompressState === 3" | ||||
| > | > | ||||
| {{i18n.dataset_unzip_failed}} | |||||
| {{ i18n.dataset_unzip_failed }} | |||||
| </span> | </span> | ||||
| </span> | </span> | ||||
| </span> | </span> | ||||
| @@ -536,9 +544,9 @@ | |||||
| line-height: 40px; | line-height: 40px; | ||||
| " | " | ||||
| > | > | ||||
| {{i18n.dataset_selected}} | |||||
| {{ i18n.dataset_selected }} | |||||
| </div> | </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-group v-model="checkList"> | ||||
| <el-checkbox | <el-checkbox | ||||
| v-for="(item, index) in selectDatasetArray" | v-for="(item, index) in selectDatasetArray" | ||||
| @@ -558,7 +566,7 @@ | |||||
| color: #fff; | color: #fff; | ||||
| border: 1px solid #389e0d; | border: 1px solid #389e0d; | ||||
| " | " | ||||
| >{{i18n.dataset_ok}}</el-button | |||||
| >{{ i18n.dataset_ok }}</el-button | |||||
| > | > | ||||
| </div> | </div> | ||||
| </el-col> | </el-col> | ||||
| @@ -732,7 +740,6 @@ export default { | |||||
| .then((res) => { | .then((res) => { | ||||
| this.loadingCurrent = false; | this.loadingCurrent = false; | ||||
| let data = JSON.parse(res.data.data); | let data = JSON.parse(res.data.data); | ||||
| console.log(data); | |||||
| this.currentDatasetList = this.transformeTreeData( | this.currentDatasetList = this.transformeTreeData( | ||||
| data, | data, | ||||
| "currentTree", | "currentTree", | ||||
| @@ -978,7 +985,10 @@ export default { | |||||
| let hasSelectDatasetName = $(".cloudbrain-type") | let hasSelectDatasetName = $(".cloudbrain-type") | ||||
| .data("dataset-name") | .data("dataset-name") | ||||
| .split(";"); | .split(";"); | ||||
| if (this.hasSelectDatasetList.length !== 0) { | |||||
| if ( | |||||
| this.hasSelectDatasetList.length !== 0 && | |||||
| hasSelectDatasetName[0] !== "" | |||||
| ) { | |||||
| this.saveStatusList = this.hasSelectDatasetList; | this.saveStatusList = this.hasSelectDatasetList; | ||||
| this.checkList = hasSelectDatasetName; | this.checkList = hasSelectDatasetName; | ||||
| this.hasSelectDatasetList.forEach((item, index) => { | this.hasSelectDatasetList.forEach((item, index) => { | ||||
| @@ -996,7 +1006,6 @@ export default { | |||||
| location.href.indexOf("train-job") !== -1 || | location.href.indexOf("train-job") !== -1 || | ||||
| location.href.indexOf("inference") !== -1 | location.href.indexOf("inference") !== -1 | ||||
| ) { | ) { | ||||
| console.log("this.benchmarkNew"); | |||||
| this.benchmarkNew = true; | this.benchmarkNew = true; | ||||
| } | } | ||||
| if ( | if ( | ||||
| @@ -1,9 +1,8 @@ | |||||
| <template> | <template> | ||||
| <div> | <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 attached segment"> | ||||
| <div class="ui form ignore-dirty"> | <div class="ui form ignore-dirty"> | ||||
| <div class="ui fluid action input"> | <div class="ui fluid action input"> | ||||
| @@ -31,8 +30,8 @@ | |||||
| <div class="ui six wide column right aligned" style="margin: 1rem 0;"> | <div class="ui six wide column right aligned" style="margin: 1rem 0;"> | ||||
| <a class="ui blue small button" href="/admin/images/commit_image">创建云脑镜像</a> | <a class="ui blue small button" href="/admin/images/commit_image">创建云脑镜像</a> | ||||
| </div> | </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"> | <el-table-column label="镜像Tag" min-width="19%" align="left" prop="tag"> | ||||
| <template slot-scope="scope"> | <template slot-scope="scope"> | ||||
| <div style="display: flex;align-items: center;"> | <div style="display: flex;align-items: center;"> | ||||
| @@ -56,7 +56,7 @@ export const i18nVue = { | |||||
| dataset_link_success: "关联数据集成功!", | dataset_link_success: "关联数据集成功!", | ||||
| dataset_link_failed: "关联数据集失败!", | dataset_link_failed: "关联数据集失败!", | ||||
| dataset_over_nums: "关联超过?个数据集", | dataset_over_nums: "关联超过?个数据集", | ||||
| cancel_link_dataset: "取消?关联数据集成功!", | |||||
| cancel_link_dataset: "取消关联数据集?成功!", | |||||
| image_label: "镜像", | image_label: "镜像", | ||||
| image_select_placeholder: "选择镜像或输入镜像地址", | image_select_placeholder: "选择镜像或输入镜像地址", | ||||
| image_select: "选择镜像", | image_select: "选择镜像", | ||||
| @@ -95,6 +95,8 @@ export const i18nVue = { | |||||
| model_create_new_ver: "创建新版本", | model_create_new_ver: "创建新版本", | ||||
| model_download: "下载", | model_download: "下载", | ||||
| model_delete: "删除", | model_delete: "删除", | ||||
| model_create_title: "导入新模型", | |||||
| model_create_version_title: "创建模型新版本", | |||||
| }, | }, | ||||
| US: { | US: { | ||||
| computer_vision: "computer vision", | computer_vision: "computer vision", | ||||
| @@ -142,21 +144,21 @@ export const i18nVue = { | |||||
| disassociate: "Unlink", | disassociate: "Unlink", | ||||
| public_dataset: "Public Dataset", | public_dataset: "Public Dataset", | ||||
| selected_data_file: "Selected DataSets", | selected_data_file: "Selected DataSets", | ||||
| sure: "Ok", | |||||
| sure: "OK", | |||||
| search_dataset: "Search dataset name/description ...", | search_dataset: "Search dataset name/description ...", | ||||
| citations: "Citations", | citations: "Citations", | ||||
| downloads: "Downloads", | downloads: "Downloads", | ||||
| not_link_dataset: "No datasets have been associated yet", | |||||
| not_link_dataset: "No datasets have been linked yet", | |||||
| no_link_dataset_tips1: | 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: | 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_success: "Linked dataset succeeded!", | ||||
| dataset_link_failed: "Linked dataset Failed!", | dataset_link_failed: "Linked dataset Failed!", | ||||
| dataset_over_nums: "Linked over ? datasets!", | dataset_over_nums: "Linked over ? datasets!", | ||||
| cancel_link_dataset: "Cancel ? Linked dataset succeeded!", | |||||
| cancel_link_dataset: "Cancel Linked dataset ? succeeded!", | |||||
| image_label: "Image", | image_label: "Image", | ||||
| image_select_placeholder: "Select image or input image url", | image_select_placeholder: "Select image or input image url", | ||||
| image_select: "Select Image", | image_select: "Select Image", | ||||
| @@ -196,5 +198,7 @@ export const i18nVue = { | |||||
| model_create_new_ver: "New Version", | model_create_new_ver: "New Version", | ||||
| model_download: "Download", | model_download: "Download", | ||||
| model_delete: "Delete", | 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 { | .admin { | ||||
| padding-top: 15px; | |||||
| padding-top: 15px !important; | |||||
| .table.segment { | .table.segment { | ||||
| padding: 0; | padding: 0; | ||||
| @@ -75,4 +75,58 @@ | |||||
| white-space: pre-wrap; | white-space: pre-wrap; | ||||
| word-wrap: break-word; | 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]; | 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'; | const isProduction = process.env.NODE_ENV !== 'development'; | ||||
| module.exports = { | module.exports = { | ||||
| @@ -44,6 +49,7 @@ module.exports = { | |||||
| icons: glob('node_modules/@primer/octicons/build/svg/**/*.svg'), | icons: glob('node_modules/@primer/octicons/build/svg/**/*.svg'), | ||||
| ...standalone, | ...standalone, | ||||
| ...themes, | ...themes, | ||||
| ...vuePages, | |||||
| }, | }, | ||||
| devtool: false, | devtool: false, | ||||
| output: { | output: { | ||||
| @@ -267,6 +273,7 @@ module.exports = { | |||||
| symlinks: false, | symlinks: false, | ||||
| alias: { | alias: { | ||||
| vue$: 'vue/dist/vue.esm.js', // needed because vue's default export is the runtime only | 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'] | extensions: ['.tsx', '.ts', '.js'] | ||||
| }, | }, | ||||
| @@ -29,6 +29,11 @@ for (const path of stadalonePaths) { | |||||
| standalone[parse(path).name] = [path]; | 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'; | const isProduction = process.env.NODE_ENV !== 'development'; | ||||
| module.exports = { | module.exports = { | ||||
| @@ -44,6 +49,7 @@ module.exports = { | |||||
| icons: glob('node_modules/@primer/octicons/build/svg/**/*.svg'), | icons: glob('node_modules/@primer/octicons/build/svg/**/*.svg'), | ||||
| ...standalone, | ...standalone, | ||||
| ...themes, | ...themes, | ||||
| ...vuePages | |||||
| }, | }, | ||||
| devtool: false, | devtool: false, | ||||
| output: { | output: { | ||||
| @@ -267,6 +273,7 @@ module.exports = { | |||||
| symlinks: false, | symlinks: false, | ||||
| alias: { | alias: { | ||||
| vue$: 'vue/dist/vue.esm.js', // needed because vue's default export is the runtime only | 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'] | extensions: ['.tsx', '.ts', '.js'] | ||||
| }, | }, | ||||