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 20 kB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. package cloudbrainTask
  2. import (
  3. "fmt"
  4. "net/http"
  5. "path"
  6. "strings"
  7. "code.gitea.io/gitea/modules/notebook"
  8. "code.gitea.io/gitea/modules/modelarts"
  9. "code.gitea.io/gitea/modules/modelarts_cd"
  10. "code.gitea.io/gitea/modules/git"
  11. "code.gitea.io/gitea/modules/cloudbrain"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/redis/redis_key"
  14. "code.gitea.io/gitea/modules/redis/redis_lock"
  15. "code.gitea.io/gitea/modules/storage"
  16. "code.gitea.io/gitea/services/cloudbrain/resource"
  17. "code.gitea.io/gitea/services/reward/point/account"
  18. "code.gitea.io/gitea/modules/setting"
  19. cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
  20. repo_service "code.gitea.io/gitea/services/repository"
  21. "code.gitea.io/gitea/models"
  22. "code.gitea.io/gitea/modules/context"
  23. api "code.gitea.io/gitea/modules/structs"
  24. "code.gitea.io/gitea/modules/util"
  25. )
  26. const NoteBookExtension = ".ipynb"
  27. const CPUType = 0
  28. const GPUType = 1
  29. const NPUType = 2
  30. func FileNotebookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption) {
  31. if ctx.Written() {
  32. return
  33. }
  34. if path.Ext(option.File) != NoteBookExtension {
  35. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_select_wrong")))
  36. return
  37. }
  38. if len(getBootFile(option.File, option.OwnerName, option.ProjectName)) > 255 {
  39. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_path_too_long")))
  40. return
  41. }
  42. if len(option.BranchName) > 255 {
  43. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_branch_name_too_long")))
  44. return
  45. }
  46. isNotebookFileExist, _ := isNoteBookFileExist(ctx, option)
  47. if !isNotebookFileExist {
  48. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
  49. return
  50. }
  51. sourceRepo, err := models.GetRepositoryByOwnerAndName(option.OwnerName, option.ProjectName)
  52. if err != nil {
  53. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
  54. return
  55. }
  56. permission, err := models.GetUserRepoPermission(sourceRepo, ctx.User)
  57. if err != nil {
  58. log.Error("Get permission failed", err)
  59. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_no_right")))
  60. return
  61. }
  62. if !permission.CanRead(models.UnitTypeCode) {
  63. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_no_right")))
  64. return
  65. }
  66. //create repo if not exist
  67. repo, _ := models.GetRepositoryByName(ctx.User.ID, setting.FileNoteBook.ProjectName)
  68. if repo == nil {
  69. repo, err = repo_service.CreateRepository(ctx.User, ctx.User, models.CreateRepoOptions{
  70. Name: setting.FileNoteBook.ProjectName,
  71. Alias: "",
  72. Description: "",
  73. IssueLabels: "",
  74. Gitignores: "",
  75. License: "",
  76. Readme: "Default",
  77. IsPrivate: false,
  78. AutoInit: true,
  79. DefaultBranch: "master",
  80. })
  81. if err != nil {
  82. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.failed_to_create_notebook_repo", setting.FileNoteBook.ProjectName)))
  83. return
  84. }
  85. } else {
  86. noteBook, _ := models.GetWaitOrRunFileNotebookByRepo(repo.ID, getCloudbrainType(option.Type))
  87. if noteBook != nil {
  88. if isRepoConfilcts(option, noteBook) {
  89. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_repo_conflict")))
  90. return
  91. }
  92. if isNotebookSpecMath(option, noteBook) {
  93. if !isRepoMatch(option, noteBook) {
  94. err = downloadCode(sourceRepo, getCodePath(noteBook.JobName, sourceRepo), option.BranchName)
  95. if err != nil {
  96. log.Error("download code failed", err)
  97. if !strings.Contains(err.Error(), "already exists and is not an empty directory") {
  98. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.load_code_failed")))
  99. return
  100. }
  101. }
  102. }
  103. if !isRepoFileMatch(option, noteBook) {
  104. if len(noteBook.BootFile)+len(getBootFile(option.File, option.OwnerName, option.ProjectName))+1 <= 255 {
  105. noteBook.BootFile += ";" + getBootFile(option.File, option.OwnerName, option.ProjectName)
  106. } else {
  107. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.notebook_path_too_long")))
  108. return
  109. }
  110. if len(noteBook.BranchName)+len(option.BranchName)+1 <= 255 {
  111. noteBook.BranchName += ";" + option.BranchName
  112. } else {
  113. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.notebook_branch_name_too_long")))
  114. return
  115. }
  116. if len(noteBook.Description)+len(getDescription(option))+1 <= 256 {
  117. noteBook.Description += ";" + getDescription(option)
  118. }
  119. err := models.UpdateJob(noteBook)
  120. if err != nil {
  121. log.Error("GenerateNotebook2 failed, %v", err, ctx.Data["MsgID"])
  122. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error()))
  123. return
  124. }
  125. }
  126. ctx.JSON(http.StatusOK, models.BaseMessageApi{
  127. Code: 0,
  128. Message: noteBook.JobID,
  129. })
  130. return
  131. }
  132. }
  133. }
  134. if option.Type <= GPUType {
  135. cloudBrainFileNoteBookCreate(ctx, option, repo, sourceRepo)
  136. } else {
  137. modelartsFileNoteBookCreate(ctx, option, repo, sourceRepo)
  138. }
  139. }
  140. func FileNotebookStatus(ctx *context.Context, option api.CreateFileNotebookJobOption) {
  141. if ctx.Written() {
  142. return
  143. }
  144. if path.Ext(option.File) != NoteBookExtension {
  145. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_select_wrong")))
  146. return
  147. }
  148. isNotebookFileExist, _ := isNoteBookFileExist(ctx, option)
  149. if !isNotebookFileExist {
  150. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
  151. return
  152. }
  153. task, err := models.GetCloudbrainByJobID(option.JobId)
  154. if err != nil {
  155. log.Error("job not found:"+option.JobId, err)
  156. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("Job id may not be right. can not find job."))
  157. return
  158. }
  159. if task.BootFile == "" || task.Status != string(models.ModelArtsRunning) {
  160. log.Warn("Boot file is empty or status is running. ")
  161. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("Boot file is empty or status is running."))
  162. return
  163. }
  164. if !isRepoFileMatch(option, task) {
  165. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("can not math repo file."))
  166. return
  167. }
  168. debugBaseUrl, token, err := getBaseUrlAndToken(task)
  169. if err != nil {
  170. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error()))
  171. return
  172. }
  173. if uploadNotebookFileIfCannotBroswer(debugBaseUrl, getBootFile(option.File, option.OwnerName, option.ProjectName), task, token) {
  174. ctx.JSON(http.StatusOK, models.BaseOKMessageApi)
  175. } else {
  176. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("upload failed."))
  177. }
  178. }
  179. func getBaseUrlAndToken(task *models.Cloudbrain) (string, string, error) {
  180. var debugBaseUrl string
  181. var token string
  182. if task.Type == models.TypeCloudBrainOne {
  183. debugBaseUrl = setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName + "/lab"
  184. } else {
  185. var result *models.GetNotebook2Result
  186. var err error
  187. if task.Type == models.TypeCloudBrainTwo {
  188. result, err = modelarts.GetNotebook2(task.JobID)
  189. } else if task.Type == models.TypeCDCenter {
  190. result, err = modelarts_cd.GetNotebook(task.JobID)
  191. }
  192. if err != nil || result == nil || result.Status != string(models.ModelArtsRunning) || result.Url == "" {
  193. log.Error("notebook job not found:"+task.JobID, err)
  194. return "", "", fmt.Errorf("can not get job or job is invalid.")
  195. }
  196. debugBaseUrl = result.Url
  197. token = result.Token
  198. }
  199. return debugBaseUrl, token, nil
  200. }
  201. func uploadNotebookFileIfCannotBroswer(debugBaseUrl string, bootFile string, task *models.Cloudbrain, token string) bool {
  202. c := &notebook.NotebookContent{
  203. Url: debugBaseUrl,
  204. Path: bootFile,
  205. PathType: "file",
  206. Token: token,
  207. }
  208. if c.IsNotebookFileCanBrowser() {
  209. return true
  210. } else {
  211. c.SetCookiesAndCsrf()
  212. c.UploadNoteBookFile(task)
  213. return c.IsNotebookFileCanBrowser()
  214. }
  215. }
  216. func isNotebookSpecMath(option api.CreateFileNotebookJobOption, book *models.Cloudbrain) bool {
  217. if option.Type == NPUType || option.Type == CPUType {
  218. return true
  219. }
  220. spec, err := models.GetCloudbrainSpecByID(book.ID)
  221. if err != nil {
  222. log.Warn("can not get spec ", err)
  223. return false
  224. }
  225. return spec.AccCardsNum > 0
  226. }
  227. func isRepoConfilcts(option api.CreateFileNotebookJobOption, book *models.Cloudbrain) bool {
  228. bootFiles := strings.Split(book.BootFile, ";")
  229. branches := strings.Split(book.BranchName, ";")
  230. for i, bootFile := range bootFiles {
  231. splits := strings.Split(bootFile, "/")
  232. if len(splits) >= 3 {
  233. if splits[0] == option.OwnerName && splits[1] == option.ProjectName && branches[i] != option.BranchName {
  234. return true
  235. }
  236. }
  237. }
  238. return false
  239. }
  240. func isRepoMatch(option api.CreateFileNotebookJobOption, book *models.Cloudbrain) bool {
  241. bootFiles := strings.Split(book.BootFile, ";")
  242. for _, bootFile := range bootFiles {
  243. splits := strings.Split(bootFile, "/")
  244. if len(splits) >= 3 {
  245. if splits[0] == option.OwnerName && splits[1] == option.ProjectName {
  246. return true
  247. }
  248. }
  249. }
  250. return false
  251. }
  252. func isRepoFileMatch(option api.CreateFileNotebookJobOption, book *models.Cloudbrain) bool {
  253. bootFiles := strings.Split(book.BootFile, ";")
  254. branches := strings.Split(book.BranchName, ";")
  255. for i, bootFile := range bootFiles {
  256. if branches[i] == option.BranchName && getBootFile(option.File, option.OwnerName, option.ProjectName) == bootFile {
  257. return true
  258. }
  259. }
  260. return false
  261. }
  262. func UploadNotebookFiles(task *models.Cloudbrain) {
  263. if task.Status == string(models.JobRunning) && task.BootFile != "" {
  264. debugBaseUrl, token, err := getBaseUrlAndToken(task)
  265. if err != nil {
  266. log.Error("can not get base url:", err)
  267. return
  268. }
  269. bootFiles := strings.Split(task.BootFile, ";")
  270. for _, bootFile := range bootFiles {
  271. uploadNotebookFileIfCannotBroswer(debugBaseUrl, bootFile, task, token)
  272. }
  273. }
  274. }
  275. func cloudBrainFileNoteBookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption, repo *models.Repository, sourceRepo *models.Repository) {
  276. displayJobName := cloudbrainService.GetDisplayJobName(ctx.User.Name)
  277. jobName := util.ConvertDisplayJobNameToJobName(displayJobName)
  278. jobType := string(models.JobTypeDebug)
  279. lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), jobType, displayJobName))
  280. defer lock.UnLock()
  281. isOk, err := lock.Lock(models.CloudbrainKeyDuration)
  282. if !isOk {
  283. log.Error("lock processed failed:%v", err, ctx.Data["MsgID"])
  284. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
  285. return
  286. }
  287. tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, jobType, displayJobName)
  288. if err == nil {
  289. if len(tasks) != 0 {
  290. log.Error("the job name did already exist", ctx.Data["MsgID"])
  291. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
  292. return
  293. }
  294. } else {
  295. if !models.IsErrJobNotExist(err) {
  296. log.Error("system error, %v", err, ctx.Data["MsgID"])
  297. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
  298. return
  299. }
  300. }
  301. count, err := GetNotFinalStatusTaskCount(ctx.User.ID, models.TypeCloudBrainOne, jobType)
  302. if err != nil {
  303. log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"])
  304. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
  305. return
  306. } else {
  307. if count >= 1 {
  308. log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
  309. ctx.JSON(http.StatusOK, models.BaseMessageApi{
  310. Code: 2,
  311. Message: ctx.Tr("repo.cloudbrain.morethanonejob"),
  312. })
  313. return
  314. }
  315. }
  316. err = downloadCode(sourceRepo, getCodePath(jobName, sourceRepo), option.BranchName)
  317. if err != nil {
  318. log.Error("download code failed", err)
  319. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.load_code_failed")))
  320. return
  321. }
  322. command := cloudbrain.GetCloudbrainDebugCommand()
  323. specId := setting.FileNoteBook.SpecIdGPU
  324. if option.Type == 0 {
  325. specId = setting.FileNoteBook.SpecIdCPU
  326. }
  327. spec, err := resource.GetAndCheckSpec(ctx.User.ID, specId, models.FindSpecsOptions{
  328. JobType: models.JobType(jobType),
  329. ComputeResource: models.GPU,
  330. Cluster: models.OpenICluster,
  331. AiCenterCode: models.AICenterOfCloudBrainOne})
  332. if err != nil || spec == nil {
  333. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.wrong_specification")))
  334. return
  335. }
  336. if !account.IsPointBalanceEnough(ctx.User.ID, spec.UnitPrice) {
  337. log.Error("point balance is not enough,userId=%d specId=%d", ctx.User.ID, spec.ID)
  338. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("points.insufficient_points_balance")))
  339. return
  340. }
  341. ctx.Repo = &context.Repository{
  342. Repository: repo,
  343. }
  344. req := cloudbrain.GenerateCloudBrainTaskReq{
  345. Ctx: ctx,
  346. DisplayJobName: displayJobName,
  347. JobName: jobName,
  348. Image: setting.FileNoteBook.ImageGPU,
  349. Command: command,
  350. Uuids: "",
  351. DatasetNames: "",
  352. DatasetInfos: nil,
  353. CodePath: storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
  354. ModelPath: storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/"),
  355. BenchmarkPath: storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"),
  356. Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
  357. BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"),
  358. JobType: jobType,
  359. Description: getDescription(option),
  360. BranchName: option.BranchName,
  361. BootFile: getBootFile(option.File, option.OwnerName, option.ProjectName),
  362. Params: "{\"parameter\":[]}",
  363. CommitID: "",
  364. BenchmarkTypeID: 0,
  365. BenchmarkChildTypeID: 0,
  366. ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"),
  367. Spec: spec,
  368. }
  369. jobId, err := cloudbrain.GenerateTask(req)
  370. if err != nil {
  371. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error()))
  372. return
  373. }
  374. ctx.JSON(http.StatusOK, models.BaseMessageApi{
  375. Code: 0,
  376. Message: jobId,
  377. })
  378. }
  379. func getCloudbrainType(optionType int) int {
  380. if optionType <= GPUType {
  381. return models.TypeCloudBrainOne
  382. }
  383. if setting.ModelartsCD.Enabled {
  384. return models.TypeCDCenter
  385. }
  386. return models.TypeCloudBrainTwo
  387. }
  388. func getCodePath(jobName string, repo *models.Repository) string {
  389. return setting.JobPath + jobName + cloudbrain.CodeMountPath + "/" + repo.OwnerName + "/" + repo.Name
  390. }
  391. func getDescription(option api.CreateFileNotebookJobOption) string {
  392. return option.OwnerName + "/" + option.ProjectName + "/" + option.File
  393. }
  394. func modelartsFileNoteBookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption, repo *models.Repository, sourceRepo *models.Repository) {
  395. displayJobName := cloudbrainService.GetDisplayJobName(ctx.User.Name)
  396. jobName := util.ConvertDisplayJobNameToJobName(displayJobName)
  397. lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), string(models.JobTypeDebug), displayJobName))
  398. isOk, err := lock.Lock(models.CloudbrainKeyDuration)
  399. if !isOk {
  400. log.Error("lock processed failed:%v", err, ctx.Data["MsgID"])
  401. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
  402. return
  403. }
  404. defer lock.UnLock()
  405. count, err := GetNotFinalStatusTaskCount(ctx.User.ID, models.TypeCloudBrainTwo, string(models.JobTypeDebug))
  406. if err != nil {
  407. log.Error("GetCloudbrainNotebookCountByUserID failed:%v", err, ctx.Data["MsgID"])
  408. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
  409. return
  410. } else {
  411. if count >= 1 {
  412. log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
  413. ctx.JSON(http.StatusOK, models.BaseMessageApi{
  414. Code: 2,
  415. Message: ctx.Tr("repo.cloudbrain.morethanonejob"),
  416. })
  417. return
  418. }
  419. }
  420. tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, string(models.JobTypeDebug), displayJobName)
  421. if err == nil {
  422. if len(tasks) != 0 {
  423. log.Error("the job name did already exist", ctx.Data["MsgID"])
  424. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
  425. return
  426. }
  427. } else {
  428. if !models.IsErrJobNotExist(err) {
  429. log.Error("system error, %v", err, ctx.Data["MsgID"])
  430. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
  431. return
  432. }
  433. }
  434. err = downloadCode(sourceRepo, getCodePath(jobName, sourceRepo), option.BranchName)
  435. if err != nil {
  436. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.load_code_failed")))
  437. return
  438. }
  439. var aiCenterCode = models.AICenterOfCloudBrainTwo
  440. var specId = setting.FileNoteBook.SpecIdNPU
  441. if setting.ModelartsCD.Enabled {
  442. aiCenterCode = models.AICenterOfChengdu
  443. specId = setting.FileNoteBook.SpecIdNPUCD
  444. }
  445. spec, err := resource.GetAndCheckSpec(ctx.User.ID, specId, models.FindSpecsOptions{
  446. JobType: models.JobTypeDebug,
  447. ComputeResource: models.NPU,
  448. Cluster: models.OpenICluster,
  449. AiCenterCode: aiCenterCode})
  450. if err != nil || spec == nil {
  451. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.wrong_specification")))
  452. return
  453. }
  454. if !account.IsPointBalanceEnough(ctx.User.ID, spec.UnitPrice) {
  455. log.Error("point balance is not enough,userId=%d specId=%d ", ctx.User.ID, spec.ID)
  456. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("points.insufficient_points_balance")))
  457. return
  458. }
  459. ctx.Repo = &context.Repository{
  460. Repository: repo,
  461. }
  462. var jobId string
  463. req := cloudbrain.GenerateModelArtsNotebookReq{
  464. DisplayJobName: displayJobName,
  465. JobName: jobName,
  466. Description: getDescription(option),
  467. ImageId: setting.FileNoteBook.ImageIdNPU,
  468. Spec: spec,
  469. BootFile: getBootFile(option.File, option.OwnerName, option.ProjectName),
  470. AutoStopDurationMs: modelarts.AutoStopDurationMs / 4,
  471. BranchName: option.BranchName,
  472. }
  473. if setting.ModelartsCD.Enabled {
  474. req.ImageId = setting.FileNoteBook.ImageIdNPUCD
  475. jobId, err = modelarts_cd.GenerateNotebook(ctx, req)
  476. } else {
  477. jobId, err = modelarts.GenerateNotebook2(ctx, req)
  478. }
  479. if err != nil {
  480. log.Error("GenerateNotebook2 failed, %v", err, ctx.Data["MsgID"])
  481. ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error()))
  482. return
  483. }
  484. ctx.JSON(http.StatusOK, models.BaseMessageApi{
  485. Code: 0,
  486. Message: jobId,
  487. })
  488. }
  489. func isNoteBookFileExist(ctx *context.Context, option api.CreateFileNotebookJobOption) (bool, error) {
  490. repoPathOfNoteBook := models.RepoPath(option.OwnerName, option.ProjectName)
  491. gitRepoOfNoteBook, err := git.OpenRepository(repoPathOfNoteBook)
  492. if err != nil {
  493. log.Error("RepoRef Invalid repo "+repoPathOfNoteBook, err.Error())
  494. return false, err
  495. }
  496. // We opened it, we should close it
  497. defer func() {
  498. // If it's been set to nil then assume someone else has closed it.
  499. if gitRepoOfNoteBook != nil {
  500. gitRepoOfNoteBook.Close()
  501. }
  502. }()
  503. fileExist, err := fileExists(gitRepoOfNoteBook, option.File, option.BranchName)
  504. if err != nil || !fileExist {
  505. log.Error("Get file error:", err, ctx.Data["MsgID"])
  506. return false, err
  507. }
  508. return true, nil
  509. }
  510. func getBootFile(filePath string, ownerName string, projectName string) string {
  511. return ownerName + "/" + projectName + "/" + filePath
  512. }
  513. func fileExists(gitRepo *git.Repository, path string, branch string) (bool, error) {
  514. commit, err := gitRepo.GetBranchCommit(branch)
  515. if err != nil {
  516. return false, err
  517. }
  518. if _, err := commit.GetTreeEntryByPath(path); err != nil {
  519. return false, err
  520. }
  521. return true, nil
  522. }