V20240402.patch
into V20240423
1 month ago
@@ -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) { | |||
@@ -19,6 +19,7 @@ type CreateNoteBookTaskRequest struct { | |||
Tasks []NoteBookTask | |||
PrimitiveDatasetName string | |||
RepoName string | |||
IsSubscriber bool | |||
} | |||
type NoteBookTask struct { | |||
@@ -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 { | |||
@@ -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) | |||
@@ -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") | |||
@@ -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) | |||
@@ -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 { | |||
@@ -2112,6 +2113,7 @@ type GetGrampusJobListResponse struct { | |||
type GrampusNotebookResponse struct { | |||
GrampusResult | |||
JobInfo GrampusNotebookInfo `json:"otJob"` | |||
ExitDiagnostics string `json:"exitDiagnostics"` | |||
} | |||
type GrampusNotebookRestartResponse struct { | |||
@@ -2189,6 +2191,7 @@ type GrampusNotebookTask struct { | |||
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) | |||
} | |||
@@ -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) | |||
@@ -187,6 +187,7 @@ type User struct { | |||
WechatBindUnix timeutil.TimeStamp | |||
//Mobile phone | |||
PhoneNumber string `xorm:"VARCHAR(255)"` | |||
IsSubscriber bool `xorm:"-"` | |||
} | |||
type UserShow struct { | |||
@@ -1783,6 +1784,8 @@ type SearchUserOptions struct { | |||
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}) | |||
@@ -42,6 +42,7 @@ type AdminEditUserForm struct { | |||
AllowImportLocal bool | |||
AllowCreateOrganization bool | |||
ProhibitLogin bool | |||
Subscriber bool | |||
} | |||
// Validate validates form fields | |||
@@ -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,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" | |||
) |
@@ -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 | |||
@@ -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任务失败 | |||
@@ -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,15 +31,23 @@ 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, | |||
}, | |||
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")) | |||
@@ -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). | |||
@@ -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)) | |||
} |
@@ -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" | |||
@@ -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) | |||
@@ -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 | |||
@@ -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, | |||
@@ -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 | |||
} | |||
@@ -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) | |||
@@ -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) | |||
@@ -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, | |||
@@ -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) | |||
@@ -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) | |||
@@ -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) | |||
@@ -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 | |||
} | |||
@@ -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 | |||
} |
@@ -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 { | |||
@@ -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> | |||
@@ -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> |
@@ -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> |
@@ -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; | |||
@@ -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); | |||
@@ -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 > 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>`, | |||
@@ -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">个人中心 > 云脑任务</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>`, | |||
@@ -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', '', | |||
@@ -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 || { | |||
@@ -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); | |||
} | |||
} | |||
}, | |||
@@ -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 || { | |||
Dear OpenI User
Thank you for your continuous support to the Openl Qizhi Community AI Collaboration Platform. In order to protect your usage rights and ensure network security, we updated the Openl Qizhi Community AI Collaboration Platform Usage Agreement in January 2024. The updated agreement specifies that users are prohibited from using intranet penetration tools. After you click "Agree and continue", you can continue to use our services. Thank you for your cooperation and understanding.
For more agreement content, please refer to the《Openl Qizhi Community AI Collaboration Platform Usage Agreement》