| @@ -2126,6 +2126,14 @@ func GetCloudbrainByName(jobName string) (*Cloudbrain, error) { | |||||
| cb := &Cloudbrain{JobName: jobName} | cb := &Cloudbrain{JobName: jobName} | ||||
| return getRepoCloudBrain(cb) | return getRepoCloudBrain(cb) | ||||
| } | } | ||||
| func GetWaitOrRunFileNotebookByRepo(repoId int64, cloudbrainType int) (*Cloudbrain, error) { | |||||
| cloudBrain := new(Cloudbrain) | |||||
| has, err := x.In("status", JobWaiting, JobRunning).Where("repo_id=? and type=? and boot_file!=''", repoId, cloudbrainType).Get(cloudBrain) | |||||
| if has { | |||||
| return cloudBrain, err | |||||
| } | |||||
| return nil, err | |||||
| } | |||||
| func CanDelJob(isSigned bool, user *User, job *CloudbrainInfo) bool { | func CanDelJob(isSigned bool, user *User, job *CloudbrainInfo) bool { | ||||
| if !isSigned || (job.Status != string(JobStopped) && job.Status != string(JobFailed) && job.Status != string(ModelArtsStartFailed) && job.Status != string(ModelArtsCreateFailed)) { | if !isSigned || (job.Status != string(JobStopped) && job.Status != string(JobFailed) && job.Status != string(ModelArtsStartFailed) && job.Status != string(ModelArtsCreateFailed)) { | ||||
| @@ -709,6 +709,7 @@ type GenerateModelArtsNotebookReq struct { | |||||
| ImageId string | ImageId string | ||||
| AutoStopDurationMs int64 | AutoStopDurationMs int64 | ||||
| BranchName string | |||||
| Spec *models.Specification | Spec *models.Specification | ||||
| ModelName string | ModelName string | ||||
| @@ -1,13 +1,9 @@ | |||||
| package modelarts | package modelarts | ||||
| import ( | import ( | ||||
| "encoding/base64" | |||||
| "encoding/json" | "encoding/json" | ||||
| "errors" | "errors" | ||||
| "fmt" | "fmt" | ||||
| "io/ioutil" | |||||
| "net/http" | |||||
| "path" | |||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| @@ -239,6 +235,7 @@ func GenerateNotebook2(ctx *context.Context, req cloudbrain.GenerateModelArtsNot | |||||
| ComputeResource: models.NPUResource, | ComputeResource: models.NPUResource, | ||||
| Image: imageName, | Image: imageName, | ||||
| BootFile: req.BootFile, | BootFile: req.BootFile, | ||||
| BranchName: req.BranchName, | |||||
| Description: req.Description, | Description: req.Description, | ||||
| CreatedUnix: createTime, | CreatedUnix: createTime, | ||||
| UpdatedUnix: createTime, | UpdatedUnix: createTime, | ||||
| @@ -830,10 +827,6 @@ func HandleNotebookInfo(task *models.Cloudbrain) error { | |||||
| task.FlavorCode = result.Flavor | task.FlavorCode = result.Flavor | ||||
| } | } | ||||
| if oldStatus != task.Status && task.Status == string(models.ModelArtsRunning) && task.BootFile != "" { | |||||
| uploadNoteBookFile(task, result) | |||||
| } | |||||
| err = models.UpdateJob(task) | err = models.UpdateJob(task) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err) | log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err) | ||||
| @@ -844,81 +837,6 @@ func HandleNotebookInfo(task *models.Cloudbrain) error { | |||||
| return nil | return nil | ||||
| } | } | ||||
| func uploadNoteBookFile(task *models.Cloudbrain, result *models.GetNotebook2Result) { | |||||
| jupyterUrl := result.Url + "?token=" + result.Token | |||||
| cookies, xsrf := getCookiesAndCsrf(jupyterUrl) | |||||
| if xsrf == "" { | |||||
| log.Error("browser jupyterUrl failed:%v", task.DisplayJobName) | |||||
| } else { | |||||
| codePath := setting.JobPath + task.JobName + cloudbrain.CodeMountPath | |||||
| fileContents, err := ioutil.ReadFile(codePath + "/" + task.BootFile) | |||||
| if err != nil { | |||||
| log.Error("read jupyter file failed:%v", task.DisplayJobName, err) | |||||
| } | |||||
| base64Content := base64.StdEncoding.EncodeToString(fileContents) | |||||
| client := getRestyClient() | |||||
| uploadUrl := getJupyterBaseUrl(result.Url) + "api/contents/" + path.Base(task.BootFile) | |||||
| res, err := client.R(). | |||||
| SetCookies(cookies). | |||||
| SetHeader("X-XSRFToken", xsrf). | |||||
| SetBody(map[string]interface{}{ | |||||
| "type": "file", | |||||
| "format": "base64", | |||||
| "name": path.Base(task.BootFile), | |||||
| "path": path.Base(task.BootFile), | |||||
| "content": base64Content}). | |||||
| Put(uploadUrl) | |||||
| if err != nil { | |||||
| log.Error("upload jupyter file failed:%v", task.DisplayJobName, err) | |||||
| } else if res.StatusCode() != http.StatusCreated { | |||||
| log.Error("upload jupyter file failed:%v", task.DisplayJobName, err) | |||||
| } | |||||
| } | |||||
| } | |||||
| func getJupyterBaseUrl(url string) string { | |||||
| jupyterUrlLength := len(url) | |||||
| baseUrl := url[0 : jupyterUrlLength-len(path.Base(url))] | |||||
| return baseUrl | |||||
| } | |||||
| func getCookiesAndCsrf(jupyterUrl string) ([]*http.Cookie, string) { | |||||
| log.Info("jupyter url:" + jupyterUrl) | |||||
| var cookies []*http.Cookie | |||||
| const retryTimes = 10 | |||||
| for i := 0; i < retryTimes; i++ { | |||||
| res, err := http.Get(jupyterUrl) | |||||
| if err != nil { | |||||
| log.Error("browser jupyterUrl failed.", err) | |||||
| if i == retryTimes-1 { | |||||
| return cookies, "" | |||||
| } | |||||
| } else { | |||||
| cookies = res.Cookies() | |||||
| xsrf := "" | |||||
| for _, cookie := range cookies { | |||||
| if cookie.Name == "_xsrf" { | |||||
| xsrf = cookie.Value | |||||
| break | |||||
| } | |||||
| } | |||||
| if xsrf != "" { | |||||
| return cookies, xsrf | |||||
| } | |||||
| } | |||||
| } | |||||
| return cookies, "" | |||||
| } | |||||
| func SyncTempStatusJob() { | func SyncTempStatusJob() { | ||||
| jobs, err := models.GetCloudBrainTempJobs() | jobs, err := models.GetCloudBrainTempJobs() | ||||
| if err != nil { | if err != nil { | ||||
| @@ -148,6 +148,7 @@ func GenerateNotebook(ctx *context.Context, req cloudbrain.GenerateModelArtsNote | |||||
| UpdatedUnix: createTime, | UpdatedUnix: createTime, | ||||
| Spec: req.Spec, | Spec: req.Spec, | ||||
| BootFile: req.BootFile, | BootFile: req.BootFile, | ||||
| BranchName: req.BranchName, | |||||
| ModelName: req.ModelName, | ModelName: req.ModelName, | ||||
| ModelVersion: req.ModelVersion, | ModelVersion: req.ModelVersion, | ||||
| LabelName: req.LabelName, | LabelName: req.LabelName, | ||||
| @@ -0,0 +1,198 @@ | |||||
| package notebook | |||||
| import ( | |||||
| "crypto/tls" | |||||
| "encoding/base64" | |||||
| "fmt" | |||||
| "io/ioutil" | |||||
| "net/http" | |||||
| "path" | |||||
| "strings" | |||||
| "github.com/go-resty/resty/v2" | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/cloudbrain" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| ) | |||||
| var restyClient *resty.Client | |||||
| type NotebookApiResponse struct { | |||||
| Name string `json:"name"` | |||||
| Path string `json:"path"` | |||||
| } | |||||
| type NotebookContent struct { | |||||
| Url string | |||||
| Path string | |||||
| Cookies []*http.Cookie | |||||
| Xsrf string | |||||
| PathType string //file directory | |||||
| Token string | |||||
| } | |||||
| func (c *NotebookContent) IsNotebookFileCanBrowser() bool { | |||||
| if c.Xsrf == "" { | |||||
| c.SetCookiesAndCsrf() | |||||
| } | |||||
| if c.Xsrf == "" { | |||||
| log.Warn("xsrf is empty, can not broswer url:" + c.Url) | |||||
| return false | |||||
| } | |||||
| return c.IsNoteBookContentsExist() | |||||
| } | |||||
| func (c *NotebookContent) SetCookiesAndCsrf() { | |||||
| log.Info("jupyter url:" + c.Url) | |||||
| var cookies []*http.Cookie | |||||
| const retryTimes = 10 | |||||
| url := c.Url | |||||
| if c.Token != "" { | |||||
| url = c.Url + "?token=" + c.Token | |||||
| } | |||||
| for i := 0; i < retryTimes; i++ { | |||||
| res, err := http.Get(url) | |||||
| if err != nil { | |||||
| log.Error("browser jupyterUrl failed.", err) | |||||
| if i == retryTimes-1 { | |||||
| c.Cookies = cookies | |||||
| } | |||||
| } else { | |||||
| cookies = res.Cookies() | |||||
| xsrf := "" | |||||
| for _, cookie := range cookies { | |||||
| if cookie.Name == "_xsrf" { | |||||
| xsrf = cookie.Value | |||||
| if len(cookies) > 1 { | |||||
| break | |||||
| } | |||||
| } | |||||
| } | |||||
| if xsrf != "" { | |||||
| c.Cookies = cookies | |||||
| c.Xsrf = xsrf | |||||
| } | |||||
| } | |||||
| } | |||||
| c.Cookies = cookies | |||||
| } | |||||
| func (c *NotebookContent) IsNoteBookContentsExist() bool { | |||||
| client := getRestyClient() | |||||
| uploadUrl := getJupyterBaseUrl(c.Url) + "api/contents/" + c.Path + "?type=" + c.PathType | |||||
| res, err := client.R(). | |||||
| SetCookies(c.Cookies). | |||||
| SetHeader("X-XSRFToken", c.Xsrf). | |||||
| Get(uploadUrl) | |||||
| if err != nil { | |||||
| log.Warn("browser url error:"+uploadUrl, err) | |||||
| return false | |||||
| } | |||||
| return res.StatusCode() == http.StatusOK | |||||
| } | |||||
| func (c *NotebookContent) UploadNoteBookFile(task *models.Cloudbrain) error { | |||||
| err := c.MakeNoteBookDir() | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| codePath := setting.JobPath + task.JobName + cloudbrain.CodeMountPath | |||||
| fileContents, err := ioutil.ReadFile(codePath + "/" + c.Path) | |||||
| if err != nil { | |||||
| log.Error("read jupyter file failed:%v", task.DisplayJobName, err) | |||||
| } | |||||
| base64Content := base64.StdEncoding.EncodeToString(fileContents) | |||||
| client := getRestyClient() | |||||
| uploadUrl := getJupyterBaseUrl(c.Url) + "api/contents/" + c.Path | |||||
| res, err := client.R(). | |||||
| SetCookies(c.Cookies). | |||||
| SetHeader("X-XSRFToken", c.Xsrf). | |||||
| SetBody(map[string]interface{}{ | |||||
| "type": "file", | |||||
| "format": "base64", | |||||
| "name": path.Base(c.Path), | |||||
| "path": c.Path, | |||||
| "content": base64Content}). | |||||
| Put(uploadUrl) | |||||
| if err != nil { | |||||
| log.Error("upload jupyter file failed:%v", task.DisplayJobName, err) | |||||
| return err | |||||
| } else if res.StatusCode() != http.StatusCreated { | |||||
| log.Error("upload jupyter file failed:%v, status is %s", task.DisplayJobName, res.Status(), err) | |||||
| return fmt.Errorf("status:", res.StatusCode()) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| /** | |||||
| if c.Path is a/b/c.txt | |||||
| makedir a/b | |||||
| if c.Path is a/b/c | |||||
| makedir a/b | |||||
| */ | |||||
| func (c *NotebookContent) MakeNoteBookDir() error { | |||||
| filePaths := strings.Split(c.Path, "/") | |||||
| for i := 0; i < len(filePaths)-1; i++ { | |||||
| cTemp := &NotebookContent{ | |||||
| Url: c.Url, | |||||
| Cookies: c.Cookies, | |||||
| Path: path.Join(filePaths[0 : i+1]...), | |||||
| PathType: "directory", | |||||
| Xsrf: c.Xsrf, | |||||
| } | |||||
| if !cTemp.IsNoteBookContentsExist() { | |||||
| createTempDirUrl := getJupyterBaseUrl(cTemp.Url) + "api/contents/" + cTemp.Path | |||||
| client := getRestyClient() | |||||
| var jobResult NotebookApiResponse | |||||
| res, err := client.R(). | |||||
| SetCookies(c.Cookies). | |||||
| SetHeader("X-XSRFToken", c.Xsrf). | |||||
| SetBody(map[string]interface{}{ | |||||
| "type": cTemp.PathType, | |||||
| "path": cTemp.Path, | |||||
| }).SetResult(&jobResult). | |||||
| Put(createTempDirUrl) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| if res.StatusCode() != http.StatusCreated { | |||||
| return fmt.Errorf("status code:" + res.Status()) | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func getJupyterBaseUrl(url string) string { | |||||
| jupyterUrlLength := len(url) | |||||
| baseUrl := url | |||||
| if strings.HasSuffix(url, "lab") { | |||||
| baseUrl = url[0 : jupyterUrlLength-len(path.Base(url))] | |||||
| } | |||||
| return baseUrl | |||||
| } | |||||
| func getRestyClient() *resty.Client { | |||||
| if restyClient == nil { | |||||
| restyClient = resty.New() | |||||
| restyClient.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) | |||||
| } | |||||
| return restyClient | |||||
| } | |||||
| @@ -47,6 +47,7 @@ type CreateFileNotebookJobOption struct { | |||||
| BranchName string `json:"branch_name" binding:"Required"` | BranchName string `json:"branch_name" binding:"Required"` | ||||
| OwnerName string `json:"owner_name" binding:"Required"` | OwnerName string `json:"owner_name" binding:"Required"` | ||||
| ProjectName string `json:"project_name" binding:"Required"` | ProjectName string `json:"project_name" binding:"Required"` | ||||
| JobId string `json:"job_id"` | |||||
| } | } | ||||
| type Cloudbrain struct { | type Cloudbrain struct { | ||||
| @@ -1061,7 +1061,7 @@ model_rename=Duplicate model name, please modify model name. | |||||
| notebook_file_not_exist=Notebook file does not exist. | notebook_file_not_exist=Notebook file does not exist. | ||||
| notebook_select_wrong=Please select a Notebook(.ipynb) file first. | notebook_select_wrong=Please select a Notebook(.ipynb) file first. | ||||
| notebook_file_no_right=You have no right to access the Notebook(.ipynb) file. | notebook_file_no_right=You have no right to access the Notebook(.ipynb) file. | ||||
| debug_again_fail=Fail to restart debug task, please try again later. | |||||
| notebook_repo_conflict=The files in different branches of the same repository can not run together. | |||||
| date=Date | date=Date | ||||
| repo_add=Project Increment | repo_add=Project Increment | ||||
| @@ -1060,7 +1060,7 @@ model_rename=模型名称重复,请修改模型名称 | |||||
| notebook_file_not_exist=Notebook文件不存在。 | notebook_file_not_exist=Notebook文件不存在。 | ||||
| notebook_select_wrong=请先选择Notebook(.ipynb)文件。 | notebook_select_wrong=请先选择Notebook(.ipynb)文件。 | ||||
| notebook_file_no_right=您没有这个Notebook文件的读权限。 | notebook_file_no_right=您没有这个Notebook文件的读权限。 | ||||
| debug_again_fail=再次调试失败,请稍后再试。 | |||||
| notebook_repo_conflict=同一个仓库的不同分支文件不能同时运行。 | |||||
| date=日期 | date=日期 | ||||
| repo_add=新增项目 | repo_add=新增项目 | ||||
| @@ -745,7 +745,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Group("/file_notebook", func() { | m.Group("/file_notebook", func() { | ||||
| m.Get("", repo.GetFileNoteBookInfo) | m.Get("", repo.GetFileNoteBookInfo) | ||||
| m.Post("/create", reqToken(), reqWeChat(), bind(api.CreateFileNotebookJobOption{}), repo.CreateFileNoteBook) | m.Post("/create", reqToken(), reqWeChat(), bind(api.CreateFileNotebookJobOption{}), repo.CreateFileNoteBook) | ||||
| //m.Post("/status", reqToken(), bind(api.CreateFileNotebookJobOption{}), repo.FileNoteBookStatus) | |||||
| m.Post("/status", bind(api.CreateFileNotebookJobOption{}), repo.FileNoteBookStatus) | |||||
| }) | }) | ||||
| m.Group("/repos", func() { | m.Group("/repos", func() { | ||||
| @@ -110,6 +110,9 @@ func GeneralCloudBrainJobStop(ctx *context.APIContext) { | |||||
| func CreateFileNoteBook(ctx *context.APIContext, option api.CreateFileNotebookJobOption) { | func CreateFileNoteBook(ctx *context.APIContext, option api.CreateFileNotebookJobOption) { | ||||
| cloudbrainTask.FileNotebookCreate(ctx.Context, option) | cloudbrainTask.FileNotebookCreate(ctx.Context, option) | ||||
| } | } | ||||
| func FileNoteBookStatus(ctx *context.APIContext, option api.CreateFileNotebookJobOption) { | |||||
| cloudbrainTask.FileNotebookStatus(ctx.Context, option) | |||||
| } | |||||
| func GetFileNoteBookInfo(ctx *context.APIContext) { | func GetFileNoteBookInfo(ctx *context.APIContext) { | ||||
| //image description spec description waiting count | //image description spec description waiting count | ||||
| @@ -440,10 +440,9 @@ func NotebookDebug2(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| if task.BootFile != "" { | if task.BootFile != "" { | ||||
| ctx.Redirect(getFileUrl(result.Url, task.BootFile) + "?token=" + result.Token) | |||||
| } else { | |||||
| ctx.Redirect(result.Url + "?token=" + result.Token) | |||||
| go cloudbrainTask.UploadNotebookFiles(task) | |||||
| } | } | ||||
| ctx.Redirect(result.Url + "?token=" + result.Token) | |||||
| } | } | ||||
| @@ -4,6 +4,9 @@ import ( | |||||
| "fmt" | "fmt" | ||||
| "net/http" | "net/http" | ||||
| "path" | "path" | ||||
| "strings" | |||||
| "code.gitea.io/gitea/modules/notebook" | |||||
| "code.gitea.io/gitea/modules/modelarts" | "code.gitea.io/gitea/modules/modelarts" | ||||
| "code.gitea.io/gitea/modules/modelarts_cd" | "code.gitea.io/gitea/modules/modelarts_cd" | ||||
| @@ -29,6 +32,9 @@ import ( | |||||
| ) | ) | ||||
| const NoteBookExtension = ".ipynb" | const NoteBookExtension = ".ipynb" | ||||
| const CPUType = 0 | |||||
| const GPUType = 1 | |||||
| const NPUType = 2 | |||||
| func FileNotebookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption) { | func FileNotebookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption) { | ||||
| @@ -66,7 +72,7 @@ func FileNotebookCreate(ctx *context.Context, option api.CreateFileNotebookJobOp | |||||
| } | } | ||||
| //create repo if not exist | //create repo if not exist | ||||
| repo, err := models.GetRepositoryByName(ctx.User.ID, setting.FileNoteBook.ProjectName) | |||||
| repo, _ := models.GetRepositoryByName(ctx.User.ID, setting.FileNoteBook.ProjectName) | |||||
| if repo == nil { | if repo == nil { | ||||
| repo, err = repo_service.CreateRepository(ctx.User, ctx.User, models.CreateRepoOptions{ | repo, err = repo_service.CreateRepository(ctx.User, ctx.User, models.CreateRepoOptions{ | ||||
| Name: setting.FileNoteBook.ProjectName, | Name: setting.FileNoteBook.ProjectName, | ||||
| @@ -80,17 +86,200 @@ func FileNotebookCreate(ctx *context.Context, option api.CreateFileNotebookJobOp | |||||
| AutoInit: true, | AutoInit: true, | ||||
| DefaultBranch: "master", | DefaultBranch: "master", | ||||
| }) | }) | ||||
| if err != nil { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.failed_to_create_notebook_repo", setting.FileNoteBook.ProjectName))) | |||||
| return | |||||
| } | |||||
| } else { | |||||
| noteBook, _ := models.GetWaitOrRunFileNotebookByRepo(repo.ID, getCloudbrainType(option.Type)) | |||||
| if noteBook != nil { | |||||
| if isRepoConfilcts(option, noteBook) { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_repo_conflict"))) | |||||
| return | |||||
| } | |||||
| if isNotebookSpecMath(option, noteBook) { | |||||
| err = downloadCode(sourceRepo, getCodePath(noteBook.JobName, sourceRepo), option.BranchName) | |||||
| if err != nil { | |||||
| log.Error("download code failed", err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.load_code_failed"))) | |||||
| return | |||||
| } | |||||
| noteBook.BootFile += ";" + getBootFile(option.File, option.OwnerName, option.ProjectName) | |||||
| noteBook.BranchName += ";" + option.BranchName | |||||
| err := models.UpdateJob(noteBook) | |||||
| if err != nil { | |||||
| log.Error("GenerateNotebook2 failed, %v", err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error())) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, models.BaseMessageApi{ | |||||
| Code: 0, | |||||
| Message: noteBook.JobID, | |||||
| }) | |||||
| return | |||||
| } | |||||
| } | |||||
| } | |||||
| if option.Type <= GPUType { | |||||
| cloudBrainFileNoteBookCreate(ctx, option, repo, sourceRepo) | |||||
| } else { | |||||
| modelartsFileNoteBookCreate(ctx, option, repo, sourceRepo) | |||||
| } | } | ||||
| } | |||||
| func FileNotebookStatus(ctx *context.Context, option api.CreateFileNotebookJobOption) { | |||||
| if ctx.Written() { | |||||
| return | |||||
| } | |||||
| if path.Ext(option.File) != NoteBookExtension { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_select_wrong"))) | |||||
| return | |||||
| } | |||||
| isNotebookFileExist, _ := isNoteBookFileExist(ctx, option) | |||||
| if !isNotebookFileExist { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist"))) | |||||
| return | |||||
| } | |||||
| task, err := models.GetCloudbrainByJobID(option.JobId) | |||||
| if err != nil { | if err != nil { | ||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.failed_to_create_notebook_repo", setting.FileNoteBook.ProjectName))) | |||||
| log.Error("job not found:"+option.JobId, err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("Job id may not be right. can not find job.")) | |||||
| return | return | ||||
| } | } | ||||
| if option.Type <= 1 { | |||||
| cloudBrainFileNoteBookCreate(ctx, option, repo, sourceRepo) | |||||
| if task.BootFile == "" || task.Status != string(models.ModelArtsRunning) { | |||||
| log.Warn("Boot file is empty or status is running. ") | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("Boot file is empty or status is running.")) | |||||
| return | |||||
| } | |||||
| if !isRepoFileMatch(option, task) { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("can not math repo file.")) | |||||
| return | |||||
| } | |||||
| debugBaseUrl, token, err := getBaseUrlAndToken(task) | |||||
| if err != nil { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error())) | |||||
| return | |||||
| } | |||||
| if uploadNotebookFileIfCannotBroswer(debugBaseUrl, getBootFile(option.File, option.OwnerName, option.ProjectName), task, token) { | |||||
| ctx.JSON(http.StatusOK, models.BaseOKMessageApi) | |||||
| } else { | } else { | ||||
| modelartsFileNoteBookCreate(ctx, option, repo, sourceRepo) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("upload failed.")) | |||||
| } | |||||
| } | |||||
| func getBaseUrlAndToken(task *models.Cloudbrain) (string, string, error) { | |||||
| var debugBaseUrl string | |||||
| var token string | |||||
| if task.Type == models.TypeCloudBrainOne { | |||||
| debugBaseUrl = setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName + "/lab" | |||||
| } else { | |||||
| var result *models.GetNotebook2Result | |||||
| var err error | |||||
| if task.Type == models.TypeCloudBrainTwo { | |||||
| result, err = modelarts.GetNotebook2(task.JobID) | |||||
| } else if task.Type == models.TypeCDCenter { | |||||
| result, err = modelarts_cd.GetNotebook(task.JobID) | |||||
| } | |||||
| if err != nil || result == nil || result.Status != string(models.ModelArtsRunning) || result.Url == "" { | |||||
| log.Error("notebook job not found:"+task.JobID, err) | |||||
| return "", "", fmt.Errorf("can not get job or job is invalid.") | |||||
| } | |||||
| debugBaseUrl = result.Url | |||||
| token = result.Token | |||||
| } | |||||
| return debugBaseUrl, token, nil | |||||
| } | |||||
| func uploadNotebookFileIfCannotBroswer(debugBaseUrl string, bootFile string, task *models.Cloudbrain, token string) bool { | |||||
| c := ¬ebook.NotebookContent{ | |||||
| Url: debugBaseUrl, | |||||
| Path: bootFile, | |||||
| PathType: "file", | |||||
| Token: token, | |||||
| } | |||||
| if c.IsNotebookFileCanBrowser() { | |||||
| return true | |||||
| } else { | |||||
| c.SetCookiesAndCsrf() | |||||
| c.UploadNoteBookFile(task) | |||||
| return c.IsNotebookFileCanBrowser() | |||||
| } | |||||
| } | |||||
| func isNotebookSpecMath(option api.CreateFileNotebookJobOption, book *models.Cloudbrain) bool { | |||||
| if option.Type == NPUType || option.Type == CPUType { | |||||
| return true | |||||
| } | |||||
| spec, err := models.GetCloudbrainSpecByID(book.ID) | |||||
| if err != nil { | |||||
| log.Warn("can not get spec ", err) | |||||
| return false | |||||
| } | |||||
| return spec.AccCardsNum > 0 | |||||
| } | |||||
| func isRepoConfilcts(option api.CreateFileNotebookJobOption, book *models.Cloudbrain) bool { | |||||
| bootFiles := strings.Split(book.BootFile, ";") | |||||
| branches := strings.Split(book.BranchName, ";") | |||||
| for i, bootFile := range bootFiles { | |||||
| splits := strings.Split(bootFile, "/") | |||||
| if len(splits) >= 3 { | |||||
| if splits[0] == option.OwnerName && splits[1] == option.ProjectName && branches[i] != option.BranchName { | |||||
| return true | |||||
| } | |||||
| } | |||||
| } | } | ||||
| return false | |||||
| } | |||||
| func isRepoFileMatch(option api.CreateFileNotebookJobOption, book *models.Cloudbrain) bool { | |||||
| bootFiles := strings.Split(book.BootFile, ";") | |||||
| branches := strings.Split(book.BranchName, ";") | |||||
| for i, bootFile := range bootFiles { | |||||
| if branches[i] == option.BranchName && getBootFile(option.File, option.OwnerName, option.ProjectName) == bootFile { | |||||
| return true | |||||
| } | |||||
| } | |||||
| return false | |||||
| } | |||||
| func UploadNotebookFiles(task *models.Cloudbrain) { | |||||
| if task.Status == string(models.JobRunning) && task.BootFile != "" { | |||||
| debugBaseUrl, token, err := getBaseUrlAndToken(task) | |||||
| if err != nil { | |||||
| log.Error("can not get base url:", err) | |||||
| return | |||||
| } | |||||
| bootFiles := strings.Split(task.BootFile, ";") | |||||
| for _, bootFile := range bootFiles { | |||||
| uploadNotebookFileIfCannotBroswer(debugBaseUrl, bootFile, task, token) | |||||
| } | |||||
| } | |||||
| } | } | ||||
| func cloudBrainFileNoteBookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption, repo *models.Repository, sourceRepo *models.Repository) { | func cloudBrainFileNoteBookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption, repo *models.Repository, sourceRepo *models.Repository) { | ||||
| @@ -131,17 +320,18 @@ func cloudBrainFileNoteBookCreate(ctx *context.Context, option api.CreateFileNot | |||||
| } else { | } else { | ||||
| if count >= 1 { | if count >= 1 { | ||||
| log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | ||||
| ctx.JSON(http.StatusOK,models.BaseMessageApi{ | |||||
| Code: 2, | |||||
| ctx.JSON(http.StatusOK, models.BaseMessageApi{ | |||||
| Code: 2, | |||||
| Message: ctx.Tr("repo.cloudbrain.morethanonejob"), | Message: ctx.Tr("repo.cloudbrain.morethanonejob"), | ||||
| }) | }) | ||||
| return | return | ||||
| } | } | ||||
| } | } | ||||
| errStr := uploadCodeFile(sourceRepo, getCodePath(jobName), option.BranchName, option.File, jobName) | |||||
| if errStr != "" { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist"))) | |||||
| err = downloadCode(sourceRepo, getCodePath(jobName, sourceRepo), option.BranchName) | |||||
| if err != nil { | |||||
| log.Error("download code failed", err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.load_code_failed"))) | |||||
| return | return | ||||
| } | } | ||||
| command := cloudbrain.GetCloudbrainDebugCommand() | command := cloudbrain.GetCloudbrainDebugCommand() | ||||
| @@ -185,7 +375,7 @@ func cloudBrainFileNoteBookCreate(ctx *context.Context, option api.CreateFileNot | |||||
| JobType: jobType, | JobType: jobType, | ||||
| Description: getDescription(option), | Description: getDescription(option), | ||||
| BranchName: option.BranchName, | BranchName: option.BranchName, | ||||
| BootFile: option.File, | |||||
| BootFile: getBootFile(option.File, option.OwnerName, option.ProjectName), | |||||
| Params: "{\"parameter\":[]}", | Params: "{\"parameter\":[]}", | ||||
| CommitID: "", | CommitID: "", | ||||
| BenchmarkTypeID: 0, | BenchmarkTypeID: 0, | ||||
| @@ -206,8 +396,15 @@ func cloudBrainFileNoteBookCreate(ctx *context.Context, option api.CreateFileNot | |||||
| } | } | ||||
| func getCodePath(jobName string) string { | |||||
| return setting.JobPath + jobName + cloudbrain.CodeMountPath | |||||
| func getCloudbrainType(optionType int) int { | |||||
| if optionType < 1 { | |||||
| return models.TypeCloudBrainOne | |||||
| } | |||||
| return models.TypeCloudBrainTwo | |||||
| } | |||||
| func getCodePath(jobName string, repo *models.Repository) string { | |||||
| return setting.JobPath + jobName + cloudbrain.CodeMountPath + "/" + repo.OwnerName + "/" + repo.Name | |||||
| } | } | ||||
| func getDescription(option api.CreateFileNotebookJobOption) string { | func getDescription(option api.CreateFileNotebookJobOption) string { | ||||
| @@ -237,8 +434,8 @@ func modelartsFileNoteBookCreate(ctx *context.Context, option api.CreateFileNote | |||||
| } else { | } else { | ||||
| if count >= 1 { | if count >= 1 { | ||||
| log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | ||||
| ctx.JSON(http.StatusOK,models.BaseMessageApi{ | |||||
| Code: 2, | |||||
| ctx.JSON(http.StatusOK, models.BaseMessageApi{ | |||||
| Code: 2, | |||||
| Message: ctx.Tr("repo.cloudbrain.morethanonejob"), | Message: ctx.Tr("repo.cloudbrain.morethanonejob"), | ||||
| }) | }) | ||||
| return | return | ||||
| @@ -260,7 +457,7 @@ func modelartsFileNoteBookCreate(ctx *context.Context, option api.CreateFileNote | |||||
| } | } | ||||
| } | } | ||||
| err = downloadCode(sourceRepo, getCodePath(jobName), option.BranchName) | |||||
| err = downloadCode(sourceRepo, getCodePath(jobName, sourceRepo), option.BranchName) | |||||
| if err != nil { | if err != nil { | ||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.load_code_failed"))) | ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.load_code_failed"))) | ||||
| return | return | ||||
| @@ -299,6 +496,7 @@ func modelartsFileNoteBookCreate(ctx *context.Context, option api.CreateFileNote | |||||
| Spec: spec, | Spec: spec, | ||||
| BootFile: "", | BootFile: "", | ||||
| AutoStopDurationMs: modelarts.AutoStopDurationMs / 4, | AutoStopDurationMs: modelarts.AutoStopDurationMs / 4, | ||||
| BranchName: option.BranchName, | |||||
| } | } | ||||
| if setting.ModelartsCD.Enabled { | if setting.ModelartsCD.Enabled { | ||||
| @@ -347,17 +545,8 @@ func isNoteBookFileExist(ctx *context.Context, option api.CreateFileNotebookJobO | |||||
| return true, nil | return true, nil | ||||
| } | } | ||||
| func uploadCodeFile(repo *models.Repository, codePath string, branchName string, filePath string, jobName string) string { | |||||
| err := downloadCode(repo, codePath, branchName) | |||||
| if err != nil { | |||||
| return "cloudbrain.load_code_failed" | |||||
| } | |||||
| err = uploadOneFileToMinio(codePath, filePath, jobName, cloudbrain.CodeMountPath+"/") | |||||
| if err != nil { | |||||
| return "cloudbrain.load_code_failed" | |||||
| } | |||||
| return "" | |||||
| func getBootFile(filePath string, ownerName string, projectName string) string { | |||||
| return ownerName + "/" + projectName + "/" + filePath | |||||
| } | } | ||||
| func fileExists(gitRepo *git.Repository, path string, branch string) (bool, error) { | func fileExists(gitRepo *git.Repository, path string, branch string) (bool, error) { | ||||