@@ -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> | ||||