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