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.

modelarts.go 17 kB

4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 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
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. package modelarts
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "path"
  6. "strconv"
  7. "code.gitea.io/gitea/models"
  8. "code.gitea.io/gitea/modules/context"
  9. "code.gitea.io/gitea/modules/log"
  10. "code.gitea.io/gitea/modules/setting"
  11. "code.gitea.io/gitea/modules/storage"
  12. )
  13. const (
  14. //notebook
  15. storageTypeOBS = "obs"
  16. autoStopDuration = 4 * 60 * 60
  17. autoStopDurationMs = 4 * 60 * 60 * 1000
  18. DataSetMountPath = "/home/ma-user/work"
  19. NotebookEnv = "Python3"
  20. NotebookType = "Ascend"
  21. FlavorInfo = "Ascend: 1*Ascend 910 CPU: 24 核 96GiB (modelarts.kat1.xlarge)"
  22. //train-job
  23. // ResourcePools = "{\"resource_pool\":[{\"id\":\"pool1328035d\", \"value\":\"专属资源池\"}]}"
  24. // Engines = "{\"engine\":[{\"id\":1, \"value\":\"Ascend-Powered-Engine\"}]}"
  25. // EngineVersions = "{\"version\":[{\"id\":118,\"value\":\"MindSpore-1.0.0-c75-python3.7-euleros2.8-aarch64\"}," +
  26. // "{\"id\":119,\"value\":\"MindSpore-1.1.1-c76-python3.7-euleros2.8-aarch64\"}," +
  27. // "{\"id\":120,\"value\":\"MindSpore-1.1.1-c76-tr5-python3.7-euleros2.8-aarch64\"}," +
  28. // "{\"id\":117,\"value\":\"TF-1.15-c75-python3.7-euleros2.8-aarch64\"}" +
  29. // "]}"
  30. // TrainJobFlavorInfo = "{\"flavor\":[{\"code\":\"modelarts.bm.910.arm.public.2\",\"value\":\"Ascend : 2 * Ascend 910 CPU:48 核 512GiB\"}," +
  31. // "{\"code\":\"modelarts.bm.910.arm.public.8\",\"value\":\"Ascend : 8 * Ascend 910 CPU:192 核 2048GiB\"}," +
  32. // "{\"code\":\"modelarts.bm.910.arm.public.4\",\"value\":\"Ascend : 4 * Ascend 910 CPU:96 核 1024GiB\"}," +
  33. // "{\"code\":\"modelarts.bm.910.arm.public.1\",\"value\":\"Ascend : 1 * Ascend 910 CPU:24 核 256GiB\"}" +
  34. // "]}"
  35. CodePath = "/code/"
  36. OutputPath = "/output/"
  37. ResultPath = "/result/"
  38. LogPath = "/log/"
  39. JobPath = "/job/"
  40. OrderDesc = "desc" //向下查询
  41. OrderAsc = "asc" //向上查询
  42. Lines = 500
  43. TrainUrl = "train_url"
  44. DataUrl = "data_url"
  45. ResultUrl = "result_url"
  46. CkptUrl = "ckpt_url"
  47. PerPage = 10
  48. IsLatestVersion = "1"
  49. NotLatestVersion = "0"
  50. DebugType = -1
  51. VersionCount = 1
  52. SortByCreateTime = "create_time"
  53. ConfigTypeCustom = "custom"
  54. TotalVersionCount = 1
  55. )
  56. var (
  57. poolInfos *models.PoolInfos
  58. FlavorInfos *models.FlavorInfos
  59. )
  60. type GenerateTrainJobReq struct {
  61. JobName string
  62. Uuid string
  63. Description string
  64. CodeObsPath string
  65. BootFile string
  66. BootFileUrl string
  67. DataUrl string
  68. TrainUrl string
  69. FlavorCode string
  70. LogUrl string
  71. PoolID string
  72. WorkServerNumber int
  73. EngineID int64
  74. Parameters []models.Parameter
  75. CommitID string
  76. IsLatestVersion string
  77. Params string
  78. BranchName string
  79. PreVersionId int64
  80. PreVersionName string
  81. FlavorName string
  82. VersionCount int
  83. EngineName string
  84. TotalVersionCount int
  85. }
  86. type GenerateTrainJobVersionReq struct {
  87. JobName string
  88. Uuid string
  89. Description string
  90. CodeObsPath string
  91. BootFile string
  92. BootFileUrl string
  93. DataUrl string
  94. TrainUrl string
  95. FlavorCode string
  96. LogUrl string
  97. PoolID string
  98. WorkServerNumber int
  99. EngineID int64
  100. Parameters []models.Parameter
  101. Params string
  102. PreVersionId int64
  103. CommitID string
  104. BranchName string
  105. FlavorName string
  106. EngineName string
  107. PreVersionName string
  108. TotalVersionCount int
  109. }
  110. type GenerateInferenceJobReq struct {
  111. JobName string
  112. Uuid string
  113. Description string
  114. CodeObsPath string
  115. BootFile string
  116. BootFileUrl string
  117. DataUrl string
  118. TrainUrl string
  119. FlavorCode string
  120. LogUrl string
  121. PoolID string
  122. WorkServerNumber int
  123. EngineID int64
  124. Parameters []models.Parameter
  125. CommitID string
  126. Params string
  127. BranchName string
  128. FlavorName string
  129. EngineName string
  130. LabelName string
  131. IsLatestVersion string
  132. VersionCount int
  133. TotalVersionCount int
  134. ModelName string
  135. ModelVersion string
  136. CkptName string
  137. ResultUrl string
  138. }
  139. type VersionInfo struct {
  140. Version []struct {
  141. ID int `json:"id"`
  142. Value string `json:"value"`
  143. } `json:"version"`
  144. }
  145. type Flavor struct {
  146. Info []struct {
  147. Code string `json:"code"`
  148. Value string `json:"value"`
  149. } `json:"flavor"`
  150. }
  151. type Engine struct {
  152. Info []struct {
  153. ID int `json:"id"`
  154. Value string `json:"value"`
  155. } `json:"engine"`
  156. }
  157. type ResourcePool struct {
  158. Info []struct {
  159. ID string `json:"id"`
  160. Value string `json:"value"`
  161. } `json:"resource_pool"`
  162. }
  163. // type Parameter struct {
  164. // Label string `json:"label"`
  165. // Value string `json:"value"`
  166. // }
  167. // type Parameters struct {
  168. // Parameter []Parameter `json:"parameter"`
  169. // }
  170. type Parameters struct {
  171. Parameter []struct {
  172. Label string `json:"label"`
  173. Value string `json:"value"`
  174. } `json:"parameter"`
  175. }
  176. func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor string) error {
  177. var dataActualPath string
  178. if uuid != "" {
  179. dataActualPath = setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + "/"
  180. } else {
  181. userPath := setting.UserBasePath + ctx.User.Name + "/"
  182. isExist, err := storage.ObsHasObject(userPath)
  183. if err != nil {
  184. log.Error("ObsHasObject failed:%v", err.Error(), ctx.Data["MsgID"])
  185. return err
  186. }
  187. if !isExist {
  188. if err = storage.ObsCreateObject(userPath); err != nil {
  189. log.Error("ObsCreateObject failed:%v", err.Error(), ctx.Data["MsgID"])
  190. return err
  191. }
  192. }
  193. dataActualPath = setting.Bucket + "/" + userPath
  194. }
  195. if poolInfos == nil {
  196. json.Unmarshal([]byte(setting.PoolInfos), &poolInfos)
  197. }
  198. jobResult, err := CreateJob(models.CreateNotebookParams{
  199. JobName: jobName,
  200. Description: description,
  201. ProfileID: setting.ProfileID,
  202. Flavor: flavor,
  203. Pool: models.Pool{
  204. ID: poolInfos.PoolInfo[0].PoolId,
  205. Name: poolInfos.PoolInfo[0].PoolName,
  206. Type: poolInfos.PoolInfo[0].PoolType,
  207. },
  208. Spec: models.Spec{
  209. Storage: models.Storage{
  210. Type: storageTypeOBS,
  211. Location: models.Location{
  212. Path: dataActualPath,
  213. },
  214. },
  215. AutoStop: models.AutoStop{
  216. Enable: true,
  217. Duration: autoStopDuration,
  218. },
  219. },
  220. })
  221. if err != nil {
  222. log.Error("CreateJob failed: %v", err.Error())
  223. return err
  224. }
  225. err = models.CreateCloudbrain(&models.Cloudbrain{
  226. Status: string(models.JobWaiting),
  227. UserID: ctx.User.ID,
  228. RepoID: ctx.Repo.Repository.ID,
  229. JobID: jobResult.ID,
  230. JobName: jobName,
  231. JobType: string(models.JobTypeDebug),
  232. Type: models.TypeCloudBrainTwo,
  233. Uuid: uuid,
  234. ComputeResource: models.NPUResource,
  235. })
  236. if err != nil {
  237. return err
  238. }
  239. return nil
  240. }
  241. func GenerateNotebook2(ctx *context.Context, jobName, uuid, description, flavor string) error {
  242. if poolInfos == nil {
  243. json.Unmarshal([]byte(setting.PoolInfos), &poolInfos)
  244. }
  245. jobResult, err := createNotebook2(models.CreateNotebook2Params{
  246. JobName: jobName,
  247. Description: description,
  248. Flavor: flavor,
  249. Duration: autoStopDurationMs,
  250. ImageID: "59a6e9f5-93c0-44dd-85b0-82f390c5d53a",
  251. PoolID: poolInfos.PoolInfo[0].PoolId,
  252. Feature: models.NotebookFeature,
  253. Volume: models.VolumeReq{
  254. Capacity: 100,
  255. Category: models.EVSCategory,
  256. Ownership: models.ManagedOwnership,
  257. },
  258. WorkspaceID: "0",
  259. })
  260. if err != nil {
  261. log.Error("createNotebook2 failed: %v", err.Error())
  262. return err
  263. }
  264. err = models.CreateCloudbrain(&models.Cloudbrain{
  265. Status: string(models.JobWaiting),
  266. UserID: ctx.User.ID,
  267. RepoID: ctx.Repo.Repository.ID,
  268. JobID: jobResult.ID,
  269. JobName: jobName,
  270. JobType: string(models.JobTypeDebug),
  271. Type: models.TypeCloudBrainTwo,
  272. Uuid: uuid,
  273. ComputeResource: models.NPUResource,
  274. })
  275. if err != nil {
  276. return err
  277. }
  278. return nil
  279. }
  280. func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) {
  281. jobResult, err := createTrainJob(models.CreateTrainJobParams{
  282. JobName: req.JobName,
  283. Description: req.Description,
  284. Config: models.Config{
  285. WorkServerNum: req.WorkServerNumber,
  286. AppUrl: req.CodeObsPath,
  287. BootFileUrl: req.BootFileUrl,
  288. DataUrl: req.DataUrl,
  289. EngineID: req.EngineID,
  290. TrainUrl: req.TrainUrl,
  291. LogUrl: req.LogUrl,
  292. PoolID: req.PoolID,
  293. CreateVersion: true,
  294. Flavor: models.Flavor{
  295. Code: req.FlavorCode,
  296. },
  297. Parameter: req.Parameters,
  298. },
  299. })
  300. if err != nil {
  301. log.Error("CreateJob failed: %v", err.Error())
  302. return err
  303. }
  304. attach, err := models.GetAttachmentByUUID(req.Uuid)
  305. if err != nil {
  306. log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error())
  307. return err
  308. }
  309. err = models.CreateCloudbrain(&models.Cloudbrain{
  310. Status: TransTrainJobStatus(jobResult.Status),
  311. UserID: ctx.User.ID,
  312. RepoID: ctx.Repo.Repository.ID,
  313. JobID: strconv.FormatInt(jobResult.JobID, 10),
  314. JobName: req.JobName,
  315. JobType: string(models.JobTypeTrain),
  316. Type: models.TypeCloudBrainTwo,
  317. VersionID: jobResult.VersionID,
  318. VersionName: jobResult.VersionName,
  319. Uuid: req.Uuid,
  320. DatasetName: attach.Name,
  321. CommitID: req.CommitID,
  322. IsLatestVersion: req.IsLatestVersion,
  323. ComputeResource: models.NPUResource,
  324. EngineID: req.EngineID,
  325. TrainUrl: req.TrainUrl,
  326. BranchName: req.BranchName,
  327. Parameters: req.Params,
  328. BootFile: req.BootFile,
  329. DataUrl: req.DataUrl,
  330. LogUrl: req.LogUrl,
  331. FlavorCode: req.FlavorCode,
  332. Description: req.Description,
  333. WorkServerNumber: req.WorkServerNumber,
  334. FlavorName: req.FlavorName,
  335. EngineName: req.EngineName,
  336. VersionCount: req.VersionCount,
  337. TotalVersionCount: req.TotalVersionCount,
  338. })
  339. if err != nil {
  340. log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, err.Error())
  341. return err
  342. }
  343. return nil
  344. }
  345. func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, jobId string) (err error) {
  346. jobResult, err := createTrainJobVersion(models.CreateTrainJobVersionParams{
  347. Description: req.Description,
  348. Config: models.TrainJobVersionConfig{
  349. WorkServerNum: req.WorkServerNumber,
  350. AppUrl: req.CodeObsPath,
  351. BootFileUrl: req.BootFileUrl,
  352. DataUrl: req.DataUrl,
  353. EngineID: req.EngineID,
  354. TrainUrl: req.TrainUrl,
  355. LogUrl: req.LogUrl,
  356. PoolID: req.PoolID,
  357. Flavor: models.Flavor{
  358. Code: req.FlavorCode,
  359. },
  360. Parameter: req.Parameters,
  361. PreVersionId: req.PreVersionId,
  362. },
  363. }, jobId)
  364. if err != nil {
  365. log.Error("CreateJob failed: %v", err.Error())
  366. return err
  367. }
  368. attach, err := models.GetAttachmentByUUID(req.Uuid)
  369. if err != nil {
  370. log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error())
  371. return err
  372. }
  373. var jobTypes []string
  374. jobTypes = append(jobTypes, string(models.JobTypeTrain))
  375. repo := ctx.Repo.Repository
  376. VersionTaskList, VersionListCount, err := models.CloudbrainsVersionList(&models.CloudbrainsOptions{
  377. RepoID: repo.ID,
  378. Type: models.TypeCloudBrainTwo,
  379. JobTypes: jobTypes,
  380. JobID: strconv.FormatInt(jobResult.JobID, 10),
  381. })
  382. if err != nil {
  383. ctx.ServerError("Cloudbrain", err)
  384. return err
  385. }
  386. //将当前版本的isLatestVersion设置为"1"和任务数量更新,任务数量包括当前版本数VersionCount和历史创建的总版本数TotalVersionCount
  387. err = models.CreateCloudbrain(&models.Cloudbrain{
  388. Status: TransTrainJobStatus(jobResult.Status),
  389. UserID: ctx.User.ID,
  390. RepoID: ctx.Repo.Repository.ID,
  391. JobID: strconv.FormatInt(jobResult.JobID, 10),
  392. JobName: req.JobName,
  393. JobType: string(models.JobTypeTrain),
  394. Type: models.TypeCloudBrainTwo,
  395. VersionID: jobResult.VersionID,
  396. VersionName: jobResult.VersionName,
  397. Uuid: req.Uuid,
  398. DatasetName: attach.Name,
  399. CommitID: req.CommitID,
  400. IsLatestVersion: req.IsLatestVersion,
  401. PreVersionName: req.PreVersionName,
  402. ComputeResource: models.NPUResource,
  403. EngineID: req.EngineID,
  404. TrainUrl: req.TrainUrl,
  405. BranchName: req.BranchName,
  406. Parameters: req.Params,
  407. BootFile: req.BootFile,
  408. DataUrl: req.DataUrl,
  409. LogUrl: req.LogUrl,
  410. PreVersionId: req.PreVersionId,
  411. FlavorCode: req.FlavorCode,
  412. Description: req.Description,
  413. WorkServerNumber: req.WorkServerNumber,
  414. FlavorName: req.FlavorName,
  415. EngineName: req.EngineName,
  416. TotalVersionCount: VersionTaskList[0].TotalVersionCount + 1,
  417. VersionCount: VersionListCount + 1,
  418. })
  419. if err != nil {
  420. log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, err.Error())
  421. return err
  422. }
  423. //将训练任务的上一版本的isLatestVersion设置为"0"
  424. err = models.SetVersionCountAndLatestVersion(strconv.FormatInt(jobResult.JobID, 10), VersionTaskList[0].VersionName, VersionCount, NotLatestVersion, TotalVersionCount)
  425. if err != nil {
  426. ctx.ServerError("Update IsLatestVersion failed", err)
  427. return err
  428. }
  429. return err
  430. }
  431. func TransTrainJobStatus(status int) string {
  432. switch status {
  433. case 0:
  434. return "UNKNOWN"
  435. case 1:
  436. return "INIT"
  437. case 2:
  438. return "IMAGE_CREATING"
  439. case 3:
  440. return "IMAGE_FAILED"
  441. case 4:
  442. return "SUBMIT_TRYING"
  443. case 5:
  444. return "SUBMIT_FAILED"
  445. case 6:
  446. return "DELETE_FAILED"
  447. case 7:
  448. return "WAITING"
  449. case 8:
  450. return "RUNNING"
  451. case 9:
  452. return "KILLING"
  453. case 10:
  454. return "COMPLETED"
  455. case 11:
  456. return "FAILED"
  457. case 12:
  458. return "KILLED"
  459. case 13:
  460. return "CANCELED"
  461. case 14:
  462. return "LOST"
  463. case 15:
  464. return "SCALING"
  465. case 16:
  466. return "SUBMIT_MODEL_FAILED"
  467. case 17:
  468. return "DEPLOY_SERVICE_FAILED"
  469. case 18:
  470. return "CHECK_INIT"
  471. case 19:
  472. return "CHECK_RUNNING"
  473. case 20:
  474. return "CHECK_RUNNING_COMPLETED"
  475. case 21:
  476. return "CHECK_FAILED"
  477. default:
  478. return strconv.Itoa(status)
  479. }
  480. }
  481. func GetOutputPathByCount(TotalVersionCount int) (VersionOutputPath string) {
  482. talVersionCountToString := fmt.Sprintf("%04d", TotalVersionCount)
  483. VersionOutputPath = "V" + talVersionCountToString
  484. return VersionOutputPath
  485. }
  486. func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (err error) {
  487. jobResult, err := createInferenceJob(models.CreateInferenceJobParams{
  488. JobName: req.JobName,
  489. Description: req.Description,
  490. InfConfig: models.InfConfig{
  491. WorkServerNum: req.WorkServerNumber,
  492. AppUrl: req.CodeObsPath,
  493. BootFileUrl: req.BootFileUrl,
  494. DataUrl: req.DataUrl,
  495. EngineID: req.EngineID,
  496. // TrainUrl: req.TrainUrl,
  497. LogUrl: req.LogUrl,
  498. PoolID: req.PoolID,
  499. CreateVersion: true,
  500. Flavor: models.Flavor{
  501. Code: req.FlavorCode,
  502. },
  503. Parameter: req.Parameters,
  504. },
  505. })
  506. if err != nil {
  507. log.Error("CreateJob failed: %v", err.Error())
  508. return err
  509. }
  510. attach, err := models.GetAttachmentByUUID(req.Uuid)
  511. if err != nil {
  512. log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error())
  513. return err
  514. }
  515. err = models.CreateCloudbrain(&models.Cloudbrain{
  516. Status: TransTrainJobStatus(jobResult.Status),
  517. UserID: ctx.User.ID,
  518. RepoID: ctx.Repo.Repository.ID,
  519. JobID: strconv.FormatInt(jobResult.JobID, 10),
  520. JobName: req.JobName,
  521. JobType: string(models.JobTypeInference),
  522. Type: models.TypeCloudBrainTwo,
  523. VersionID: jobResult.VersionID,
  524. VersionName: jobResult.VersionName,
  525. Uuid: req.Uuid,
  526. DatasetName: attach.Name,
  527. CommitID: req.CommitID,
  528. EngineID: req.EngineID,
  529. TrainUrl: req.TrainUrl,
  530. BranchName: req.BranchName,
  531. Parameters: req.Params,
  532. BootFile: req.BootFile,
  533. DataUrl: req.DataUrl,
  534. LogUrl: req.LogUrl,
  535. FlavorCode: req.FlavorCode,
  536. Description: req.Description,
  537. WorkServerNumber: req.WorkServerNumber,
  538. FlavorName: req.FlavorName,
  539. EngineName: req.EngineName,
  540. LabelName: req.LabelName,
  541. IsLatestVersion: req.IsLatestVersion,
  542. VersionCount: req.VersionCount,
  543. TotalVersionCount: req.TotalVersionCount,
  544. ModelName: req.ModelName,
  545. ModelVersion: req.ModelVersion,
  546. CkptName: req.CkptName,
  547. ResultUrl: req.ResultUrl,
  548. })
  549. if err != nil {
  550. log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, err.Error())
  551. return err
  552. }
  553. return nil
  554. }