You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

notebook.go 12 kB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package cloudbrainTask
  2. import (
  3. "fmt"
  4. "net/http"
  5. "path"
  6. "code.gitea.io/gitea/modules/modelarts"
  7. "code.gitea.io/gitea/modules/modelarts_cd"
  8. "code.gitea.io/gitea/modules/git"
  9. "code.gitea.io/gitea/modules/cloudbrain"
  10. "code.gitea.io/gitea/modules/log"
  11. "code.gitea.io/gitea/modules/redis/redis_key"
  12. "code.gitea.io/gitea/modules/redis/redis_lock"
  13. "code.gitea.io/gitea/modules/storage"
  14. "code.gitea.io/gitea/services/cloudbrain/resource"
  15. "code.gitea.io/gitea/services/reward/point/account"
  16. "code.gitea.io/gitea/modules/setting"
  17. cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
  18. repo_service "code.gitea.io/gitea/services/repository"
  19. "code.gitea.io/gitea/models"
  20. "code.gitea.io/gitea/modules/context"
  21. api "code.gitea.io/gitea/modules/structs"
  22. "code.gitea.io/gitea/modules/util"
  23. )
  24. const NoteBookExtension = ".ipynb"
  25. func FileNotebookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption) {
  26. if ctx.Written() {
  27. return
  28. }
  29. if path.Ext(option.File) != NoteBookExtension {
  30. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_select_wrong")))
  31. return
  32. }
  33. isNotebookFileExist, _ := isNoteBookFileExist(ctx, option)
  34. if !isNotebookFileExist {
  35. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
  36. return
  37. }
  38. //create repo if not exist
  39. repo, err := models.GetRepositoryByName(ctx.User.ID, setting.FileNoteBook.ProjectName)
  40. if repo == nil {
  41. repo, err = repo_service.CreateRepository(ctx.User, ctx.User, models.CreateRepoOptions{
  42. Name: setting.FileNoteBook.ProjectName,
  43. Alias: "",
  44. Description: "",
  45. IssueLabels: "",
  46. Gitignores: "",
  47. License: "",
  48. Readme: "Default",
  49. IsPrivate: false,
  50. AutoInit: true,
  51. DefaultBranch: "master",
  52. })
  53. }
  54. if err != nil {
  55. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("repo.failed_to_create_repo"))
  56. return
  57. }
  58. if option.Type <= 1 {
  59. cloudBrainFileNoteBookCreate(ctx, option, repo)
  60. } else {
  61. modelartsFileNoteBookCreate(ctx, option, repo)
  62. }
  63. }
  64. func cloudBrainFileNoteBookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption, repo *models.Repository) {
  65. displayJobName := cloudbrainService.GetDisplayJobName(ctx.User.Name)
  66. jobName := util.ConvertDisplayJobNameToJobName(displayJobName)
  67. jobType := string(models.JobTypeDebug)
  68. lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), jobType, displayJobName))
  69. defer lock.UnLock()
  70. isOk, err := lock.Lock(models.CloudbrainKeyDuration)
  71. if !isOk {
  72. log.Error("lock processed failed:%v", err, ctx.Data["MsgID"])
  73. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
  74. return
  75. }
  76. tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, jobType, displayJobName)
  77. if err == nil {
  78. if len(tasks) != 0 {
  79. log.Error("the job name did already exist", ctx.Data["MsgID"])
  80. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
  81. return
  82. }
  83. } else {
  84. if !models.IsErrJobNotExist(err) {
  85. log.Error("system error, %v", err, ctx.Data["MsgID"])
  86. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
  87. return
  88. }
  89. }
  90. count, err := GetNotFinalStatusTaskCount(ctx.User.ID, models.TypeCloudBrainOne, jobType)
  91. if err != nil {
  92. log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"])
  93. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
  94. return
  95. } else {
  96. if count >= 1 {
  97. log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
  98. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain.morethanonejob")))
  99. return
  100. }
  101. }
  102. sourceRepo, err := models.GetRepositoryByOwnerAndName(option.OwnerName, option.ProjectName)
  103. if err != nil {
  104. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
  105. return
  106. }
  107. errStr := uploadCodeFile(sourceRepo, getCodePath(jobName), option.BranchName, option.File, jobName)
  108. if errStr != "" {
  109. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
  110. return
  111. }
  112. command := cloudbrain.GetCloudbrainDebugCommand()
  113. specId := setting.FileNoteBook.SpecIdGPU
  114. if option.Type == 0 {
  115. specId = setting.FileNoteBook.SpecIdCPU
  116. }
  117. spec, err := resource.GetAndCheckSpec(ctx.User.ID, specId, models.FindSpecsOptions{
  118. JobType: models.JobType(jobType),
  119. ComputeResource: models.GPU,
  120. Cluster: models.OpenICluster,
  121. AiCenterCode: models.AICenterOfCloudBrainOne})
  122. if err != nil || spec == nil {
  123. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.wrong_specification")))
  124. return
  125. }
  126. if !account.IsPointBalanceEnough(ctx.User.ID, spec.UnitPrice) {
  127. log.Error("point balance is not enough,userId=%d specId=%d", ctx.User.ID, spec.ID)
  128. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("points.insufficient_points_balance")))
  129. return
  130. }
  131. ctx.Repo = &context.Repository{
  132. Repository: repo,
  133. }
  134. req := cloudbrain.GenerateCloudBrainTaskReq{
  135. Ctx: ctx,
  136. DisplayJobName: displayJobName,
  137. JobName: jobName,
  138. Image: setting.FileNoteBook.ImageGPU,
  139. Command: command,
  140. Uuids: "",
  141. DatasetNames: "",
  142. DatasetInfos: nil,
  143. CodePath: storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
  144. ModelPath: storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/"),
  145. BenchmarkPath: storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"),
  146. Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
  147. BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"),
  148. JobType: jobType,
  149. Description: getDescription(option),
  150. BranchName: option.BranchName,
  151. BootFile: option.File,
  152. Params: "{\"parameter\":[]}",
  153. CommitID: "",
  154. BenchmarkTypeID: 0,
  155. BenchmarkChildTypeID: 0,
  156. ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"),
  157. Spec: spec,
  158. }
  159. jobId, err := cloudbrain.GenerateTask(req)
  160. if err != nil {
  161. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error()))
  162. return
  163. }
  164. ctx.JSON(http.StatusOK, models.BaseMessageApi{
  165. Code: 0,
  166. Message: jobId,
  167. })
  168. }
  169. func getCodePath(jobName string) string {
  170. return setting.JobPath + jobName + cloudbrain.CodeMountPath
  171. }
  172. func getDescription(option api.CreateFileNotebookJobOption) string {
  173. return option.OwnerName + "/" + option.ProjectName + "/" + option.File
  174. }
  175. func modelartsFileNoteBookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption, repo *models.Repository) {
  176. displayJobName := cloudbrainService.GetDisplayJobName(ctx.User.Name)
  177. jobName := util.ConvertDisplayJobNameToJobName(displayJobName)
  178. lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), string(models.JobTypeDebug), displayJobName))
  179. isOk, err := lock.Lock(models.CloudbrainKeyDuration)
  180. if !isOk {
  181. log.Error("lock processed failed:%v", err, ctx.Data["MsgID"])
  182. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
  183. return
  184. }
  185. defer lock.UnLock()
  186. count, err := GetNotFinalStatusTaskCount(ctx.User.ID, models.TypeCloudBrainTwo, string(models.JobTypeDebug))
  187. if err != nil {
  188. log.Error("GetCloudbrainNotebookCountByUserID failed:%v", err, ctx.Data["MsgID"])
  189. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
  190. return
  191. } else {
  192. if count >= 1 {
  193. log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
  194. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain.morethanonejob")))
  195. return
  196. }
  197. }
  198. tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, string(models.JobTypeDebug), displayJobName)
  199. if err == nil {
  200. if len(tasks) != 0 {
  201. log.Error("the job name did already exist", ctx.Data["MsgID"])
  202. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
  203. return
  204. }
  205. } else {
  206. if !models.IsErrJobNotExist(err) {
  207. log.Error("system error, %v", err, ctx.Data["MsgID"])
  208. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
  209. return
  210. }
  211. }
  212. sourceRepo, err := models.GetRepositoryByOwnerAndName(option.OwnerName, option.ProjectName)
  213. if err != nil {
  214. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
  215. return
  216. }
  217. err = downloadCode(sourceRepo, getCodePath(jobName), option.BranchName)
  218. if err != nil {
  219. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.load_code_failed")))
  220. return
  221. }
  222. var aiCenterCode = models.AICenterOfCloudBrainTwo
  223. var specId = setting.FileNoteBook.SpecIdNPU
  224. if setting.ModelartsCD.Enabled {
  225. aiCenterCode = models.AICenterOfChengdu
  226. specId = setting.FileNoteBook.SpecIdNPUCD
  227. }
  228. spec, err := resource.GetAndCheckSpec(ctx.User.ID, specId, models.FindSpecsOptions{
  229. JobType: models.JobTypeDebug,
  230. ComputeResource: models.NPU,
  231. Cluster: models.OpenICluster,
  232. AiCenterCode: aiCenterCode})
  233. if err != nil || spec == nil {
  234. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.wrong_specification")))
  235. return
  236. }
  237. if !account.IsPointBalanceEnough(ctx.User.ID, spec.UnitPrice) {
  238. log.Error("point balance is not enough,userId=%d specId=%d ", ctx.User.ID, spec.ID)
  239. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("points.insufficient_points_balance")))
  240. return
  241. }
  242. ctx.Repo = &context.Repository{
  243. Repository: repo,
  244. }
  245. var jobId string
  246. if setting.ModelartsCD.Enabled {
  247. jobId, err = modelarts_cd.GenerateNotebook(ctx, displayJobName, jobName, "", getDescription(option), setting.FileNoteBook.ImageIdNPUCD, spec, option.File)
  248. } else {
  249. jobId, err = modelarts.GenerateNotebook2(ctx, displayJobName, jobName, "", getDescription(option), setting.FileNoteBook.ImageIdNPU, spec, option.File)
  250. }
  251. if err != nil {
  252. log.Error("GenerateNotebook2 failed, %v", err, ctx.Data["MsgID"])
  253. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error()))
  254. return
  255. }
  256. ctx.JSON(http.StatusOK, models.BaseMessageApi{
  257. Code: 0,
  258. Message: jobId,
  259. })
  260. }
  261. func isNoteBookFileExist(ctx *context.Context, option api.CreateFileNotebookJobOption) (bool, error) {
  262. repoPathOfNoteBook := models.RepoPath(option.OwnerName, option.ProjectName)
  263. gitRepoOfNoteBook, err := git.OpenRepository(repoPathOfNoteBook)
  264. if err != nil {
  265. log.Error("RepoRef Invalid repo "+repoPathOfNoteBook, err.Error())
  266. return false, err
  267. }
  268. // We opened it, we should close it
  269. defer func() {
  270. // If it's been set to nil then assume someone else has closed it.
  271. if gitRepoOfNoteBook != nil {
  272. gitRepoOfNoteBook.Close()
  273. }
  274. }()
  275. fileExist, err := fileExists(gitRepoOfNoteBook, option.File, option.BranchName)
  276. if err != nil || !fileExist {
  277. log.Error("Get file error:", err, ctx.Data["MsgID"])
  278. return false, err
  279. }
  280. return true, nil
  281. }
  282. func uploadCodeFile(repo *models.Repository, codePath string, branchName string, filePath string, jobName string) string {
  283. err := downloadCode(repo, codePath, branchName)
  284. if err != nil {
  285. return "cloudbrain.load_code_failed"
  286. }
  287. err = uploadOneFileToMinio(codePath, filePath, jobName, cloudbrain.CodeMountPath+"/")
  288. if err != nil {
  289. return "cloudbrain.load_code_failed"
  290. }
  291. return ""
  292. }
  293. func fileExists(gitRepo *git.Repository, path string, branch string) (bool, error) {
  294. commit, err := gitRepo.GetBranchCommit(branch)
  295. if err != nil {
  296. return false, err
  297. }
  298. if _, err := commit.GetTreeEntryByPath(path); err != nil {
  299. return false, err
  300. }
  301. return true, nil
  302. }