#1501 notebook2.0

Merged
lewis merged 7 commits from notebook2.0 into V20220228 2 years ago
  1. +66
    -0
      models/cloudbrain.go
  2. +43
    -0
      modules/modelarts/modelarts.go
  3. +185
    -0
      modules/modelarts/resty.go
  4. +2
    -0
      modules/setting/setting.go
  5. +2
    -1
      routers/api/v1/api.go
  6. +31
    -0
      routers/api/v1/repo/modelarts.go
  7. +2
    -1
      routers/repo/cloudbrain.go
  8. +89
    -16
      routers/repo/modelarts.go
  9. +10
    -0
      routers/routes/routes.go
  10. +4
    -4
      templates/repo/modelarts/notebook/new.tmpl
  11. +1
    -79
      templates/repo/modelarts/notebook/show.tmpl

+ 66
- 0
models/cloudbrain.go View File

@@ -22,6 +22,16 @@ const (
NPUResource = "NPU"
GPUResource = "CPU/GPU"

//notebook storage category
EVSCategory = "EVS"
EFSCategory = "EFS"

ManagedOwnership = "MANAGED"
DetectedOwnership = "DEDICATED"

NotebookFeature = "NOTEBOOK"
DefaultFeature = "DEFAULT"

JobWaiting CloudbrainStatus = "WAITING"
JobStopped CloudbrainStatus = "STOPPED"
JobSucceeded CloudbrainStatus = "SUCCEEDED"
@@ -520,6 +530,25 @@ type CloudBrainResult struct {
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 {
JobName string `json:"name"`
Description string `json:"description"`
@@ -637,6 +666,42 @@ type GetNotebookResult struct {
} `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 {
Auth Auth `json:"auth"`
}
@@ -690,6 +755,7 @@ type NotebookActionResult struct {
ErrorMsg string `json:"error_msg"`
CurrentStatus string `json:"current_status"`
PreviousState string `json:"previous_state"`
Status string `json:"status"`
}

type NotebookGetJobTokenResult struct {


+ 43
- 0
modules/modelarts/modelarts.go View File

@@ -17,6 +17,7 @@ const (
//notebook
storageTypeOBS = "obs"
autoStopDuration = 4 * 60 * 60
autoStopDurationMs = 4 * 60 * 60 * 1000

DataSetMountPath = "/home/ma-user/work"
NotebookEnv = "Python3"
@@ -262,6 +263,48 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor strin
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) {
jobResult, err := createTrainJob(models.CreateTrainJobParams{
JobName: req.JobName,


+ 185
- 0
modules/modelarts/resty.go View File

@@ -28,6 +28,11 @@ const (
urlResourceSpecs = "/job/resource-specs"
urlTrainJobConfig = "/training-job-configs"
errorCodeExceedLimit = "ModelArts.0118"

//notebook 2.0
urlNotebook2 = "/notebooks"

modelartsIllegalToken = "ModelArts.6401"
)

func getRestyClient() *resty.Client {
@@ -174,6 +179,50 @@ sendjob:
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) {
checkSetting()
client := getRestyClient()
@@ -214,6 +263,50 @@ sendjob:
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) {
checkSetting()
client := getRestyClient()
@@ -253,6 +346,50 @@ sendjob:
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) {
checkSetting()
client := getRestyClient()
@@ -930,3 +1067,51 @@ sendjob:

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
}

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

@@ -512,6 +512,7 @@ var (
ProfileID string
PoolInfos string
Flavor string
DebugHost string
//train-job
ResourcePools string
Engines string
@@ -1325,6 +1326,7 @@ func NewContext() {
ProfileID = sec.Key("PROFILE_ID").MustString("")
PoolInfos = sec.Key("POOL_INFOS").MustString("")
Flavor = sec.Key("FLAVOR").MustString("")
DebugHost = sec.Key("DEBUG_SERVER_HOST").MustString("http://192.168.202.73")
ResourcePools = sec.Key("Resource_Pools").MustString("")
Engines = sec.Key("Engines").MustString("")
EngineVersions = sec.Key("Engine_Versions").MustString("")


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

@@ -883,7 +883,8 @@ func RegisterRoutes(m *macaron.Macaron) {
}, reqRepoReader(models.UnitTypeCloudBrain))
m.Group("/modelarts", 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("/:jobid", func() {


+ 31
- 0
routers/api/v1/repo/modelarts.go View File

@@ -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) {
var (
err error


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

@@ -967,7 +967,8 @@ func SyncCloudbrainStatus() {
}
} else if task.Type == models.TypeCloudBrainTwo {
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 {
log.Error("GetJob(%s) failed:%v", task.JobName, err)
continue


+ 89
- 16
routers/repo/modelarts.go View File

@@ -25,8 +25,6 @@ import (
"code.gitea.io/gitea/modules/obs"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"

"github.com/unknwon/com"
)

const (
@@ -129,6 +127,20 @@ func NotebookNew(ctx *context.Context) {
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) {
ctx.Data["PageIsNotebook"] = true
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")
}

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) {
ctx.Data["PageIsCloudBrain"] = true

@@ -184,7 +244,7 @@ func NotebookShow(ctx *context.Context) {
return
}

result, err := modelarts.GetJob(jobID)
result, err := modelarts.GetNotebook2(jobID)
if err != nil {
ctx.Data["error"] = err.Error()
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookShow, nil)
@@ -200,12 +260,10 @@ func NotebookShow(ctx *context.Context) {
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
@@ -241,6 +299,18 @@ func NotebookDebug(ctx *context.Context) {
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) {
var jobID = ctx.Params(":jobid")
var action = ctx.Params(":action")
@@ -312,15 +382,18 @@ func NotebookManage(ctx *context.Context) {
param := models.NotebookAction{
Action: action,
}
res, err := modelarts.ManageNotebook(jobID, param)
res, err := modelarts.ManageNotebook2(jobID, param)
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"
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
}

task.Status = res.CurrentStatus
task.Status = res.Status
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"])
@@ -352,10 +425,10 @@ func NotebookDel(ctx *context.Context) {
return
}

_, err := modelarts.DelNotebook(jobID)
_, err := modelarts.DelNotebook2(jobID)
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
}



+ 10
- 0
routers/routes/routes.go View File

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

m.Group("/modelarts", func() {
m.Group("/notebook", func() {
/* v1.0
m.Group("/:jobid", func() {
m.Get("", reqRepoCloudBrainReader, repo.NotebookShow)
m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.NotebookDebug)
@@ -1037,6 +1038,15 @@ func RegisterRoutes(m *macaron.Macaron) {
})
m.Get("/create", reqRepoCloudBrainWriter, repo.NotebookNew)
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() {


+ 4
- 4
templates/repo/modelarts/notebook/new.tmpl View File

@@ -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,'')">
</div>

<div class="inline field">
<!-- <div class="inline field">
<label>数据集</label>
<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">
@@ -69,7 +69,7 @@
<div class="inline required field">
<label>类型</label>
<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">
<label>规格</label>
<select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor">
@@ -79,10 +79,10 @@
{{end}}
</select>
</div>
<div class="inline required field">
<!--<div class="inline required field">
<label>数据集存放路径</label>
<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">
<label>描述</label>
<input name="description" id="cloudbrain_description" tabindex="3" autofocus maxlength="255">


+ 1
- 79
templates/repo/modelarts/notebook/show.tmpl View File

@@ -47,85 +47,7 @@
</table>
{{end}}
</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>


Loading…
Cancel
Save