#5429 V20240402.patch

Merged
ychao_1983 merged 46 commits from V20240402.patch into V20240423 2 weeks ago
  1. +2
    -0
      entity/ai_task.go
  2. +1
    -0
      entity/cluster.go
  3. +2
    -0
      entity/creation.go
  4. +2
    -2
      manager/client/cloudbrain_two/resty.go
  5. +2
    -2
      manager/client/cloudbrain_two_cd/resty.go
  6. +2
    -2
      manager/client/grampus/grampus.go
  7. +25
    -22
      models/cloudbrain.go
  8. +10
    -0
      models/role.go
  9. +26
    -9
      models/user.go
  10. +1
    -0
      modules/auth/admin.go
  11. +7
    -0
      modules/setting/setting.go
  12. +1
    -5
      modules/urfs_client/objectstorage/mocks/objectstorage_mock.go
  13. +7
    -1
      options/locale/locale_en-US.ini
  14. +7
    -1
      options/locale/locale_zh-CN.ini
  15. +27
    -1
      routers/admin/users.go
  16. +1
    -0
      routers/api/v1/api.go
  17. +5
    -0
      routers/api/v1/user/user.go
  18. +11
    -3
      routers/home.go
  19. +13
    -1
      routers/repo/cloudbrain.go
  20. +8
    -13
      services/ai_task_service/cluster/c2net.go
  21. +1
    -1
      services/ai_task_service/cluster/cloudbrain_one.go
  22. +5
    -5
      services/ai_task_service/cluster/cloudbrain_two.go
  23. +1
    -1
      services/ai_task_service/cluster/cluster_base.go
  24. +3
    -2
      services/ai_task_service/task/cloudbrain_one_notebook_task.go
  25. +3
    -2
      services/ai_task_service/task/cloudbrain_two_notebook_task.go
  26. +17
    -2
      services/ai_task_service/task/grampus_notebook_task.go
  27. +12
    -2
      services/ai_task_service/task/opt_handler.go
  28. +8
    -4
      services/ai_task_service/task/task_creation_info.go
  29. +20
    -0
      services/ai_task_service/task/task_service.go
  30. +5
    -0
      services/role/role.go
  31. +6
    -0
      templates/admin/user/edit.tmpl
  32. +66
    -0
      templates/admin/user/list.tmpl
  33. +155
    -0
      web_src/vuepages/components/cloudbrain/RunTimeLimit.vue
  34. +15
    -3
      web_src/vuepages/components/cloudbrain/details/ConfigInfo.vue
  35. +46
    -16
      web_src/vuepages/components/cloudbrain/details/ResourceUseage.vue
  36. +11
    -1
      web_src/vuepages/langs/config/en-US.js
  37. +11
    -1
      web_src/vuepages/langs/config/zh-CN.js
  38. +16
    -8
      web_src/vuepages/pages/cloudbrain/configs.js
  39. +18
    -5
      web_src/vuepages/pages/cloudbrain/create/index.vue
  40. +3
    -3
      web_src/vuepages/pages/cloudbrain/detail/index.vue
  41. +6
    -2
      web_src/vuepages/pages/supercompute/create/index.vue

+ 2
- 0
entity/ai_task.go View File

@@ -45,6 +45,7 @@ type CreateReq struct {
SourceCloudbrainId int64 `json:"source_cloudbrain_id"`
AppName string `json:"app_name"`
HasInternet models.SpecInternetQuery `json:"has_internet"` //0 all;1 no internet;2 has internet
TimeLimit int `json:"time_limit" binding:"Range(-1,24)"`
ParamArray models.Parameters
ComputeSource *models.ComputeSource
ReqCommitID string
@@ -145,6 +146,7 @@ type AITaskDetailInfo struct {
UserId int64 `json:"-"`
AppName string `json:"app_name"`
HasInternet int `json:"has_internet"`
TimeLimit int `json:"time_limit"`
}

func (a *AITaskDetailInfo) Tr(language string) {


+ 1
- 0
entity/cluster.go View File

@@ -19,6 +19,7 @@ type CreateNoteBookTaskRequest struct {
Tasks []NoteBookTask
PrimitiveDatasetName string
RepoName string
IsSubscriber bool
}

type NoteBookTask struct {


+ 2
- 0
entity/creation.go View File

@@ -14,11 +14,13 @@ type CreationRequiredInfo struct {
DefaultBranch string `json:"default_branch"`
WaitCount int64 `json:"wait_count"`
NotStopTaskCount int `json:"not_stop_task_count"`
CanCreateMore bool `json:"can_create_more"`
DisplayJobName string `json:"display_job_name"`
PointAccount *PointAccountInfo `json:"point_account"`
PaySwitch bool `json:"pay_switch"`
Config AITaskCreationConfig `json:"config"`
AllowedWorkerNum []int `json:"allowed_worker_num"`
IsSubscriber bool `json:"is_subscriber"`
}

type ImageRequiredInfo struct {


+ 2
- 2
manager/client/cloudbrain_two/resty.go View File

@@ -273,7 +273,7 @@ sendjob:
return &result, nil
}

func ManageNotebook2(jobID string, param models.NotebookAction) (*models.NotebookActionResult, error) {
func ManageNotebook2(jobID string, param models.NotebookAction, autoStopDuration int) (*models.NotebookActionResult, error) {
checkSetting()
client := getRestyClient()
var result models.NotebookActionResult
@@ -285,7 +285,7 @@ sendjob:
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID + "/" + param.Action + "?duration=" + strconv.Itoa(AutoStopDurationMs))
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID + "/" + param.Action + "?duration=" + strconv.Itoa(autoStopDuration))

if err != nil {
return &result, fmt.Errorf("resty ManageNotebook2: %v", err)


+ 2
- 2
manager/client/cloudbrain_two_cd/resty.go View File

@@ -92,7 +92,7 @@ func GetNotebook(jobID string) (*models.GetNotebook2Result, error) {
return &result, nil
}

func ManageNotebook(jobID string, param models.NotebookAction) (*models.NotebookActionResult, error) {
func ManageNotebook(jobID string, param models.NotebookAction, autoStopDuration int) (*models.NotebookActionResult, error) {
var result models.NotebookActionResult

client := getHttpClient()
@@ -101,7 +101,7 @@ func ManageNotebook(jobID string, param models.NotebookAction) (*models.Notebook
Secret: setting.ModelartsCD.SecretKey,
}
r, _ := http.NewRequest(http.MethodPost,
setting.ModelartsCD.EndPoint+"/v1/"+setting.ModelartsCD.ProjectID+urlNotebook2+"/"+jobID+"/"+param.Action+"?duration="+strconv.Itoa(autoStopDurationMs),
setting.ModelartsCD.EndPoint+"/v1/"+setting.ModelartsCD.ProjectID+urlNotebook2+"/"+jobID+"/"+param.Action+"?duration="+strconv.Itoa(autoStopDuration),
nil)

r.Header.Add("content-type", "application/json")


+ 2
- 2
manager/client/grampus/grampus.go View File

@@ -561,7 +561,7 @@ sendjob:
return &result, nil
}

func RestartNotebookJob(jobID string) (*models.GrampusNotebookRestartResponse, error) {
func RestartNotebookJob(jobID string, autoStopDuration int64) (*models.GrampusNotebookRestartResponse, error) {
checkSetting()
client := getRestyClient()
var restartResponse *models.GrampusNotebookRestartResponse
@@ -571,7 +571,7 @@ sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&restartResponse).
Post(HOST + urlNotebookJob + "/" + jobID + "/start")
Post(HOST + urlNotebookJob + "/" + jobID + "/start?autoStopDuration=" + strconv.FormatInt(autoStopDuration, 10))

if err != nil {
return nil, fmt.Errorf("resty grampus restart note book job: %v", err)


+ 25
- 22
models/cloudbrain.go View File

@@ -332,6 +332,7 @@ type Cloudbrain struct {
Config *CloudbrainConfig `xorm:"-"`
AppName string //超算任务的应用类型
HasInternet int
TimeLimit int `xorm:"DEFAULT 0"` //just for subscriber
}

type CloudbrainShow struct {
@@ -2111,7 +2112,8 @@ type GetGrampusJobListResponse struct {

type GrampusNotebookResponse struct {
GrampusResult
JobInfo GrampusNotebookInfo `json:"otJob"`
JobInfo GrampusNotebookInfo `json:"otJob"`
ExitDiagnostics string `json:"exitDiagnostics"`
}

type GrampusNotebookRestartResponse struct {
@@ -2169,26 +2171,27 @@ type GrampusTasks struct {
RunParams map[string]interface{} `json:"runParams"`
}
type GrampusNotebookTask struct {
AutoStopDuration int64 `json:"autoStopDuration"`
Name string `json:"name"`
Capacity int `json:"capacity"`
CenterID []string `json:"centerID"`
CenterName []string `json:"centerName"`
PoolId string `json:"poolId"`
Code GrampusDataset `json:"code"`
Datasets []GrampusDataset `json:"datasets"`
PreTrainModel []GrampusDataset `json:"models"`
OutPut GrampusDataset `json:"output"`
CodeUrl string `json:"codeUrl"`
DataUrl string `json:"dataUrl"`
ImageId string `json:"imageId"`
ImageUrl string `json:"imageUrl"`
ResourceSpecId string `json:"resourceSpecId"`
Token string `json:"token"`
Url string `json:"url"`
Status string `json:"status"`
Command string `json:"command"`
EnvVariables GrampusEnvVarReq `json:"envVariables"`
AutoStopDuration int64 `json:"autoStopDuration"`
Name string `json:"name"`
Capacity int `json:"capacity"`
CenterID []string `json:"centerID"`
CenterName []string `json:"centerName"`
PoolId string `json:"poolId"`
Code GrampusDataset `json:"code"`
Datasets []GrampusDataset `json:"datasets"`
PreTrainModel []GrampusDataset `json:"models"`
OutPut GrampusDataset `json:"output"`
CodeUrl string `json:"codeUrl"`
DataUrl string `json:"dataUrl"`
ImageId string `json:"imageId"`
ImageUrl string `json:"imageUrl"`
ResourceSpecId string `json:"resourceSpecId"`
Token string `json:"token"`
Url string `json:"url"`
Status string `json:"status"`
Command string `json:"command"`
EnvVariables GrampusEnvVarReq `json:"envVariables"`
NoActAutoShutDownTimeout int `json:"noActAutoShutDownTimeout"`
}

type GrampusInferenceTask struct {
@@ -2890,7 +2893,7 @@ func GetCloudBrainUnStoppedJob() ([]*Cloudbrain, error) {
NotIn("status",
JobStopped, JobSucceeded, JobFailed, ModelArtsCreateFailed, ModelArtsStartFailed, ModelArtsUnavailable, ModelArtsResizFailed, ModelArtsDeleted,
ModelArtsStopped, ModelArtsTrainJobCanceled, ModelArtsTrainJobCheckFailed, ModelArtsTrainJobCompleted, ModelArtsTrainJobDeleteFailed, ModelArtsTrainJobDeployServiceFailed,
ModelArtsTrainJobFailed, ModelArtsTrainJobImageFailed, ModelArtsTrainJobKilled, ModelArtsTrainJobLost, ModelArtsTrainJobSubmitFailed, ModelArtsTrainJobSubmitModelFailed).
ModelArtsTrainJobFailed, ModelArtsTrainJobImageFailed, ModelArtsTrainJobKilled, ModelArtsTrainJobLost, ModelArtsTrainJobSubmitFailed, ModelArtsTrainJobSubmitModelFailed, LocalStatusFailed).
// Limit(1000).
Find(&cloudbrains)
}


+ 10
- 0
models/role.go View File

@@ -12,6 +12,7 @@ const (
TechProgramAdmin RoleType = "TechProgramAdmin"
RewardPointAdmin RoleType = "RewardPointAdmin"
MonitorAdmin RoleType = "MonitorAdmin"
Subscriber RoleType = "Subscriber"
)

type Role struct {
@@ -45,6 +46,15 @@ func GetUserRoleByUserAndRole(userId int64, roleType RoleType) (*UserRole, error
return r, nil
}

func GetUserRoleList(userId int64) ([]UserRole, error) {
r := make([]UserRole, 0)
err := x.Where("user_id = ?", userId).Find(&r)
if err != nil {
return nil, err
}
return r, nil
}

func GetRoleByCode(code string) (*Role, error) {
r := &Role{}
has, err := x.Where("code = ?", code).Get(r)


+ 26
- 9
models/user.go View File

@@ -186,7 +186,8 @@ type User struct {
WechatOpenId string `xorm:"INDEX"`
WechatBindUnix timeutil.TimeStamp
//Mobile phone
PhoneNumber string `xorm:"VARCHAR(255)"`
PhoneNumber string `xorm:"VARCHAR(255)"`
IsSubscriber bool `xorm:"-"`
}

type UserShow struct {
@@ -1775,14 +1776,16 @@ func GetUser(user *User) (bool, error) {
// SearchUserOptions contains the options for searching
type SearchUserOptions struct {
ListOptions
Keyword string
Type UserType
UID int64
OrderBy SearchOrderBy
Visible []structs.VisibleType
Actor *User // The user doing the search
IsActive util.OptionalBool
SearchByEmail bool // Search by email as well as username/full name
Keyword string
Type UserType
UID int64
OrderBy SearchOrderBy
Visible []structs.VisibleType
Actor *User // The user doing the search
IsActive util.OptionalBool
SearchByEmail bool // Search by email as well as username/full name
AdminType int // 0 all 1 admin 2 normal
SubscriberType int //0 all 1 subscriber 2 normal
}

func (opts *SearchUserOptions) toConds() builder.Cond {
@@ -1827,6 +1830,20 @@ func (opts *SearchUserOptions) toConds() builder.Cond {
}
cond = cond.And(accessCond)
}
if opts.AdminType > 0 {
if opts.AdminType == 1 {
cond = cond.And(builder.Eq{"is_admin": true})
} else {
cond = cond.And(builder.Eq{"is_admin": false})
}
}
if opts.SubscriberType > 0 {
if opts.SubscriberType == 1 {
cond = cond.And(builder.In("id", builder.Select("user_id").From("user_role").Where(builder.Eq{"role_type": Subscriber})))
} else {
cond = cond.And(builder.NotIn("id", builder.Select("user_id").From("user_role").Where(builder.Eq{"role_type": Subscriber})))
}
}

if opts.UID > 0 {
cond = cond.And(builder.Eq{"id": opts.UID})


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

@@ -42,6 +42,7 @@ type AdminEditUserForm struct {
AllowImportLocal bool
AllowCreateOrganization bool
ProhibitLogin bool
Subscriber bool
}

// Validate validates form fields


+ 7
- 0
modules/setting/setting.go View File

@@ -748,6 +748,7 @@ var (
SPECIFICATION_SPECIAL_QUEUE string
DEBUG_MODEL_NUM_LIMIT int
DEBUG_MODEL_SIZE_LIMIT_GB int
ROLE_MULTI_LIMIT_MAP map[string]map[string]int

//wenxin url
BaiduWenXin = struct {
@@ -1625,6 +1626,12 @@ func NewContext() {
SPECIFICATION_SPECIAL_QUEUE = sec.Key("SPECIFICATION_SPECIAL_QUEUE").MustString("{}")
DEBUG_MODEL_NUM_LIMIT = sec.Key("DEBUG_MODEL_NUM_LIMIT").MustInt(5)
DEBUG_MODEL_SIZE_LIMIT_GB = sec.Key("DEBUG_MODEL_SIZE_LIMIT_GB").MustInt(20)
DEBUG_MODEL_SIZE_LIMIT_GB = sec.Key("DEBUG_MODEL_SIZE_LIMIT_GB").MustInt(20)

roleLimitMap := map[string]map[string]int{}
tmpRoleLimitStr := sec.Key("ROLE_MULTI_LIMIT_MAP").MustString("")
json.Unmarshal([]byte(tmpRoleLimitStr), &roleLimitMap)
ROLE_MULTI_LIMIT_MAP = roleLimitMap

sec = Cfg.Section("benchmark")
IsBenchmarkEnabled = sec.Key("ENABLED").MustBool(false)


+ 1
- 5
modules/urfs_client/objectstorage/mocks/objectstorage_mock.go View File

@@ -1,9 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: objectstorage.go

// Package mocks is a generated GoMock package.
package mocks

import (
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)

+ 7
- 1
options/locale/locale_en-US.ini View File

@@ -548,6 +548,7 @@ still_has_org = "Your account is a member of one or more organizations; leave th
org_still_own_repo = "This organization still owns one or more repositories; delete or transfer them first."

target_branch_not_exist = Target branch does not exist.
fail_set_subscriber="Fail to config subscriber parameter."

[user]
change_avatar = Change your avatar…
@@ -2772,7 +2773,9 @@ users.name = Username
users.full_name = Full Name
users.activated = Activated
users.bind_phone = Bind Phone
users.subscriber = Subscriber
users.admin = Admin
users.common = Common User
users.restricted = Restricted
users.repos = Repos
users.created = Created
@@ -2802,6 +2805,9 @@ users.delete_account = Delete User Account
users.still_own_repo = This user still owns one or more repositories. Delete or transfer these repositories first.
users.still_has_org = This user is a member of an organization. Remove the user from any organizations first.
users.deletion_success = The user account has been deleted.
users.all_subscriber_or_not = All Subscribers or Not
users.free_users = Free Users
users.all_admin_or_not = All Admin or Not

emails.email_manage_panel = User Email Management
emails.primary = Primary
@@ -3488,7 +3494,7 @@ to_migrate_repo_exists = The project has been migrated to OpenI, please use the
task_not_exists = AI task is not exists
task_not_finished = AI task not finished
spec_not_available = Specification not available
multi_task = You have already a running or waiting task, can not create more
multi_task = You have already %d running or waiting task(s), can not create more
job_name_already_used = The job name did already exist
insufficient_point_balance = Insufficient point balance
create_failed = Create AI task failed


+ 7
- 1
options/locale/locale_zh-CN.ini View File

@@ -552,6 +552,7 @@ still_has_org=此帐户仍隶属于一个或多个组织,您需要退出他们
org_still_own_repo=该组织仍然是某些项目的拥有者,您必须先转移或删除它们才能执行删除组织操作!

target_branch_not_exist=目标分支不存在。
fail_set_subscriber=设置付费用户属性失败。

[user]
change_avatar=修改头像
@@ -2790,7 +2791,9 @@ users.name=用户名
users.full_name=全名
users.activated=已激活
users.bind_phone=手机验证
users.subscriber=付费用户
users.admin=管理员
users.common=普通用户
users.restricted=受限
users.repos=项目数
users.created=创建时间
@@ -2820,6 +2823,9 @@ users.delete_account=删除帐户
users.still_own_repo=此用户仍然拥有一个或多个项目。必须首先删除或转让这些项目。
users.still_has_org=此用户是组织的成员。必须先从组织中删除用户。
users.deletion_success=用户帐户已被删除。
users.all_subscriber_or_not=全部付费免费用户
users.free_users=免费用户
users.all_admin_or_not=全部管理员和普通用户

emails.email_manage_panel=邮件管理
emails.primary=主要的
@@ -3511,7 +3517,7 @@ to_migrate_repo_exists = 该项目已迁移到启智,请使用启智社区方
task_not_exists = AI任务不存在
task_not_finished = AI任务未结束
spec_not_available = 资源规格不可用
multi_task = 您已经有个正在等待或运行中的任务,请结束该任务再试
multi_task = 您已经有%d个正在等待或运行中的任务,请结束该任务再试
job_name_already_used = 任务名已被使用,请换一个名称
insufficient_point_balance = 积分余额不足
create_failed = 创建AI任务失败


+ 27
- 1
routers/admin/users.go View File

@@ -8,6 +8,8 @@ package admin
import (
"strings"

"code.gitea.io/gitea/services/role"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base"
@@ -29,16 +31,24 @@ const (

// Users show all the users
func Users(ctx *context.Context) {
AdminType := ctx.QueryInt("adminType")
SubscriberType := ctx.QueryInt("subscriberType")

ctx.Data["Title"] = ctx.Tr("admin.users")
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminUsers"] = true

ctx.Data["AdminType"] = AdminType
ctx.Data["SubscriberType"] = SubscriberType

routers.RenderUserSearch(ctx, &models.SearchUserOptions{
Type: models.UserTypeIndividual,
ListOptions: models.ListOptions{
PageSize: setting.UI.Admin.UserPagingNum,
},
SearchByEmail: true,
AdminType: AdminType,
SubscriberType: SubscriberType,
SearchByEmail: true,
}, tplUsers)
}

@@ -149,6 +159,7 @@ func prepareUserInfo(ctx *context.Context) *models.User {
ctx.ServerError("GetUserByID", err)
return nil
}
u.IsSubscriber = role.UserHasRole(u.ID, models.Subscriber)
ctx.Data["User"] = u

if u.LoginSource > 0 {
@@ -260,6 +271,21 @@ func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) {
}
return
}
if form.Subscriber && !u.IsSubscriber {
if err := role.AddUserRole(u.ID, models.Subscriber); err != nil {
log.Error("fail_set_subscriber", err)
ctx.RenderWithErr(ctx.Tr("form.fail_set_subscriber"), tplUserEdit, &form)
return
}
}
if !form.Subscriber && u.IsSubscriber {
if err := role.DeleteUserRole(u.ID, models.Subscriber); err != nil {
log.Error("fail_set_subscriber", err)
ctx.RenderWithErr(ctx.Tr("form.fail_set_subscriber"), tplUserEdit, &form)
return
}
}

log.Trace("Account profile updated by admin (%s): %s", ctx.User.Name, u.Name)

ctx.Flash.Success(ctx.Tr("admin.users.update_profile_success"))


+ 1
- 0
routers/api/v1/api.go View File

@@ -926,6 +926,7 @@ func RegisterRoutes(m *macaron.Macaron) {

m.Group("/user", func() {
m.Get("", user.GetAuthenticatedUser)
m.Get("/subscriber", user.IsSubscriber)
m.Get("/point_account", user.GetPointAccount)
m.Combo("/emails").Get(user.ListEmails).
Post(bind(api.CreateEmailOption{}), user.AddEmail).


+ 5
- 0
routers/api/v1/user/user.go View File

@@ -175,3 +175,8 @@ func IsRewardPointAdmin(ctx *context.APIContext) {
r["is_admin"] = isAdmin
ctx.JSON(http.StatusOK, response.OuterSuccessWithData(r))
}
func IsSubscriber(ctx *context.APIContext) {
r := map[string]interface{}{}
r["isSubscriber"] = role.UserHasRole(ctx.User.ID, models.Subscriber)
ctx.JSON(http.StatusOK, response.OuterSuccessWithData(r))
}

+ 11
- 3
routers/home.go View File

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

"code.gitea.io/gitea/services/role"

"code.gitea.io/gitea/routers/repo"

"code.gitea.io/gitea/routers/response"
@@ -55,10 +57,10 @@ const (
tplRepoSquare base.TplName = "explore/repos/square"
tplRepoSearch base.TplName = "explore/repos/search"
tplRoshmci base.TplName = "explore/ros-hmci"
tplExploreCenterMap base.TplName = "explore/center_map"
tplExploreCenterMap base.TplName = "explore/center_map"

tplComputingPowerDemand base.TplName = "computingpower/demand"
tplComputingPowerDomestic base.TplName = "computingpower/domestic"
tplComputingPowerDemand base.TplName = "computingpower/demand"
tplComputingPowerDomestic base.TplName = "computingpower/domestic"
)

// Home render home page
@@ -627,6 +629,10 @@ func RenderUserSearch(ctx *context.Context, opts *models.SearchUserOptions, tplN
ctx.ServerError("SearchUsers", err)
return
}
for _, user := range users {
user.IsSubscriber = role.UserHasRole(user.ID, models.Subscriber)
}

}
ctx.Data["Keyword"] = opts.Keyword
ctx.Data["Total"] = count
@@ -636,6 +642,8 @@ func RenderUserSearch(ctx *context.Context, opts *models.SearchUserOptions, tplN

pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "adminType", "AdminType")
pager.AddParam(ctx, "subscriberType", "SubscriberType")
ctx.Data["Page"] = pager

ctx.HTML(200, tplName)


+ 13
- 1
routers/repo/cloudbrain.go View File

@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io"
"math"
"net/http"
"os"
"regexp"
@@ -15,6 +16,8 @@ import (
"time"
"unicode/utf8"

"code.gitea.io/gitea/services/role"

"code.gitea.io/gitea/services/lock"

cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
@@ -2137,12 +2140,21 @@ func SyncCloudbrainStatus() {
if task.JobType == string(models.JobTypeModelSafety) {
continue
}
maxDuration := setting.MaxDuration
if role.UserHasRole(task.UserID, models.Subscriber) && task.TimeLimit != 0 {
if task.TimeLimit > 0 {
maxDuration = int64(task.TimeLimit * 60 * 60)
} else {
maxDuration = math.MaxInt64
}
}

task, _ = ai_task.UpdateCloudbrain(task)
if task.Duration >= setting.MaxDuration && task.JobType == string(models.JobTypeDebug) {
if task.Duration >= maxDuration && task.JobType == string(models.JobTypeDebug) || task.Duration >= setting.Grampus.MMLSparkMaxTime && task.JobType == string(models.JobTypeSuperCompute) {
ai_task.StopCloudbrain(task)
}
go ai_task.TryToNotifyLongRunningTask(task)

}

return


+ 8
- 13
services/ai_task_service/cluster/c2net.go View File

@@ -145,19 +145,13 @@ func convertNoteBookReq2Grampus(req entity.CreateNoteBookTaskRequest) (models.Cr
}
}

var commandGpuDebug = "mkdir -p /tmp/dataset;jupyter lab --ServerApp.shutdown_no_activity_timeout=%s --TerminalManager.cull_inactive_timeout=%s --TerminalManager.cull_interval=%s --MappingKernelManager.cull_idle_timeout=%s --MappingKernelManager.cull_interval=%s --MappingKernelManager.cull_connected=True --MappingKernelManager.cull_busy=True --no-browser --ip=0.0.0.0 --allow-root --notebook-dir='%s' --port=$OCTOPUS_NOTEBOOK_PORT --LabApp.token='' --LabApp.allow_origin='*' --LabApp.base_url=$OCTOPUS_NOTEBOOK_BASE_URL;"
command := fmt.Sprintf(commandGpuDebug, setting.CullIdleTimeout, setting.CullIdleTimeout, setting.CullInterval, setting.CullIdleTimeout, setting.CullInterval, codePath)
if models.DCU == req.Tasks[0].Spec.ComputeResource {
command = ""
}
if models.NPU == req.Tasks[0].Spec.ComputeResource {
command = ""
}
log.Info("debug cmd=" + command)
tasks := make([]models.GrampusNotebookTask, len(req.Tasks))
for i := 0; i < len(req.Tasks); i++ {
t := req.Tasks[i]
task, err := convertNoteBookTask2Grampus(t, command)
task, err := convertNoteBookTask2Grampus(t, "")
if !req.IsSubscriber {
task.NoActAutoShutDownTimeout = 1800000
}
if err != nil {
return models.CreateGrampusNotebookRequest{}, err
}
@@ -479,8 +473,8 @@ func convertGrampus2GeneralTaskRes(res *models.GrampusNotebookResponse) *entity.
}
}

func (c C2NetClusterAdapter) RestartNoteBook(jobId string) (*entity.RestartNoteBookTaskResponse, error) {
res, err := grampus.RestartNotebookJob(jobId)
func (c C2NetClusterAdapter) RestartNoteBook(jobId string, autoStopDuration int64) (*entity.RestartNoteBookTaskResponse, error) {
res, err := grampus.RestartNotebookJob(jobId, autoStopDuration)
if err != nil {
log.Error("RestartNotebookJob err jobId=%s .%v", jobId, err)
return nil, err
@@ -598,7 +592,8 @@ func (c C2NetClusterAdapter) GetNoteBookOperationProfile(jobId string) (*entity.
}

r := parseC2NetEventsToOperationProfile(jobResult.NotebookEvents)
getJobResult, err := grampus.GetJob(jobId)

getJobResult, err := grampus.GetNotebookJob(jobId)
if err == nil && getJobResult != nil && getJobResult.ExitDiagnostics != "" {
r.Events = append(r.Events, entity.ProfileEvent{
Message: getJobResult.ExitDiagnostics,


+ 1
- 1
services/ai_task_service/cluster/cloudbrain_one.go View File

@@ -108,7 +108,7 @@ func convertCloudbrainOne2NoteBookRes(res *models.CreateJobResult) *entity.Creat
}
}

func (c CloudbrainOneClusterAdapter) RestartNoteBook(string) (*entity.RestartNoteBookTaskResponse, error) {
func (c CloudbrainOneClusterAdapter) RestartNoteBook(string, int64) (*entity.RestartNoteBookTaskResponse, error) {

return nil, nil
}


+ 5
- 5
services/ai_task_service/cluster/cloudbrain_two.go View File

@@ -170,7 +170,7 @@ func convertCloudbrainTwo2NoteBookRes(res *models.CreateNotebookResult) *entity.
}
}

func (c CloudbrainTwoClusterAdapter) RestartNoteBook(jobId string) (*entity.RestartNoteBookTaskResponse, error) {
func (c CloudbrainTwoClusterAdapter) RestartNoteBook(jobId string, autoStopDuration int64) (*entity.RestartNoteBookTaskResponse, error) {
param := models.NotebookAction{
Action: models.ActionStart,
}
@@ -181,9 +181,9 @@ func (c CloudbrainTwoClusterAdapter) RestartNoteBook(jobId string) (*entity.Rest

var res *models.NotebookActionResult
if task.Type == models.TypeCloudBrainTwo {
res, err = cloudbrain_two.ManageNotebook2(task.JobID, param)
res, err = cloudbrain_two.ManageNotebook2(task.JobID, param, int(autoStopDuration))
} else if task.Type == models.TypeCDCenter {
res, err = cloudbrain_two_cd.ManageNotebook(task.JobID, param)
res, err = cloudbrain_two_cd.ManageNotebook(task.JobID, param, int(autoStopDuration))
}
if err != nil {
log.Error("ManageNotebook err.jobID=%s err=%v", jobId, err)
@@ -227,9 +227,9 @@ func (c CloudbrainTwoClusterAdapter) StopNoteBook(opts entity.JobIdAndVersionId)
Action: models.ActionStop,
}
if task.Type == models.TypeCloudBrainTwo {
_, err = cloudbrain_two.ManageNotebook2(task.JobID, param)
_, err = cloudbrain_two.ManageNotebook2(task.JobID, param, 0)
} else if task.Type == models.TypeCDCenter {
_, err = cloudbrain_two_cd.ManageNotebook(task.JobID, param)
_, err = cloudbrain_two_cd.ManageNotebook(task.JobID, param, 0)
}
if err != nil {
log.Error("StopNoteBook err.jobID=%s err=%v", opts, err)


+ 1
- 1
services/ai_task_service/cluster/cluster_base.go View File

@@ -24,7 +24,7 @@ func GetCluster(t entity.ClusterType) (ClusterAdapter, error) {

type ClusterAdapter interface {
CreateNoteBook(req entity.CreateNoteBookTaskRequest) (*entity.CreateNoteBookTaskResponse, error)
RestartNoteBook(jobId string) (*entity.RestartNoteBookTaskResponse, error)
RestartNoteBook(jobId string, autoStopDuration int64) (*entity.RestartNoteBookTaskResponse, error)
DeleteNoteBook(opts entity.JobIdAndVersionId) error
StopNoteBook(opts entity.JobIdAndVersionId) error
QueryNoteBook(opts entity.JobIdAndVersionId) (*entity.QueryTaskResponse, error)


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

@@ -1,6 +1,8 @@
package task

import (
"strings"

"code.gitea.io/gitea/entity"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cloudbrain"
@@ -9,7 +11,6 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/routers/response"
"code.gitea.io/gitea/services/ai_task_service/context"
"strings"
)

type CloudbrainOneNotebookTaskTemplate struct {
@@ -153,7 +154,7 @@ func (g CloudbrainOneNotebookTaskTemplate) CallCreationAPI(ctx *context.Creation
Code: ctx.GetContainerDataArray(entity.ContainerCode),
PreTrainModel: ctx.GetContainerDataArray(entity.ContainerPreTrainModel),
OutPut: ctx.GetContainerDataArray(entity.ContainerOutPutPath),
AutoStopDuration: autoStopDurationMs,
AutoStopDuration: getAutoStopDurationMs(ctx.NewCloudbrain.TimeLimit, ctx.User.ID),
Capacity: setting.Capacity,
Queues: ctx.Queues,
Spec: ctx.Spec,


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

@@ -130,7 +130,7 @@ func (g CloudbrainTwoNotebookTaskTemplate) CallCreationAPI(ctx *context.Creation
ResourceSpecId: ctx.Spec.SourceSpecId,
ImageId: form.ImageID,
ImageUrl: strings.TrimSpace(form.ImageUrl),
AutoStopDuration: autoStopDurationMs,
AutoStopDuration: getAutoStopDurationMs(ctx.NewCloudbrain.TimeLimit, ctx.User.ID),
Spec: ctx.Spec,
Datasets: ctx.GetContainerDataArray(entity.ContainerDataset),
Code: ctx.GetContainerDataArray(entity.ContainerCode),
@@ -161,7 +161,8 @@ func (g CloudbrainTwoNotebookTaskTemplate) CallRestartAPI(ctx *context.CreationC
return response.SYSTEM_ERROR
}
createTime := timeutil.TimeStampNow()
res, err := c.RestartNoteBook(ctx.SourceCloudbrain.JobID)
autoStopDuration := getAutoStopDurationMs(ctx.SourceCloudbrain.TimeLimit, ctx.User.ID)
res, err := c.RestartNoteBook(ctx.SourceCloudbrain.JobID, autoStopDuration)
if err != nil {
log.Error("CloudbrainTwoNotebookTaskTemplate RestartNoteBook err.Cloudbrain.JobID=%s err=%v", ctx.SourceCloudbrain.JobID, err)
return response.NewBizError(err)


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

@@ -3,6 +3,8 @@ package task
import (
"strings"

"code.gitea.io/gitea/services/role"

"code.gitea.io/gitea/entity"

"code.gitea.io/gitea/models"
@@ -238,6 +240,17 @@ func (t GrampusNoteBookTaskTemplate) Restart(ctx *context.CreationContext) (*ent

var autoStopDurationMs = int64(4 * 60 * 60 * 1000)

func getAutoStopDurationMs(taskTimeLimit int, uid int64) int64 {
if taskTimeLimit == 0 || !role.UserHasRole(uid, models.Subscriber) {
return autoStopDurationMs
} else {
if taskTimeLimit > 0 {
return int64(taskTimeLimit * 60 * 60 * 1000)
}
return -1
}
}

func (g GrampusNoteBookTaskTemplate) CallCreationAPI(ctx *context.CreationContext) *response.BizError {
c := g.GetMyCluster()
if c == nil {
@@ -261,12 +274,13 @@ func (g GrampusNoteBookTaskTemplate) CallCreationAPI(ctx *context.CreationContex
PreTrainModel: ctx.GetContainerDataArray(entity.ContainerPreTrainModel),
Code: ctx.GetContainerDataArray(entity.ContainerCode),
EnvVariables: models.GrampusEnvVarReq{},
AutoStopDuration: autoStopDurationMs,
AutoStopDuration: getAutoStopDurationMs(ctx.NewCloudbrain.TimeLimit, ctx.User.ID),
Capacity: setting.Capacity,
Queues: ctx.Queues,
Spec: ctx.Spec,
},
},
IsSubscriber: role.UserHasRole(ctx.User.ID, models.Subscriber),
}
createTime := timeutil.TimeStampNow()
res, err := c.CreateNoteBook(req)
@@ -293,7 +307,8 @@ func (g GrampusNoteBookTaskTemplate) CallRestartAPI(ctx *context.CreationContext
return response.SYSTEM_ERROR
}
createTime := timeutil.TimeStampNow()
res, err := c.RestartNoteBook(ctx.SourceCloudbrain.JobID)
autoStopDuration := getAutoStopDurationMs(ctx.SourceCloudbrain.TimeLimit, ctx.User.ID)
res, err := c.RestartNoteBook(ctx.SourceCloudbrain.JobID, autoStopDuration)
if err != nil {
log.Error("GrampusNoteBookTask RestartNoteBook err.Cloudbrain.JobID=%s err=%v", ctx.SourceCloudbrain.JobID, err)
return response.NewBizError(err)


+ 12
- 2
services/ai_task_service/task/opt_handler.go View File

@@ -6,6 +6,8 @@ import (
"path"
"strings"

"code.gitea.io/gitea/services/role"

"code.gitea.io/gitea/entity"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
@@ -106,6 +108,7 @@ func (DefaultCreationHandler) BuildRequest4Restart(ctx *context.CreationContext)
IsRestartRequest: true,
DatasetNames: task.DatasetName,
HasInternet: models.SpecInternetQuery(task.HasInternet),
TimeLimit: task.TimeLimit,
}
log.Info("BuildRequest4Restart success.displayJobName=%s jobType=%s cluster=%s", ctx.Request.DisplayJobName, ctx.Request.JobType, ctx.Request.Cluster)
return nil
@@ -349,9 +352,10 @@ func (DefaultCreationHandler) CheckMultiRequest(ctx *context.CreationContext) *r
log.Error("GetGrampusCountByUserID failed:%v", err)
return response.SYSTEM_ERROR
}
if count >= 1 {
limitNum := GetUserMultiLimitNum(ctx.User.ID, ctx.Request.JobType)
if count >= limitNum {
log.Error("the user already has running or waiting task.")
return response.MULTI_TASK
return response.MULTI_TASK.WithParams(count)
}
log.Info("CheckMulti success.displayJobName=%s jobType=%s cluster=%s", ctx.Request.DisplayJobName, ctx.Request.JobType, ctx.Request.Cluster)

@@ -418,6 +422,10 @@ func (DefaultCreationHandler) InsertCloudbrainRecord4Async(ctx *context.Creation
if req.IsFileNoteBookRequest {
branchName = req.FileBranchName
}
//if a normal user set TimeLimit, it does not work
if !role.UserHasRole(ctx.User.ID, models.Subscriber) {
req.TimeLimit = 0
}
c := &models.Cloudbrain{
Status: models.LocalStatusPreparing,
UserID: ctx.User.ID,
@@ -450,6 +458,7 @@ func (DefaultCreationHandler) InsertCloudbrainRecord4Async(ctx *context.Creation
GpuQueue: ctx.Spec.QueueCode,
AppName: req.AppName,
HasInternet: int(req.HasInternet),
TimeLimit: req.TimeLimit,
}

err := models.CreateCloudbrain(c)
@@ -618,6 +627,7 @@ func (DefaultCreationHandler) CreateCloudbrainRecord4Restart(ctx *context.Creati
ModelId: req.PretrainModelId,
GpuQueue: ctx.Spec.QueueCode,
HasInternet: int(req.HasInternet),
TimeLimit: ctx.SourceCloudbrain.TimeLimit,
}
err := models.RestartCloudbrain(ctx.SourceCloudbrain, c)



+ 8
- 4
services/ai_task_service/task/task_creation_info.go View File

@@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/routers/response"
"code.gitea.io/gitea/services/cloudbrain/cloudbrainTask"
"code.gitea.io/gitea/services/reward/point/account"
"code.gitea.io/gitea/services/role"
)

func GetAITaskCreationInfo(req entity.GetAITaskCreationInfoReq) (*entity.CreationRequiredInfo, *response.BizError) {
@@ -19,13 +20,15 @@ func GetAITaskCreationInfo(req entity.GetAITaskCreationInfoReq) (*entity.Creatio
waitCount := cloudbrain.GetWaitingCloudbrainCount(req.ClusterType.GetCloudbrainType(), req.ComputeSource.GetCloudbrainFormat(), req.JobType)
result.WaitCount = waitCount
//查询是否有正在运行的任务
notStopTaskCount := 0
if req.IsOnlineType {
notStopTaskCount, _ := cloudbrainTask.GetNotFinalStatusTaskCount(req.User.ID, string(models.JobTypeOnlineInference))
result.NotStopTaskCount = notStopTaskCount
notStopTaskCount, _ = cloudbrainTask.GetNotFinalStatusTaskCount(req.User.ID, string(models.JobTypeOnlineInference))
} else {
notStopTaskCount, _ := cloudbrainTask.GetNotFinalStatusTaskCount(req.User.ID, string(req.JobType))
result.NotStopTaskCount = notStopTaskCount
notStopTaskCount, _ = cloudbrainTask.GetNotFinalStatusTaskCount(req.User.ID, string(req.JobType))
}
result.NotStopTaskCount = notStopTaskCount
limitNum := GetUserMultiLimitNum(req.User.ID, req.JobType)
result.CanCreateMore = notStopTaskCount < limitNum

//获取代码分支
if req.GitRepo != nil {
@@ -87,6 +90,7 @@ func GetAITaskCreationInfo(req entity.GetAITaskCreationInfoReq) (*entity.Creatio
} else {
result.AllowedWorkerNum = []int{1}
}
result.IsSubscriber = role.UserHasRole(req.User.ID, models.Subscriber)
return result, nil
}



+ 20
- 0
services/ai_task_service/task/task_service.go View File

@@ -21,6 +21,7 @@ import (
"encoding/json"
"errors"
"fmt"
"math"
"net/http"
"net/url"
"path"
@@ -168,6 +169,7 @@ func buildAITaskInfo(task *models.Cloudbrain, creator *models.User, config *enti
UserId: task.UserID,
AppName: task.AppName,
HasInternet: task.HasInternet,
TimeLimit: task.TimeLimit,
}, nil
}

@@ -922,3 +924,21 @@ func hasLongRunningNotificationSendBefore(cloudbrain *models.Cloudbrain, duratio
}
return !success
}

func GetUserMultiLimitNum(userId int64, jobType models.JobType) int {
defaultLimit := 1
roleList, err := models.GetUserRoleList(userId)
if err != nil || len(roleList) == 0 {
return defaultLimit
}
roleLimitMap := setting.ROLE_MULTI_LIMIT_MAP[string(jobType)]
if roleLimitMap == nil || len(roleLimitMap) == 0 {
return defaultLimit

}
realNum := defaultLimit
for _, r := range roleList {
realNum = int(math.Max(float64(roleLimitMap[string(r.RoleType)]), float64(realNum)))
}
return realNum
}

+ 5
- 0
services/role/role.go View File

@@ -18,6 +18,11 @@ var roleMap = map[models.RoleType]*models.Role{
Name: "监测管理员",
Description: "拥有监测的管理员权限",
},
models.Subscriber: {
Type: models.Subscriber,
Name: "Subscriber",
Description: "Subscriber",
},
}

func GetRole(roleType models.RoleType) *models.Role {


+ 6
- 0
templates/admin/user/edit.tmpl View File

@@ -109,6 +109,12 @@
</div>
</div>
{{end}}
<div class="inline field">
<div class="ui checkbox">
<label><strong>{{.i18n.Tr "admin.users.subscriber"}}</strong></label>
<input name="subscriber" type="checkbox" {{if .User.IsSubscriber}}checked{{end}}>
</div>
</div>

<div class="ui divider"></div>



+ 66
- 0
templates/admin/user/list.tmpl View File

@@ -12,6 +12,54 @@
<div class="ui attached segment">
{{template "admin/base/search" .}}
</div>
<div class="ui attached segment">
<div class="ui selection dropdown selected subscriberTypeSel" style="margin-right:10px">
<input type="hidden" name="subscriberType">
<i class="dropdown icon"></i>
<div class="text">
{{if not (or (eq .SubscriberType 1) (eq .SubscriberType 2))}}{{.i18n.Tr "admin.users.all_subscriber_or_not"}}{{end}}
{{if eq .SubscriberType 1}}{{.i18n.Tr "admin.users.subscriber"}}{{end}}
{{if eq .SubscriberType 2}}{{.i18n.Tr "admin.users.free_users"}}{{end}}
</div>
<div class="menu">
<a class="item {{if not (or (eq .SubscriberType 1) (eq .SubscriberType 2))}}active selected{{end}}" data-value="0"
href="{{$.Link}}?adminType={{.AdminType}}&sort={{.SortType}}&q={{$.Keyword}}">
{{.i18n.Tr "admin.users.all_subscriber_or_not"}}
</a>
<a class="item {{if eq .SubscriberType 1}}active selected{{end}}" data-value="1"
href="{{$.Link}}?sort={{.SortType}}&q={{$.Keyword}}&subscriberType=1&adminType={{.AdminType}}">
{{.i18n.Tr "admin.users.subscriber"}}
</a>
<a class="item {{if eq .SubscriberType 2}}active selected{{end}}" data-value="2"
href="{{$.Link}}?sort={{.SortType}}&q={{$.Keyword}}&subscriberType=2&adminType={{.AdminType}}">
{{.i18n.Tr "admin.users.free_users"}}
</a>
</div>
</div>
<div class="ui selection dropdown selected adminTypeSel">
<input type="hidden" name="adminType">
<i class="dropdown icon"></i>
<div class="text">
{{if not (or (eq .AdminType 1) (eq .AdminType 2))}}{{.i18n.Tr "admin.users.all_admin_or_not"}}{{end}}
{{if eq .AdminType 1}}{{.i18n.Tr "admin.users.admin"}}{{end}}
{{if eq .AdminType 2}}{{.i18n.Tr "admin.users.common"}}{{end}}
</div>
<div class="menu">
<a class="item {{if not (or (eq .AdminType 1) (eq .AdminType 2))}}active selected{{end}}" data-value="0"
href="{{$.Link}}?sort={{.SortType}}&q={{$.Keyword}}&subscriberType={{.SubscriberType}}">
{{.i18n.Tr "admin.users.all_admin_or_not"}}
</a>
<a class="item {{if eq .AdminType 1}}active selected{{end}}" data-value="1"
href="{{$.Link}}?sort={{.SortType}}&q={{$.Keyword}}&adminType=1&subscriberType={{.SubscriberType}}">
{{.i18n.Tr "admin.users.admin"}}
</a>
<a class="item {{if eq .AdminType 2}}active selected{{end}}" data-value="2"
href="{{$.Link}}?sort={{.SortType}}&q={{$.Keyword}}&adminType=2&subscriberType={{.SubscriberType}}">
{{.i18n.Tr "admin.users.common"}}
</a>
</div>
</div>
</div>
<div class="ui attached table segment">
<table class="ui very basic striped table">
<thead>
@@ -21,6 +69,7 @@
<th>{{.i18n.Tr "email"}}</th>
<th>{{.i18n.Tr "admin.users.activated"}}</th>
<th>{{.i18n.Tr "admin.users.bind_phone"}}</th>
<th>{{.i18n.Tr "admin.users.subscriber"}}</th>
<th>{{.i18n.Tr "admin.users.admin"}}</th>
<th>{{.i18n.Tr "admin.users.restricted"}}</th>
<th>{{.i18n.Tr "admin.users.repos"}}</th>
@@ -37,6 +86,7 @@
<td><span class="text truncate email">{{.Email}}</span></td>
<td><i class="fa fa{{if .IsActive}}-check{{end}}-square-o"></i></td>
<td><i class="fa fa{{if .PhoneNumber}}-check{{end}}-square-o"></i></td>
<td><i class="fa fa{{if .IsSubscriber}}-check{{end}}-square-o"></i></td>
<td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td>
<td><i class="fa fa{{if .IsRestricted}}-check{{end}}-square-o"></i></td>
<td>{{.NumRepos}}</td>
@@ -57,3 +107,19 @@
</div>
</div>
{{template "base/footer" .}}
<script>
;(function(){
var params = new URLSearchParams(window.location.search);
var subscriberType = params.get('subscriberType') || '';
var adminType = params.get('adminType') || '';
var sort = params.get('sort') || '';
$('.segment .ui.form .action').append('<input name="subscriberType" value="' + subscriberType +'" style="display:none">');
$('.segment .ui.form .action').append('<input name="adminType" value="' + adminType +'" style="display:none">');
$('.segment .ui.form .action').append('<input name="sort" value="' + sort +'" style="display:none">');
$('.segment .filter.menu .menu .item').each(function(index, item) {
var href = $(item).attr('href');
href += '&subscriberType=' + subscriberType + '&adminType=' + adminType;
$(item).attr('href', href);
});
})();
</script>

+ 155
- 0
web_src/vuepages/components/cloudbrain/RunTimeLimit.vue View File

@@ -0,0 +1,155 @@
<template>
<div class="form-row">
<div class="left-area">
<div class="title">
<span :class="required ? 'required' : ''">{{ $t('cloudbrainObj.taskIsAutomaticStop') }}</span>
</div>
<div class="content">
<el-select class="spec-sel field-input" v-model="type" @change="changeType">
<el-option :value="1" :label="$t('cloudbrainObj.automaticStop')" key="1"></el-option>
<el-option :value="2" :label="$t('cloudbrainObj.manualStop')" key="2"></el-option>
</el-select>
<div v-if="type == 1" class="tips">{{ $t('cloudbrainObj.automaticStopTips') }}</div>
<div v-if="type == 2" class="tips">{{ $t('cloudbrainObj.manualStopTips') }}</div>
<el-radio-group class="time-group-sel" v-if="type == 1" v-model="limitTime" @change="changeTime">
<el-radio :label="1">{{ $t('cloudbrainObj.numOfHours', { num: 1 }) }}</el-radio>
<el-radio :label="2">{{ $t('cloudbrainObj.numOfHours', { num: 2 }) }}</el-radio>
<el-radio :label="4">{{ $t('cloudbrainObj.numOfHours', { num: 4 }) }}</el-radio>
<el-radio :label="6">{{ $t('cloudbrainObj.numOfHours', { num: 6 }) }}</el-radio>
<br>
<el-radio class="custom" :label="-1">{{ $t('cloudbrainObj.customize') }}</el-radio>
<div class="limit-time-inp-c content" :class="errStatus ? 'error' : ''" v-if="type == 1 && limitTime == -1">
<div class="limit-time-inp-wrap">
<el-input class="limit-time-inp field-input" v-model="limitTimeInputValue"
@input="handleInput"></el-input>
<div class="unit">
{{ $t('cloudbrainObj.hours') }} ({{ $t('cloudbrainObj.customizeTimeLimitPlaceholder') }})
</div>
</div>
</div>
</el-radio-group>
</div>
</div>
<div class="right-area"></div>
</div>
</template>

<script>

export default {
name: 'RunTimeLimit',
props: {
value: { type: String, required: true },
required: { type: Boolean, default: true },
},
data() {
return {
type: 1,
limitTime: 4,
limitTimeInputValue: '',
currentValue: '',
errStatus: false,
};
},
watch: {
value: {
immediate: true,
handler(newVal) {
newVal = newVal === undefined ? '' : newVal;
this.currentValue = newVal.toString();
}
}
},
methods: {
check() {
if (this.type == 2) {
this.currentValue = '-1';
this.errStatus = false;
} else {
if (this.limitTime == -1) {
const value = parseInt(this.limitTimeInputValue);
if (value >= 1 && value <= 24) {
this.limitTimeInputValue = value;
this.currentValue = this.limitTimeInputValue.toString();
this.errStatus = false;
} else {
this.currentValue = '';
this.limitTimeInputValue = '';
this.errStatus = true;
}
} else {
this.currentValue = this.limitTime.toString();
this.errStatus = false;
}
}
return !this.errStatus;
},
changeType() {
this.check();
this.errStatus = false;
this.$emit('input', this.currentValue);
},
changeTime() {
this.check();
this.errStatus = false;
this.$emit('input', this.currentValue);
},
handleInput() {
const value = parseInt(this.limitTimeInputValue);
if (value >= 1 && value <= 24) {
this.limitTimeInputValue = value;
} else {
this.limitTimeInputValue = this.limitTimeInputValue.slice(0, -1);
}
this.check();
this.$emit('input', this.currentValue);
},
},
beforeMount() { }
};
</script>

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

.time-group-sel {
margin-top: 12px;
width: 100%;

/deep/ .el-radio {
margin-bottom: 16px;
}

.custom {
margin-right: 10px;
}

.limit-time-inp-c {
display: inline-block;
position: relative;
width: auto !important;

.limit-time-inp-wrap {
display: flex;
align-items: center;

/deep/.el-input__inner {
height: 32px !important;
line-height: 32px !important;
font-size: 13px !important;
text-align: center;
}
}

.limit-time-inp {
width: 60px !important;
}

.unit {
margin-left: 5px;
font-size: 14px;
color: #606266;
}
}
}
</style>

+ 15
- 3
web_src/vuepages/components/cloudbrain/details/ConfigInfo.vue View File

@@ -4,12 +4,12 @@
<div :key="item + '-' + index"
v-if="item != 'dataset' && item != 'modelList' && item != 'failedReason' && item != 'generalTaskCodeTips'"
class="item-block">
<div class="title"> {{ renderTitle(item) }} </div>
<div class="title" :title="renderTitle(item)">{{ renderTitle(item) }}</div>
<div class="content" v-html="renderContent(item)"></div>
</div>
<div :key="item + '-' + index" v-if="item == 'failedReason' && renderContent(item)"
class="item-block item-failed-reason">
<div class="title"> {{ renderTitle(item) }} </div>
<div class="title" :title="renderTitle(item)">{{ renderTitle(item) }}</div>
<div class="content" v-html="renderContent(item)"></div>
</div>
<div :key="item + '-' + index" v-if="item == 'dataset' && data.task.dataset_list.length > 0"
@@ -148,6 +148,7 @@ export default {
return hljs.highlight('python', str).value;
},
renderTitle(key) {
const task = this.data.task;
let result = key;
switch (key) {
case 'taskName':
@@ -240,6 +241,9 @@ export default {
case 'failedReason':
result = i18n.t('cloudbrainObj.failedReason');
break;
case 'timeLimit':
result = task.time_limit == 0 ? '' : i18n.t('cloudbrainObj.taskIsAutomaticStop');
break;
default:
break;
}
@@ -369,6 +373,14 @@ export default {
case 'failedReason':
result = task.failed_reason;
break;
case 'timeLimit':
if (task.time_limit == 0) {
result = '';
} else if (task.time_limit == -1) {
result = i18n.t('cloudbrainObj.manualStop');
} else {
result = i18n.t('cloudbrainObj.automaticStop') + `(${i18n.t('cloudbrainObj.numOfHours', { num: task.time_limit })})`;
}
default:
break;
}
@@ -396,7 +408,7 @@ export default {
min-width: 200px;

.title {
width: 105px;
width: 118px;
padding-right: 20px;
color: #8a8e99;
font-size: 12px;


+ 46
- 16
web_src/vuepages/components/cloudbrain/details/ResourceUseage.vue View File

@@ -40,7 +40,7 @@ const chartOptions = {
color: "#fff",
},
axisPointer: {
type: "line",
type: "cross",
},
appendToBody: true,
},
@@ -53,14 +53,23 @@ const chartOptions = {
},
name: "",
},
yAxis: {
yAxis: [{
show: true,
name: "(%)",
position: 'left',
axisLine: {
show: true,
},
axisTick: { show: true },
},
}, {
show: false,
name: "Value",
position: 'right',
axisLine: {
show: true,
},
axisTick: { show: true },
}],
series: [],
};

@@ -81,7 +90,10 @@ export default {
};
},
methods: {
getChartData() {
checkIsValueType(name) {
return name.indexOf('Bytes') >= 0 || name.indexOf('Rate') >= 0 || name.indexOf('numProcesses') >= 0;
},
getChartData(useRefreshBtn) {
const task = this.data.task;
this.loading = true;
getAiTaskResourceUseage({
@@ -97,21 +109,20 @@ export default {
const data = res.data || {};
const metricsInfo = data.metrics_info || [];
let filterData = metricsInfo.filter((item) => {
return ![
"recvBytesRate",
"diskWriteRate",
"sendBytesRate",
"diskReadRate",
].includes(item.name);
// return !["recvBytesRate", "diskWriteRate", "sendBytesRate", "diskReadRate",].includes(item.name);
return (item.value && item.value.length);
});
filterData = sortBy(filterData, "name");
const legenData = filterData.map((item) => {
return item.name;
});
let valueTypeCount = 0;
const seriesData = filterData.map((item) => {
const value = item.value.map((item) => {
return item > 0 ? item : "0";
const value = (item.value || []).map((item) => {
return item > 0 ? Number(Number(item).toFixed(3)) : "0";
});
const valueType = this.checkIsValueType(item.name);
valueTypeCount += (valueType ? 1 : 0);
const seriesOption = {
name: item.name,
type: "line",
@@ -119,6 +130,7 @@ export default {
symbolSize: 10,
smooth: true,
showSymbol: false,
yAxisIndex: valueType ? 1 : 0,
lineStyle: {
width: 2,
shadowColor: "rgba(0,0,0,0.3)",
@@ -136,6 +148,24 @@ export default {
(_, index) => index * xInterval
);
chartOptions.legend.data = legenData;
const legendSelected = {};
legenData.forEach(element => {
const valueType = this.checkIsValueType(element);
legendSelected[element] = !valueType;
});
if (valueTypeCount > 0) {
chartOptions.yAxis[1].show = true;
} else if (chartOptions.yAxis[1]) {
chartOptions.yAxis.pop();
}
if (useRefreshBtn && chartHandler && Object.keys(legendSelected).join('') == Object.keys(chartOptions.legend.selected || {}).join('')) {
try {
const selected = chartHandler.getOption().legend[0].selected;
chartOptions.legend.selected = selected;
} catch { }
} else {
chartOptions.legend.selected = legendSelected;
}
chartOptions.series = seriesData;
chartOptions.grid.top = '60';
if (legenData.length == 0) {
@@ -156,7 +186,7 @@ export default {
console.log(err);
});
},
refresh() {
refresh(useRefreshBtn) {
const task = this.data.task;
if (this.configs.multiNodes) {
this.loading = true;
@@ -169,13 +199,13 @@ export default {
if (res && res.code == 0) {
this.multiNodesData = res.data?.nodes || [];
}
this.getChartData();
this.getChartData(useRefreshBtn);
}).catch(err => {
this.loading = false;
console.log(err);
});
} else {
this.getChartData();
this.getChartData(useRefreshBtn);
}
},
changeNode() {
@@ -187,7 +217,7 @@ export default {
},
beforeMount() {
chartOptions.xAxis.name = this.$t('cloudbrainObj.chartTime');
chartOptions.yAxis.name = this.$t('cloudbrainObj.chartResourceUsage');
chartOptions.yAxis[0].name = this.$t('cloudbrainObj.chartResourceUsage');
},
mounted() {
window.addEventListener('resize', this.resize);


+ 11
- 1
web_src/vuepages/langs/config/en-US.js View File

@@ -612,7 +612,8 @@ const en = {
createTask: 'Create Task',
cluster: 'Resource cluster',
computeResource: 'Computing resources',
sameTaskTips1: 'You have created an <span>equivalent task</span> that is waiting or running, please wait for the task to finish before creating it.',
sameTaskTips0: 'You have created an <span>equivalent task</span> that is waiting or running, please wait for the task to finish before creating it.',
sameTaskTips1: 'You have created {count} <span>equivalent tasks</span> that are waiting or running, please wait for these task to finish before creating it.',
sameTaskTips2: 'You can view all your Cloud Brain tasks in <a href="/cloudbrains" target="_blank"> Home &gt; Cloudbrain Task </a>.',
pathTips1: 'The code is storaged in <strong style="color:#010101">{code}</strong>, the dataset is storaged in <strong style="color:#010101">{dataset}</strong>, the pre-trained model is storaged in the run parameter <strong style="color:#010101">{model}</strong>, and please put your model into <strong style="color:#010101">{output}</strong> then you can download it online.',
pathTips11: 'The code is storaged in <strong style="color:#010101">{code}</strong>, the dataset is storaged in <strong style="color:#010101">{dataset}</strong>, the pre-trained model is storaged in the <strong style="color:#010101">{model}</strong>, and please put your model into <strong style="color:#010101">{output}</strong> then you can download it online.',
@@ -796,6 +797,15 @@ const en = {
cloudbrainTaskType: 'Task Type',
repo: 'Repository',
cloudbrainTaskName: 'Cloudbrain Name',
taskIsAutomaticStop: 'Task is automatic stop',
automaticStop: 'Automatic stop',
manualStop: 'Manual stop',
automaticStopTips: 'This task will automatically stop when the running time exceeds the time you have selected, or when the point balance is insufficient.',
manualStopTips: 'This task will be manually stopped by you or automatically stopped when your point balance is insufficient.',
customize: 'Customize',
numOfHours: '{num} hours',
customizeTimeLimitPlaceholder: 'Please enter an integer between 1 and 24',
},
superComputeObj: {
mmlSparkDescr: `The full name of MMLSpark is Microsoft Machine Learning for Apache Spark, which enables users to run customized container images and grants them root accesses within the container. Users can directly use Microsoft's MMLSpark provided by the platform.\nNote: MMLSpark is a Spark version provided by Microsoft for machine learning environments( <a target="_blank" href="https://github.com/Azure/mmlspark">https://github.com/Azure/mmlspark</a> )Regarding mmlspark, please refer to the following paper: <a target="_blank" href="https://arxiv.org/pdf/1810.08744.pdf">https://arxiv.org/pdf/1810.08744.pdf</a>`,


+ 11
- 1
web_src/vuepages/langs/config/zh-CN.js View File

@@ -627,7 +627,8 @@ const zh = {
createTask: '新建任务',
cluster: '算力集群',
computeResource: '计算资源',
sameTaskTips1: '您已经有 <span>同类任务</span> 正在等待或运行中,请等待任务结束再创建;',
sameTaskTips0: '您已经有 <span>同类任务</span> 正在等待或运行中,请等待任务结束再创建;',
sameTaskTips1: '您已经有 {count}个 <span>同类任务</span> 正在等待或运行中,请等待任务结束再创建;',
sameTaskTips2: '可以在 “<a href="/cloudbrains" target="_blank">个人中心 &gt; 云脑任务</a>” 查看您所有的云脑任务。',
pathTips1: '训练脚本存储在 <strong style="color:#010101">{code}</strong> 中,数据集存储在 <strong style="color:#010101">{dataset}</strong> 中,预训练模型存放在运行参数 <strong style="color:#010101">{model}</strong> 中,训练输出请存储在 <strong style="color:#010101">{output}</strong> 中以供后续下载。',
pathTips11: '训练脚本存储在 <strong style="color:#010101">{code}</strong> 中,数据集存储在 <strong style="color:#010101">{dataset}</strong> 中,预训练模型存储在 <strong style="color:#010101">{model}</strong> 中,训练输出请存储在 <strong style="color:#010101">{output}</strong> 中以供后续下载。',
@@ -811,6 +812,15 @@ const zh = {
cloudbrainTaskType: '云脑任务类型',
repo: '项目',
cloudbrainTaskName: '云脑侧任务',

taskIsAutomaticStop: '任务是否自动停止',
automaticStop: '自动停止',
manualStop: '手动停止',
automaticStopTips: '该任务将在运行时长超过您所选择的时长后,或积分余额不足时,自动停止。',
manualStopTips: '该任务将由您手动停止或积分余额不足时自动停止。',
customize: '自定义',
numOfHours: '{num} 小时',
customizeTimeLimitPlaceholder: '请输入1到24之间的整数',
},
superComputeObj: {
mmlSparkDescr: `MMLSpark全称为Microsoft Machine Learning for Apache Spark,支持用户运行自制容器镜像,且赋予了用户容器内root权限。用户可直接使用平台提供的微软的MMLSpark。\n注:MMLSpark是微软提供针对机器学习环境的Spark版本(<a target="_blank" href="https://github.com/Azure/mmlspark">https://github.com/Azure/mmlspark</a>),关于mmlspark可参考论文:<a target="_blank" href="https://arxiv.org/pdf/1810.08744.pdf">https://arxiv.org/pdf/1810.08744.pdf</a>`,


+ 16
- 8
web_src/vuepages/pages/cloudbrain/configs.js View File

@@ -57,6 +57,7 @@ export const CreatePageConfigs = {
dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
runTimeLimit: { required: true },
/* just test */
// imagev2: { required: true },
// aiEngine: { required: true },
@@ -78,6 +79,7 @@ export const CreatePageConfigs = {
dataset: { required: false, type: 1, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
runTimeLimit: { required: true },
},
}],
}],
@@ -103,6 +105,7 @@ export const CreatePageConfigs = {
dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true },
spec: {},
runTimeLimit: { required: true },
},
}],
'NPU': [{
@@ -118,6 +121,7 @@ export const CreatePageConfigs = {
dataset: { required: false, type: 1, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
runTimeLimit: { required: true },
},
}],
'GCU': [{
@@ -138,6 +142,7 @@ export const CreatePageConfigs = {
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
runTimeLimit: { required: true },
},
}],
'MLU': [{
@@ -158,6 +163,7 @@ export const CreatePageConfigs = {
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
runTimeLimit: { required: true },
},
}],
'DCU': [{
@@ -198,6 +204,7 @@ export const CreatePageConfigs = {
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
runTimeLimit: { required: true },
},
}],
'METAX-GPGPU': [{
@@ -218,6 +225,7 @@ export const CreatePageConfigs = {
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
runTimeLimit: { required: true },
},
}],
}]
@@ -719,7 +727,7 @@ export const DetailPageConfigs = {
'computerRes', 'datasetPath',
'createTime', 'modelPath',
'startTime', 'outputPath',
'endTime', '',
'endTime', 'timeLimit',
'duration', '',
'descr', '',
'failedReason',
@@ -741,7 +749,7 @@ export const DetailPageConfigs = {
'status', 'spec',
'creator', 'hasInternet',
'branch', 'computerRes',
'createTime', '',
'createTime', 'timeLimit',
'startTime', '',
'endTime', '',
'duration', '',
@@ -770,7 +778,7 @@ export const DetailPageConfigs = {
'computerRes', 'codePath',
'createTime', 'datasetPath',
'startTime', 'modelPath',
'endTime', '',
'endTime', 'timeLimit',
'duration', '',
'descr', '',
'failedReason',
@@ -792,7 +800,7 @@ export const DetailPageConfigs = {
'status', 'spec',
'creator', 'aiCenter',
'branch', 'hasInternet',
'computerRes', '',
'computerRes', 'timeLimit',
'createTime', '',
'startTime', '',
'endTime', '',
@@ -819,7 +827,7 @@ export const DetailPageConfigs = {
'status', 'spec',
'creator', 'aiCenter',
'branch', 'hasInternet',
'computerRes', '',
'computerRes', 'timeLimit',
'createTime', '',
'startTime', '',
'endTime', '',
@@ -844,7 +852,7 @@ export const DetailPageConfigs = {
'status', 'spec',
'creator', 'aiCenter',
'branch', 'hasInternet',
'computerRes', '',
'computerRes', 'timeLimit',
'createTime', '',
'startTime', '',
'endTime', '',
@@ -894,7 +902,7 @@ export const DetailPageConfigs = {
'status', 'spec',
'creator', 'aiCenter',
'branch', 'hasInternet',
'computerRes', '',
'computerRes', 'timeLimit',
'createTime', '',
'startTime', '',
'endTime', '',
@@ -919,7 +927,7 @@ export const DetailPageConfigs = {
'status', 'spec',
'creator', 'aiCenter',
'branch', 'hasInternet',
'computerRes', '',
'computerRes', 'timeLimit',
'createTime', '',
'startTime', '',
'endTime', '',


+ 18
- 5
web_src/vuepages/pages/cloudbrain/create/index.vue View File

@@ -17,7 +17,9 @@
<div class="msg-content">
<i class="ri-information-line"></i>
<div class="msg-content-tip">
<div class="line-1" v-html="$t('cloudbrainObj.sameTaskTips1')"></div>
<div class="line-1" v-if="notStopTaskCount <= 1" v-html="$t('cloudbrainObj.sameTaskTips0')"></div>
<div class="line-1" v-else v-html="$t('cloudbrainObj.sameTaskTips1', { count: notStopTaskCount })">
</div>
<div class="line-2" v-html="$t('cloudbrainObj.sameTaskTips2')"></div>
</div>
</div>
@@ -73,8 +75,9 @@
<BootFile ref="bootFileRef" v-if="formCfg.bootFile" v-model="state.bootFile"
:required="formCfg.bootFile.required" :sampleUrl="formCfg.bootFile.sampleUrl"></BootFile>
<RunParameters ref="runParametersRef" v-if="formCfg.runParameters" v-model="state.runParameters"
:required="formCfg.runParameters.required">
</RunParameters>
:required="formCfg.runParameters.required"></RunParameters>
<RunTimeLimit ref="runTimeLimitRef" v-if="formCfg.runTimeLimit && subscriberUser" v-model="state.time_limit"
:required="formCfg.runTimeLimit.required"></RunTimeLimit>
<div class="form-row" v-if="this.isModifyTask && (pageCfg.modify && pageCfg.modify.showIsContinue)">
<div class="left-area">
<div class="title"></div>
@@ -140,6 +143,7 @@ import RunParameters from '~/components/cloudbrain/RunParameters.vue';
import NetworkType from '~/components/cloudbrain/NetworkType.vue';
import SpecSelect from '~/components/cloudbrain/SpecSelect.vue';
import WorkServerNum from '~/components/cloudbrain/WorkServerNum.vue';
import RunTimeLimit from '~/components/cloudbrain/RunTimeLimit.vue';
import AlgBechmarkType from '~/components/cloudbrain/AlgBechmarkType.vue';
import SDKCode from '~/components/cloudbrain/SDKCode.vue';
import DialogTips from '~/components/cloudbrain/DialogTips.vue';
@@ -172,6 +176,7 @@ export default {
networkType: 'no_internet',
spec: '',
workServerNum: 1,
time_limit: '4',
algBechmarkType: ['1', ''],
isContinue: false,
},
@@ -193,6 +198,7 @@ export default {
errorMsgBoxShow: false,
errorMsg: '',
alreadyMsgBoxShow: false,
notStopTaskCount: 0,
maskLoading: false,
maskLoadingContent: '',
datasetSize: 0,
@@ -205,11 +211,12 @@ export default {
noSpecFlag: false,
visible: false,
showFormRight: true,
subscriberUser: false,
};
},
components: {
FormTop, TaskName, TaskDescr, BranchName, BootFile, AIEngineSelect, ImageSelectV1, ImageSelectV2,
ModelSelect, DatasetSelect, RunParameters, NetworkType, SpecSelect, WorkServerNum,
ModelSelect, DatasetSelect, RunParameters, NetworkType, SpecSelect, WorkServerNum, RunTimeLimit,
AlgBechmarkType, SDKCode,
LoadingMask, DialogTips
},
@@ -288,6 +295,9 @@ export default {
case 'workServerNum':
subObj['work_server_number'] = this.state.workServerNum;
break;
case 'runTimeLimit':
subObj['time_limit'] = Number(this.state.time_limit);
break;
default:
break;
}
@@ -535,9 +545,12 @@ export default {
res = res.data;
if (res.code == 0) {
const data = res.data;
this.subscriberUser = data.is_subscriber;
if (!this.subscriberUser) delete this.formCfg['runTimeLimit'];
this.branchList = data.branches || [];
this.imageList = data.images || [];
this.alreadyMsgBoxShow = data.not_stop_task_count > 0;
this.alreadyMsgBoxShow = !data.can_create_more;
this.notStopTaskCount = data.not_stop_task_count || 0;
this.specConfigs.showPoint = data.pay_switch;
this.specConfigs.blance = data.point_account ? data.point_account.balance : 0;
this.specConfigs.specs = data.specs || {


+ 3
- 3
web_src/vuepages/pages/cloudbrain/detail/index.vue View File

@@ -37,7 +37,7 @@
</span>
<span class="task-refresh">
<el-tooltip placement="top" :content="$t('cloudbrainObj.refresh')">
<i class="el-icon-refresh-right" @click.stop.prevent="refresh(item)"></i>
<i class="el-icon-refresh-right" @click.stop.prevent="refresh(item, true)"></i>
</el-tooltip>
</span>
</div>
@@ -142,7 +142,7 @@ export default {
this.refresh(item);
});
},
refresh(item) {
refresh(item, useRefreshBtn) {
const activeTab = item.activeName;
const tabContent = this.$refs[activeTab + '-Ref'];
if (activeTab.indexOf('configInfo-') > -1) {
@@ -168,7 +168,7 @@ export default {
console.log(err);
});
} else {
tabContent && tabContent[0] && tabContent[0].refresh && tabContent[0].refresh();
tabContent && tabContent[0] && tabContent[0].refresh && tabContent[0].refresh(useRefreshBtn);
}
}
},


+ 6
- 2
web_src/vuepages/pages/supercompute/create/index.vue View File

@@ -20,7 +20,9 @@
<div class="msg-content">
<i class="ri-information-line"></i>
<div class="msg-content-tip">
<div class="line-1" v-html="$t('cloudbrainObj.sameTaskTips1')"></div>
<div class="line-1" v-if="notStopTaskCount <= 1" v-html="$t('cloudbrainObj.sameTaskTips0')"></div>
<div class="line-1" v-else v-html="$t('cloudbrainObj.sameTaskTips1', { count: notStopTaskCount })">
</div>
<div class="line-2" v-html="$t('cloudbrainObj.sameTaskTips2')"></div>
</div>
</div>
@@ -124,6 +126,7 @@ export default {
errorMsgBoxShow: false,
errorMsg: '',
alreadyMsgBoxShow: false,
notStopTaskCount: 0,
maskLoading: false,
maskLoadingContent: '',
datasetSize: 0,
@@ -251,7 +254,8 @@ export default {
const data = res.data;
this.branchList = data.branches || [];
this.imageList = data.images || [];
this.alreadyMsgBoxShow = data.not_stop_task_count > 0;
this.alreadyMsgBoxShow = !data.can_create_more;
this.notStopTaskCount = data.not_stop_task_count || 0;
this.specConfigs.showPoint = data.pay_switch;
this.specConfigs.blance = data.point_account ? data.point_account.balance : 0;
this.specConfigs.specs = data.specs || {


Loading…
Cancel
Save