@@ -22,6 +22,16 @@ const ( | |||||
NPUResource = "NPU" | NPUResource = "NPU" | ||||
GPUResource = "CPU/GPU" | GPUResource = "CPU/GPU" | ||||
//notebook storage category | |||||
EVSCategory = "EVS" | |||||
EFSCategory = "EFS" | |||||
ManagedOwnership = "MANAGED" | |||||
DetectedOwnership = "DEDICATED" | |||||
NotebookFeature = "NOTEBOOK" | |||||
DefaultFeature = "DEFAULT" | |||||
JobWaiting CloudbrainStatus = "WAITING" | JobWaiting CloudbrainStatus = "WAITING" | ||||
JobStopped CloudbrainStatus = "STOPPED" | JobStopped CloudbrainStatus = "STOPPED" | ||||
JobSucceeded CloudbrainStatus = "SUCCEEDED" | JobSucceeded CloudbrainStatus = "SUCCEEDED" | ||||
@@ -520,6 +530,25 @@ type CloudBrainResult struct { | |||||
Msg string `json:"msg"` | Msg string `json:"msg"` | ||||
} | } | ||||
type CreateNotebook2Params struct { | |||||
JobName string `json:"name"` | |||||
Description string `json:"description"` | |||||
Duration int64 `json:"duration"` //ms | |||||
Feature string `json:"feature"` | |||||
PoolID string `json:"pool_id"` | |||||
Flavor string `json:"flavor"` | |||||
ImageID string `json:"image_id"` | |||||
WorkspaceID string `json:"workspace_id"` | |||||
Volume VolumeReq `json:"volume"` | |||||
} | |||||
type VolumeReq struct { | |||||
Capacity int `json:"capacity"` | |||||
Category string `json:"category"` | |||||
Ownership string `json:"ownership"` | |||||
Uri string `json:"uri"` | |||||
} | |||||
type CreateNotebookParams struct { | type CreateNotebookParams struct { | ||||
JobName string `json:"name"` | JobName string `json:"name"` | ||||
Description string `json:"description"` | Description string `json:"description"` | ||||
@@ -637,6 +666,42 @@ type GetNotebookResult struct { | |||||
} `json:"spec"` | } `json:"spec"` | ||||
} | } | ||||
type GetNotebook2Result struct { | |||||
ErrorCode string `json:"error_code"` | |||||
ErrorMsg string `json:"error_msg"` | |||||
FailReason string `json:"fail_reason"` | |||||
ID string `json:"id"` | |||||
Name string `json:"name"` | |||||
Description string `json:"description"` | |||||
Status string `json:"status"` | |||||
Url string `json:"url"` //实例访问的URL | |||||
Token string `json:"token"` //notebook鉴权使用的token信息 | |||||
Flavor string `json:"flavor"` | |||||
CreateTime string | |||||
LatestUpdateTime string | |||||
CreateAt int64 `json:"create_at"` //实例创建的时间,UTC毫秒 | |||||
UpdateAt int64 `json:"update_at"` //实例最后更新(不包括保活心跳)的时间,UTC毫秒 | |||||
Image struct { | |||||
Name string `json:"name"` | |||||
Status string `json:"status"` | |||||
QueuingNum int `json:"queuing_num"` | |||||
QueueLeftTime int `json:"queue_left_time"` //s | |||||
Duration int `json:"duration"` //auto_stop_time s | |||||
} `json:"image"` | |||||
Lease struct { | |||||
CreateTime int64 `json:"create_at"` //实例创建的时间,UTC毫秒 | |||||
Duration int64 `json:"duration"` //实例运行时长,以创建时间为起点计算,即“创建时间+duration > 当前时刻”时,系统会自动停止实例 | |||||
UpdateTime int64 `json:"update_at"` //实例最后更新(不包括保活心跳)的时间,UTC毫秒 | |||||
} `json:"lease"` //实例自动停止的倒计时信息 | |||||
VolumeRes struct { | |||||
Capacity int `json:"capacity"` | |||||
Category string `json:"category"` | |||||
MountPath string `json:"mount_path"` | |||||
Ownership string `json:"ownership"` | |||||
Status string `json:"status"` | |||||
} `json:"volume"` | |||||
} | |||||
type GetTokenParams struct { | type GetTokenParams struct { | ||||
Auth Auth `json:"auth"` | Auth Auth `json:"auth"` | ||||
} | } | ||||
@@ -690,6 +755,7 @@ type NotebookActionResult struct { | |||||
ErrorMsg string `json:"error_msg"` | ErrorMsg string `json:"error_msg"` | ||||
CurrentStatus string `json:"current_status"` | CurrentStatus string `json:"current_status"` | ||||
PreviousState string `json:"previous_state"` | PreviousState string `json:"previous_state"` | ||||
Status string `json:"status"` | |||||
} | } | ||||
type NotebookGetJobTokenResult struct { | type NotebookGetJobTokenResult struct { | ||||
@@ -17,6 +17,7 @@ const ( | |||||
//notebook | //notebook | ||||
storageTypeOBS = "obs" | storageTypeOBS = "obs" | ||||
autoStopDuration = 4 * 60 * 60 | autoStopDuration = 4 * 60 * 60 | ||||
autoStopDurationMs = 4 * 60 * 60 * 1000 | |||||
DataSetMountPath = "/home/ma-user/work" | DataSetMountPath = "/home/ma-user/work" | ||||
NotebookEnv = "Python3" | NotebookEnv = "Python3" | ||||
@@ -262,6 +263,48 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor strin | |||||
return nil | return nil | ||||
} | } | ||||
func GenerateNotebook2(ctx *context.Context, jobName, uuid, description, flavor string) error { | |||||
if poolInfos == nil { | |||||
json.Unmarshal([]byte(setting.PoolInfos), &poolInfos) | |||||
} | |||||
jobResult, err := createNotebook2(models.CreateNotebook2Params{ | |||||
JobName: jobName, | |||||
Description: description, | |||||
Flavor: flavor, | |||||
Duration: autoStopDurationMs, | |||||
ImageID: "59a6e9f5-93c0-44dd-85b0-82f390c5d53a", | |||||
PoolID: poolInfos.PoolInfo[0].PoolId, | |||||
Feature: models.NotebookFeature, | |||||
Volume: models.VolumeReq{ | |||||
Capacity: 100, | |||||
Category: models.EVSCategory, | |||||
Ownership: models.ManagedOwnership, | |||||
}, | |||||
WorkspaceID: "0", | |||||
}) | |||||
if err != nil { | |||||
log.Error("createNotebook2 failed: %v", err.Error()) | |||||
return err | |||||
} | |||||
err = models.CreateCloudbrain(&models.Cloudbrain{ | |||||
Status: string(models.JobWaiting), | |||||
UserID: ctx.User.ID, | |||||
RepoID: ctx.Repo.Repository.ID, | |||||
JobID: jobResult.ID, | |||||
JobName: jobName, | |||||
JobType: string(models.JobTypeDebug), | |||||
Type: models.TypeCloudBrainTwo, | |||||
Uuid: uuid, | |||||
ComputeResource: models.NPUResource, | |||||
}) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return nil | |||||
} | |||||
func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) { | func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) { | ||||
jobResult, err := createTrainJob(models.CreateTrainJobParams{ | jobResult, err := createTrainJob(models.CreateTrainJobParams{ | ||||
JobName: req.JobName, | JobName: req.JobName, | ||||
@@ -28,6 +28,11 @@ const ( | |||||
urlResourceSpecs = "/job/resource-specs" | urlResourceSpecs = "/job/resource-specs" | ||||
urlTrainJobConfig = "/training-job-configs" | urlTrainJobConfig = "/training-job-configs" | ||||
errorCodeExceedLimit = "ModelArts.0118" | errorCodeExceedLimit = "ModelArts.0118" | ||||
//notebook 2.0 | |||||
urlNotebook2 = "/notebooks" | |||||
modelartsIllegalToken = "ModelArts.6401" | |||||
) | ) | ||||
func getRestyClient() *resty.Client { | func getRestyClient() *resty.Client { | ||||
@@ -174,6 +179,50 @@ sendjob: | |||||
return &result, nil | return &result, nil | ||||
} | } | ||||
func GetNotebook2(jobID string) (*models.GetNotebook2Result, error) { | |||||
checkSetting() | |||||
client := getRestyClient() | |||||
var result models.GetNotebook2Result | |||||
retry := 0 | |||||
sendjob: | |||||
res, err := client.R(). | |||||
SetHeader("Content-Type", "application/json"). | |||||
SetAuthToken(TOKEN). | |||||
SetResult(&result). | |||||
Get(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID) | |||||
if err != nil { | |||||
return nil, fmt.Errorf("resty GetJob: %v", err) | |||||
} | |||||
if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
retry++ | |||||
_ = getToken() | |||||
goto sendjob | |||||
} | |||||
var response models.NotebookResult | |||||
err = json.Unmarshal(res.Body(), &response) | |||||
if err != nil { | |||||
log.Error("json.Unmarshal failed: %s", err.Error()) | |||||
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error()) | |||||
} | |||||
if len(response.ErrorCode) != 0 { | |||||
log.Error("GetJob failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
if response.ErrorCode == modelartsIllegalToken && retry < 1 { | |||||
retry++ | |||||
_ = getToken() | |||||
goto sendjob | |||||
} | |||||
return &result, fmt.Errorf("GetJob failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
} | |||||
return &result, nil | |||||
} | |||||
func ManageNotebook(jobID string, param models.NotebookAction) (*models.NotebookActionResult, error) { | func ManageNotebook(jobID string, param models.NotebookAction) (*models.NotebookActionResult, error) { | ||||
checkSetting() | checkSetting() | ||||
client := getRestyClient() | client := getRestyClient() | ||||
@@ -214,6 +263,50 @@ sendjob: | |||||
return &result, nil | return &result, nil | ||||
} | } | ||||
func ManageNotebook2(jobID string, param models.NotebookAction) (*models.NotebookActionResult, error) { | |||||
checkSetting() | |||||
client := getRestyClient() | |||||
var result models.NotebookActionResult | |||||
retry := 0 | |||||
sendjob: | |||||
res, err := client.R(). | |||||
SetHeader("Content-Type", "application/json"). | |||||
SetAuthToken(TOKEN). | |||||
SetResult(&result). | |||||
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID + "/" + param.Action + "?duration=" + strconv.Itoa(autoStopDurationMs)) | |||||
if err != nil { | |||||
return &result, fmt.Errorf("resty ManageNotebook2: %v", err) | |||||
} | |||||
if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
retry++ | |||||
_ = getToken() | |||||
goto sendjob | |||||
} | |||||
var response models.NotebookResult | |||||
err = json.Unmarshal(res.Body(), &response) | |||||
if err != nil { | |||||
log.Error("json.Unmarshal failed: %s", err.Error()) | |||||
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error()) | |||||
} | |||||
if len(response.ErrorCode) != 0 { | |||||
log.Error("ManageNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
if response.ErrorCode == modelartsIllegalToken && retry < 1 { | |||||
retry++ | |||||
_ = getToken() | |||||
goto sendjob | |||||
} | |||||
return &result, fmt.Errorf("ManageNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
} | |||||
return &result, nil | |||||
} | |||||
func DelNotebook(jobID string) (*models.NotebookDelResult, error) { | func DelNotebook(jobID string) (*models.NotebookDelResult, error) { | ||||
checkSetting() | checkSetting() | ||||
client := getRestyClient() | client := getRestyClient() | ||||
@@ -253,6 +346,50 @@ sendjob: | |||||
return &result, nil | return &result, nil | ||||
} | } | ||||
func DelNotebook2(jobID string) (*models.NotebookDelResult, error) { | |||||
checkSetting() | |||||
client := getRestyClient() | |||||
var result models.NotebookDelResult | |||||
retry := 0 | |||||
sendjob: | |||||
res, err := client.R(). | |||||
SetHeader("Content-Type", "application/json"). | |||||
SetAuthToken(TOKEN). | |||||
SetResult(&result). | |||||
Delete(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID) | |||||
if err != nil { | |||||
return &result, fmt.Errorf("resty DelJob: %v", err) | |||||
} | |||||
if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
retry++ | |||||
_ = getToken() | |||||
goto sendjob | |||||
} | |||||
var response models.NotebookResult | |||||
err = json.Unmarshal(res.Body(), &response) | |||||
if err != nil { | |||||
log.Error("json.Unmarshal failed: %s", err.Error()) | |||||
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error()) | |||||
} | |||||
if len(response.ErrorCode) != 0 { | |||||
log.Error("DelNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
if response.ErrorCode == modelartsIllegalToken && retry < 1 { | |||||
retry++ | |||||
_ = getToken() | |||||
goto sendjob | |||||
} | |||||
return &result, fmt.Errorf("DelNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
} | |||||
return &result, nil | |||||
} | |||||
func DelJob(jobID string) (*models.NotebookDelResult, error) { | func DelJob(jobID string) (*models.NotebookDelResult, error) { | ||||
checkSetting() | checkSetting() | ||||
client := getRestyClient() | client := getRestyClient() | ||||
@@ -930,3 +1067,51 @@ sendjob: | |||||
return &result, nil | return &result, nil | ||||
} | } | ||||
func createNotebook2(createJobParams models.CreateNotebook2Params) (*models.CreateNotebookResult, error) { | |||||
checkSetting() | |||||
client := getRestyClient() | |||||
var result models.CreateNotebookResult | |||||
retry := 0 | |||||
sendjob: | |||||
res, err := client.R(). | |||||
SetHeader("Content-Type", "application/json"). | |||||
SetAuthToken(TOKEN). | |||||
SetBody(createJobParams). | |||||
SetResult(&result). | |||||
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2) | |||||
if err != nil { | |||||
return nil, fmt.Errorf("resty create notebook2: %s", err) | |||||
} | |||||
if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
retry++ | |||||
_ = getToken() | |||||
goto sendjob | |||||
} | |||||
var response models.NotebookResult | |||||
err = json.Unmarshal(res.Body(), &response) | |||||
if err != nil { | |||||
log.Error("json.Unmarshal failed: %s", err.Error()) | |||||
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error()) | |||||
} | |||||
if len(response.ErrorCode) != 0 { | |||||
log.Error("createNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
if response.ErrorCode == errorCodeExceedLimit { | |||||
response.ErrorMsg = "所选规格使用数量已超过最大配额限制。" | |||||
} | |||||
if response.ErrorCode == modelartsIllegalToken && retry < 1 { | |||||
retry++ | |||||
_ = getToken() | |||||
goto sendjob | |||||
} | |||||
return &result, fmt.Errorf("createNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
} | |||||
return &result, nil | |||||
} |
@@ -512,6 +512,7 @@ var ( | |||||
ProfileID string | ProfileID string | ||||
PoolInfos string | PoolInfos string | ||||
Flavor string | Flavor string | ||||
DebugHost string | |||||
//train-job | //train-job | ||||
ResourcePools string | ResourcePools string | ||||
Engines string | Engines string | ||||
@@ -1325,6 +1326,7 @@ func NewContext() { | |||||
ProfileID = sec.Key("PROFILE_ID").MustString("") | ProfileID = sec.Key("PROFILE_ID").MustString("") | ||||
PoolInfos = sec.Key("POOL_INFOS").MustString("") | PoolInfos = sec.Key("POOL_INFOS").MustString("") | ||||
Flavor = sec.Key("FLAVOR").MustString("") | Flavor = sec.Key("FLAVOR").MustString("") | ||||
DebugHost = sec.Key("DEBUG_SERVER_HOST").MustString("http://192.168.202.73") | |||||
ResourcePools = sec.Key("Resource_Pools").MustString("") | ResourcePools = sec.Key("Resource_Pools").MustString("") | ||||
Engines = sec.Key("Engines").MustString("") | Engines = sec.Key("Engines").MustString("") | ||||
EngineVersions = sec.Key("Engine_Versions").MustString("") | EngineVersions = sec.Key("Engine_Versions").MustString("") | ||||
@@ -883,7 +883,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
}, reqRepoReader(models.UnitTypeCloudBrain)) | }, reqRepoReader(models.UnitTypeCloudBrain)) | ||||
m.Group("/modelarts", func() { | m.Group("/modelarts", func() { | ||||
m.Group("/notebook", func() { | m.Group("/notebook", func() { | ||||
m.Get("/:jobid", repo.GetModelArtsNotebook) | |||||
//m.Get("/:jobid", repo.GetModelArtsNotebook) | |||||
m.Get("/:jobid", repo.GetModelArtsNotebook2) | |||||
}) | }) | ||||
m.Group("/train-job", func() { | m.Group("/train-job", func() { | ||||
m.Group("/:jobid", func() { | m.Group("/:jobid", func() { | ||||
@@ -51,6 +51,37 @@ func GetModelArtsNotebook(ctx *context.APIContext) { | |||||
} | } | ||||
func GetModelArtsNotebook2(ctx *context.APIContext) { | |||||
var ( | |||||
err error | |||||
) | |||||
jobID := ctx.Params(":jobid") | |||||
repoID := ctx.Repo.Repository.ID | |||||
job, err := models.GetRepoCloudBrainByJobID(repoID, jobID) | |||||
if err != nil { | |||||
ctx.NotFound(err) | |||||
return | |||||
} | |||||
result, err := modelarts.GetNotebook2(jobID) | |||||
if err != nil { | |||||
ctx.NotFound(err) | |||||
return | |||||
} | |||||
job.Status = result.Status | |||||
err = models.UpdateJob(job) | |||||
if err != nil { | |||||
log.Error("UpdateJob failed:", err) | |||||
} | |||||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||||
"JobID": jobID, | |||||
"JobStatus": result.Status, | |||||
}) | |||||
} | |||||
func GetModelArtsTrainJob(ctx *context.APIContext) { | func GetModelArtsTrainJob(ctx *context.APIContext) { | ||||
var ( | var ( | ||||
err error | err error | ||||
@@ -967,7 +967,8 @@ func SyncCloudbrainStatus() { | |||||
} | } | ||||
} else if task.Type == models.TypeCloudBrainTwo { | } else if task.Type == models.TypeCloudBrainTwo { | ||||
if task.JobType == string(models.JobTypeDebug) { | if task.JobType == string(models.JobTypeDebug) { | ||||
result, err := modelarts.GetJob(task.JobID) | |||||
//result, err := modelarts.GetJob(task.JobID) | |||||
result, err := modelarts.GetNotebook2(task.JobID) | |||||
if err != nil { | if err != nil { | ||||
log.Error("GetJob(%s) failed:%v", task.JobName, err) | log.Error("GetJob(%s) failed:%v", task.JobName, err) | ||||
continue | continue | ||||
@@ -25,8 +25,6 @@ import ( | |||||
"code.gitea.io/gitea/modules/obs" | "code.gitea.io/gitea/modules/obs" | ||||
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
"code.gitea.io/gitea/modules/storage" | "code.gitea.io/gitea/modules/storage" | ||||
"github.com/unknwon/com" | |||||
) | ) | ||||
const ( | const ( | ||||
@@ -129,6 +127,20 @@ func NotebookNew(ctx *context.Context) { | |||||
ctx.HTML(200, tplModelArtsNotebookNew) | ctx.HTML(200, tplModelArtsNotebookNew) | ||||
} | } | ||||
func notebookNewDataPrepare(ctx *context.Context) error { | |||||
ctx.Data["PageIsCloudBrain"] = true | |||||
t := time.Now() | |||||
var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||||
ctx.Data["job_name"] = jobName | |||||
if modelarts.FlavorInfos == nil { | |||||
json.Unmarshal([]byte(setting.FlavorInfos), &modelarts.FlavorInfos) | |||||
} | |||||
ctx.Data["flavors"] = modelarts.FlavorInfos.FlavorInfo | |||||
return nil | |||||
} | |||||
func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm) { | func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm) { | ||||
ctx.Data["PageIsNotebook"] = true | ctx.Data["PageIsNotebook"] = true | ||||
jobName := form.JobName | jobName := form.JobName | ||||
@@ -173,6 +185,54 @@ func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm) | |||||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all") | ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all") | ||||
} | } | ||||
func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm) { | |||||
ctx.Data["PageIsNotebook"] = true | |||||
jobName := form.JobName | |||||
uuid := form.Attachment | |||||
description := form.Description | |||||
flavor := form.Flavor | |||||
flavor = "modelarts.bm.910.arm.public.1" | |||||
count, err := models.GetCloudbrainNotebookCountByUserID(ctx.User.ID) | |||||
if err != nil { | |||||
log.Error("GetCloudbrainNotebookCountByUserID failed:%v", err, ctx.Data["MsgID"]) | |||||
notebookNewDataPrepare(ctx) | |||||
ctx.RenderWithErr("system error", tplModelArtsNotebookNew, &form) | |||||
return | |||||
} else { | |||||
if count >= 1 { | |||||
log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | |||||
notebookNewDataPrepare(ctx) | |||||
ctx.RenderWithErr("you have already a running or waiting task, can not create more", tplModelArtsNotebookNew, &form) | |||||
return | |||||
} | |||||
} | |||||
_, err = models.GetCloudbrainByName(jobName) | |||||
if err == nil { | |||||
log.Error("the job name did already exist", ctx.Data["MsgID"]) | |||||
notebookNewDataPrepare(ctx) | |||||
ctx.RenderWithErr("the job name did already exist", tplModelArtsNotebookNew, &form) | |||||
return | |||||
} else { | |||||
if !models.IsErrJobNotExist(err) { | |||||
log.Error("system error, %v", err, ctx.Data["MsgID"]) | |||||
notebookNewDataPrepare(ctx) | |||||
ctx.RenderWithErr("system error", tplModelArtsNotebookNew, &form) | |||||
return | |||||
} | |||||
} | |||||
err = modelarts.GenerateNotebook2(ctx, jobName, uuid, description, flavor) | |||||
if err != nil { | |||||
log.Error("GenerateNotebook2 failed, %v", err, ctx.Data["MsgID"]) | |||||
notebookNewDataPrepare(ctx) | |||||
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookNew, &form) | |||||
return | |||||
} | |||||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all") | |||||
} | |||||
func NotebookShow(ctx *context.Context) { | func NotebookShow(ctx *context.Context) { | ||||
ctx.Data["PageIsCloudBrain"] = true | ctx.Data["PageIsCloudBrain"] = true | ||||
@@ -184,7 +244,7 @@ func NotebookShow(ctx *context.Context) { | |||||
return | return | ||||
} | } | ||||
result, err := modelarts.GetJob(jobID) | |||||
result, err := modelarts.GetNotebook2(jobID) | |||||
if err != nil { | if err != nil { | ||||
ctx.Data["error"] = err.Error() | ctx.Data["error"] = err.Error() | ||||
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookShow, nil) | ctx.RenderWithErr(err.Error(), tplModelArtsNotebookShow, nil) | ||||
@@ -200,12 +260,10 @@ func NotebookShow(ctx *context.Context) { | |||||
return | return | ||||
} | } | ||||
createTime, _ := com.StrTo(result.CreationTimestamp).Int64() | |||||
result.CreateTime = time.Unix(int64(createTime/1000), 0).Format("2006-01-02 15:04:05") | |||||
endTime, _ := com.StrTo(result.LatestUpdateTimestamp).Int64() | |||||
result.LatestUpdateTime = time.Unix(int64(endTime/1000), 0).Format("2006-01-02 15:04:05") | |||||
result.QueuingInfo.BeginTime = time.Unix(int64(result.QueuingInfo.BeginTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||||
result.QueuingInfo.EndTime = time.Unix(int64(result.QueuingInfo.EndTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||||
result.CreateTime = time.Unix(int64(result.CreateAt/1000), 0).Format("2006-01-02 15:04:05") | |||||
result.LatestUpdateTime = time.Unix(int64(result.UpdateAt/1000), 0).Format("2006-01-02 15:04:05") | |||||
//result.QueuingInfo.BeginTime = time.Unix(int64(result.QueuingInfo.BeginTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||||
//result.QueuingInfo.EndTime = time.Unix(int64(result.QueuingInfo.EndTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||||
} | } | ||||
ctx.Data["task"] = task | ctx.Data["task"] = task | ||||
@@ -241,6 +299,18 @@ func NotebookDebug(ctx *context.Context) { | |||||
ctx.Redirect(debugUrl) | ctx.Redirect(debugUrl) | ||||
} | } | ||||
func NotebookDebug2(ctx *context.Context) { | |||||
var jobID = ctx.Params(":jobid") | |||||
result, err := modelarts.GetNotebook2(jobID) | |||||
if err != nil { | |||||
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookIndex, nil) | |||||
return | |||||
} | |||||
ctx.Redirect(result.Url) | |||||
} | |||||
func NotebookManage(ctx *context.Context) { | func NotebookManage(ctx *context.Context) { | ||||
var jobID = ctx.Params(":jobid") | var jobID = ctx.Params(":jobid") | ||||
var action = ctx.Params(":action") | var action = ctx.Params(":action") | ||||
@@ -312,15 +382,18 @@ func NotebookManage(ctx *context.Context) { | |||||
param := models.NotebookAction{ | param := models.NotebookAction{ | ||||
Action: action, | Action: action, | ||||
} | } | ||||
res, err := modelarts.ManageNotebook(jobID, param) | |||||
res, err := modelarts.ManageNotebook2(jobID, param) | |||||
if err != nil { | if err != nil { | ||||
log.Error("ManageNotebook(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) | |||||
log.Error("ManageNotebook2(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) | |||||
resultCode = "-1" | resultCode = "-1" | ||||
errorMsg = "启动失败" | |||||
errorMsg = err.Error() | |||||
if strings.Contains(err.Error(), "ModelArts.6404") { | |||||
errorMsg = "the job's version is too old and can not be restarted" | |||||
} | |||||
break | break | ||||
} | } | ||||
task.Status = res.CurrentStatus | |||||
task.Status = res.Status | |||||
err = models.UpdateJob(task) | err = models.UpdateJob(task) | ||||
if err != nil { | if err != nil { | ||||
log.Error("UpdateJob(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) | log.Error("UpdateJob(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) | ||||
@@ -352,10 +425,10 @@ func NotebookDel(ctx *context.Context) { | |||||
return | return | ||||
} | } | ||||
_, err := modelarts.DelNotebook(jobID) | |||||
_, err := modelarts.DelNotebook2(jobID) | |||||
if err != nil { | if err != nil { | ||||
log.Error("DelJob(%s) failed:%v", task.JobName, err.Error()) | |||||
ctx.ServerError("DelJob failed", err) | |||||
log.Error("DelNotebook2(%s) failed:%v", task.JobName, err.Error()) | |||||
ctx.ServerError("DelNotebook2 failed", err) | |||||
return | return | ||||
} | } | ||||
@@ -1029,6 +1029,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
m.Group("/modelarts", func() { | m.Group("/modelarts", func() { | ||||
m.Group("/notebook", func() { | m.Group("/notebook", func() { | ||||
/* v1.0 | |||||
m.Group("/:jobid", func() { | m.Group("/:jobid", func() { | ||||
m.Get("", reqRepoCloudBrainReader, repo.NotebookShow) | m.Get("", reqRepoCloudBrainReader, repo.NotebookShow) | ||||
m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.NotebookDebug) | m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.NotebookDebug) | ||||
@@ -1037,6 +1038,15 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
}) | }) | ||||
m.Get("/create", reqRepoCloudBrainWriter, repo.NotebookNew) | m.Get("/create", reqRepoCloudBrainWriter, repo.NotebookNew) | ||||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsNotebookForm{}), repo.NotebookCreate) | m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsNotebookForm{}), repo.NotebookCreate) | ||||
*/ | |||||
m.Group("/:jobid", func() { | |||||
m.Get("", reqRepoCloudBrainReader, repo.NotebookShow) | |||||
m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.NotebookDebug2) | |||||
m.Post("/:action", reqRepoCloudBrainWriter, repo.NotebookManage) | |||||
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.NotebookDel) | |||||
}) | |||||
m.Get("/create", reqRepoCloudBrainWriter, repo.NotebookNew) | |||||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsNotebookForm{}), repo.Notebook2Create) | |||||
}) | }) | ||||
m.Group("/train-job", func() { | m.Group("/train-job", func() { | ||||
@@ -51,7 +51,7 @@ | |||||
<input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255" onkeyup="this.value=this.value.replace(/[, ]/g,'')"> | <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255" onkeyup="this.value=this.value.replace(/[, ]/g,'')"> | ||||
</div> | </div> | ||||
<div class="inline field"> | |||||
<!-- <div class="inline field"> | |||||
<label>数据集</label> | <label>数据集</label> | ||||
<input type="text" list="cloudbrain_dataset" placeholder="选择数据集" name="" id="answerInput" autofocus maxlength="36"> | <input type="text" list="cloudbrain_dataset" placeholder="选择数据集" name="" id="answerInput" autofocus maxlength="36"> | ||||
<datalist id="cloudbrain_dataset" class="ui search" style='width:385px' name="attachment"> | <datalist id="cloudbrain_dataset" class="ui search" style='width:385px' name="attachment"> | ||||
@@ -69,7 +69,7 @@ | |||||
<div class="inline required field"> | <div class="inline required field"> | ||||
<label>类型</label> | <label>类型</label> | ||||
<input name="job_type" id="cloudbrain_job_type" value="{{.notebook_type}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | <input name="job_type" id="cloudbrain_job_type" value="{{.notebook_type}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | ||||
</div> | |||||
</div> --> | |||||
<div class="inline required field"> | <div class="inline required field"> | ||||
<label>规格</label> | <label>规格</label> | ||||
<select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | <select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | ||||
@@ -79,10 +79,10 @@ | |||||
{{end}} | {{end}} | ||||
</select> | </select> | ||||
</div> | </div> | ||||
<div class="inline required field"> | |||||
<!--<div class="inline required field"> | |||||
<label>数据集存放路径</label> | <label>数据集存放路径</label> | ||||
<input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | ||||
</div> | |||||
</div> --> | |||||
<div class="inline field"> | <div class="inline field"> | ||||
<label>描述</label> | <label>描述</label> | ||||
<input name="description" id="cloudbrain_description" tabindex="3" autofocus maxlength="255"> | <input name="description" id="cloudbrain_description" tabindex="3" autofocus maxlength="255"> | ||||
@@ -47,85 +47,7 @@ | |||||
</table> | </table> | ||||
{{end}} | {{end}} | ||||
</div> | </div> | ||||
<div class="ui blue segment"> | |||||
{{with .result}} | |||||
<table class="ui celled striped table"> | |||||
<thead> | |||||
<tr> <th colspan="2"> 配置信息 </th> </tr> | |||||
</thead> | |||||
<tbody> | |||||
<tr> | |||||
<td class="four wide"> 开发环境类型 </td> | |||||
<td>{{.Profile.DeType}}</td> | |||||
</tr> | |||||
<tr> | |||||
<td> 硬件类型 </td> | |||||
<td>{{.Profile.FlavorType}}</td> | |||||
</tr> | |||||
</tbody> | |||||
</table> | |||||
<table class="ui celled striped table"> | |||||
<thead> | |||||
<tr> <th colspan="2"> 机器规格详情 </th> </tr> | |||||
</thead> | |||||
<tbody> | |||||
<tr> | |||||
<td class="four wide"> 机器规格 </td> | |||||
<td> {{.Flavor}} </td> | |||||
</tr> | |||||
<tr> | |||||
<td> 规格名称 </td> | |||||
<td>{{.FlavorDetails.Name}}</td> | |||||
</tr> | |||||
<tr> | |||||
<td> 规格销售状态 </td> | |||||
<td>{{.FlavorDetails.Status}}</td> | |||||
</tr> | |||||
<tr> | |||||
<td> 排队个数 </td> | |||||
<td>{{.FlavorDetails.QueuingNum}}</td> | |||||
</tr> | |||||
<tr> | |||||
<td> 排到队的剩余时间(秒) </td> | |||||
<td>{{.FlavorDetails.QueueLeftTime}}</td> | |||||
</tr> | |||||
<tr> | |||||
<td> 自动停止时间(秒) </td> | |||||
<td>{{.FlavorDetails.Duration}}</td> | |||||
</tr> | |||||
</tbody> | |||||
</table> | |||||
<table class="ui celled striped table" {{if eq .QueuingInfo.RemainTime 0}}hidden{{end}}> | |||||
<thead> | |||||
<tr> <th colspan="2"> 排队信息 </th> </tr> | |||||
</thead> | |||||
<tbody> | |||||
<tr> | |||||
<td> 实例状态 </td> | |||||
<td>{{.QueuingInfo.Status}}</td> | |||||
</tr> | |||||
<tr> | |||||
<td> 实例排队的开始时间 </td> | |||||
<td>{{.QueuingInfo.BeginTime}}</td> | |||||
</tr> | |||||
<tr> | |||||
<td> 排到队的剩余时间(秒) </td> | |||||
<td>{{.QueuingInfo.RemainTime}}</td> | |||||
</tr> | |||||
<tr> | |||||
<td> 实例排队的预计停止时间 </td> | |||||
<td>{{.QueuingInfo.EndTime}}</td> | |||||
</tr> | |||||
<tr> | |||||
<td> 实例在队列中的排位 </td> | |||||
<td>{{.QueuingInfo.Rank}}</td> | |||||
</tr> | |||||
</tbody> | |||||
</table> | |||||
{{end}} | |||||
</div> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
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》