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.

octopus.go 18 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. /*
  2. Copyright (c) [2023] [pcm]
  3. [pcm-coordinator] is licensed under Mulan PSL v2.
  4. You can use this software according to the terms and conditions of the Mulan PSL v2.
  5. You may obtain a copy of Mulan PSL v2 at:
  6. http://license.coscl.org.cn/MulanPSL2
  7. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
  8. EITHER EXPaRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
  9. MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
  10. See the Mulan PSL v2 for more details.
  11. */
  12. package storeLink
  13. import (
  14. "context"
  15. "errors"
  16. "gitlink.org.cn/JointCloud/pcm-coordinator/api/internal/scheduler/schedulers/option"
  17. "gitlink.org.cn/JointCloud/pcm-coordinator/api/internal/scheduler/service/collector"
  18. "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/constants"
  19. "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/utils"
  20. "gitlink.org.cn/JointCloud/pcm-octopus/octopus"
  21. "gitlink.org.cn/JointCloud/pcm-octopus/octopusclient"
  22. "math"
  23. "strconv"
  24. "strings"
  25. "time"
  26. )
  27. type OctopusLink struct {
  28. octopusRpc octopusclient.Octopus
  29. pageIndex int32
  30. pageSize int32
  31. platform string
  32. participantId int64
  33. }
  34. const (
  35. IMG_NAME_PREFIX = "oct_"
  36. IMG_VERSION_PREFIX = "version_"
  37. TASK_NAME_PREFIX = "trainJob"
  38. RESOURCE_POOL = "common-pool"
  39. HANWUJI = "hanwuji"
  40. SUIYUAN = "suiyuan"
  41. SAILINGSI = "sailingsi"
  42. MLU = "MLU"
  43. CAMBRICONMLU290 = 256
  44. GCU = "GCU"
  45. ENFLAME = "enflame"
  46. EnflameT20 = 128
  47. BASE_TOPS = 128
  48. CAMBRICON = "cambricon"
  49. TRAIN_CMD = "cd /code; python train.py"
  50. VERSION = "V1"
  51. )
  52. var (
  53. cardAliasMap = map[string]string{
  54. MLU: CAMBRICON,
  55. GCU: ENFLAME,
  56. }
  57. cardTopsMap = map[string]float64{
  58. MLU: CAMBRICONMLU290,
  59. GCU: EnflameT20,
  60. }
  61. )
  62. func NewOctopusLink(octopusRpc octopusclient.Octopus, name string, id int64) *OctopusLink {
  63. return &OctopusLink{octopusRpc: octopusRpc, platform: name, participantId: id, pageIndex: 1, pageSize: 100}
  64. }
  65. func (o *OctopusLink) UploadImage(ctx context.Context, path string) (interface{}, error) {
  66. // octopus创建镜像
  67. createReq := &octopus.CreateImageReq{
  68. Platform: o.platform,
  69. CreateImage: &octopus.CreateImage{
  70. SourceType: 1,
  71. ImageName: IMG_NAME_PREFIX + utils.RandomString(7),
  72. ImageVersion: IMG_VERSION_PREFIX + utils.RandomString(7),
  73. },
  74. }
  75. createResp, err := o.octopusRpc.CreateImage(ctx, createReq)
  76. if err != nil {
  77. return nil, err
  78. }
  79. // octopus上传镜像
  80. uploadReq := &octopus.UploadImageReq{
  81. Platform: o.platform,
  82. ImageId: createResp.Payload.ImageId,
  83. Params: &octopus.UploadImageParam{
  84. Domain: "",
  85. FileName: "",
  86. },
  87. }
  88. uploadResp, err := o.octopusRpc.UploadImage(ctx, uploadReq)
  89. if err != nil {
  90. return nil, err
  91. }
  92. // Todo 实际上传
  93. return uploadResp, nil
  94. }
  95. func (o *OctopusLink) DeleteImage(ctx context.Context, imageId string) (interface{}, error) {
  96. // octopus删除镜像
  97. req := &octopus.DeleteImageReq{
  98. Platform: o.platform,
  99. ImageId: imageId,
  100. }
  101. resp, err := o.octopusRpc.DeleteImage(ctx, req)
  102. if err != nil {
  103. return nil, err
  104. }
  105. return resp, nil
  106. }
  107. func (o *OctopusLink) QueryImageList(ctx context.Context) (interface{}, error) {
  108. // octopus获取镜像列表
  109. req := &octopus.GetUserImageListReq{
  110. Platform: o.platform,
  111. PageIndex: o.pageIndex,
  112. PageSize: o.pageSize,
  113. }
  114. resp, err := o.octopusRpc.GetUserImageList(ctx, req)
  115. if err != nil {
  116. return nil, err
  117. }
  118. return resp, nil
  119. }
  120. func (o *OctopusLink) SubmitTask(ctx context.Context, imageId string, cmd string, envs []string, params []string, resourceId string, datasetsId string, algorithmId string, aiType string) (interface{}, error) {
  121. // octopus提交任务
  122. // python参数
  123. var prms []*octopus.Parameters
  124. for _, param := range params {
  125. var p octopus.Parameters
  126. s := strings.Split(param, COMMA)
  127. p.Key = s[0]
  128. p.Value = s[1]
  129. prms = append(prms, &p)
  130. }
  131. //环境变量
  132. envMap := make(map[string]string)
  133. for _, env := range envs {
  134. s := strings.Split(env, COMMA)
  135. envMap[s[0]] = s[1]
  136. }
  137. req := &octopus.CreateTrainJobReq{
  138. Platform: o.platform,
  139. Params: &octopus.CreateTrainJobParam{
  140. ImageId: imageId,
  141. Name: TASK_NAME_PREFIX + UNDERSCORE + utils.RandomString(10),
  142. ResourcePool: RESOURCE_POOL,
  143. Config: []*octopus.Config{
  144. {
  145. Command: cmd,
  146. ResourceSpecId: resourceId,
  147. MinFailedTaskCount: 1,
  148. MinSucceededTaskCount: 1,
  149. TaskNumber: 1,
  150. Parameters: prms,
  151. Envs: envMap,
  152. },
  153. },
  154. DataSetId: datasetsId,
  155. DataSetVersion: VERSION,
  156. AlgorithmId: algorithmId,
  157. AlgorithmVersion: VERSION,
  158. },
  159. }
  160. resp, err := o.octopusRpc.CreateTrainJob(ctx, req)
  161. if err != nil {
  162. return nil, err
  163. }
  164. return resp, nil
  165. }
  166. func (o *OctopusLink) QueryTask(ctx context.Context, taskId string) (interface{}, error) {
  167. // octopus获取任务
  168. req := &octopus.GetTrainJobReq{
  169. Platform: o.platform,
  170. Id: taskId,
  171. }
  172. resp, err := o.octopusRpc.GetTrainJob(ctx, req)
  173. if err != nil {
  174. return nil, err
  175. }
  176. return resp, nil
  177. }
  178. func (o *OctopusLink) DeleteTask(ctx context.Context, taskId string) (interface{}, error) {
  179. // octopus删除任务
  180. req := &octopus.DeleteTrainJobReq{
  181. Platform: o.platform,
  182. JobIds: []string{taskId},
  183. }
  184. resp, err := o.octopusRpc.DeleteTrainJob(ctx, req)
  185. if err != nil {
  186. return nil, err
  187. }
  188. return resp, nil
  189. }
  190. func (o *OctopusLink) QuerySpecs(ctx context.Context) (interface{}, error) {
  191. // octopus查询资源规格
  192. req := &octopus.GetResourceSpecsReq{
  193. Platform: o.platform,
  194. ResourcePool: RESOURCE_POOL,
  195. }
  196. resp, err := o.octopusRpc.GetResourceSpecs(ctx, req)
  197. if err != nil {
  198. return nil, err
  199. }
  200. return resp, nil
  201. }
  202. func (o *OctopusLink) GetResourceStats(ctx context.Context) (*collector.ResourceStats, error) {
  203. req := &octopus.GetResourceSpecsReq{
  204. Platform: o.platform,
  205. ResourcePool: RESOURCE_POOL,
  206. }
  207. specResp, err := o.octopusRpc.GetResourceSpecs(ctx, req)
  208. if err != nil {
  209. return nil, err
  210. }
  211. if !specResp.Success {
  212. return nil, errors.New(specResp.Error.Message)
  213. }
  214. balanceReq := &octopus.GetUserBalanceReq{
  215. Platform: o.platform,
  216. }
  217. balanceResp, err := o.octopusRpc.GetUserBalance(ctx, balanceReq)
  218. if err != nil {
  219. return nil, err
  220. }
  221. if !balanceResp.Success {
  222. return nil, errors.New(balanceResp.Error.Message)
  223. }
  224. var cards []*collector.Card
  225. balance := float64(balanceResp.Payload.BillingUser.Amount)
  226. var cpuHours float64
  227. for _, spec := range specResp.TrainResourceSpecs {
  228. if spec.Price == 0 {
  229. ns := strings.Split(spec.Name, COMMA)
  230. if len(ns) == 2 {
  231. nss := strings.Split(ns[0], COLON)
  232. if nss[0] == CPU {
  233. cpuHours = -1
  234. }
  235. }
  236. }
  237. if spec.Price == 1 {
  238. ns := strings.Split(spec.Name, COMMA)
  239. cardSpecs := strings.Split(ns[0], STAR)
  240. cardTops, isMapContainsKey := cardTopsMap[cardSpecs[1]]
  241. if !isMapContainsKey {
  242. continue
  243. }
  244. card := &collector.Card{
  245. Platform: OCTOPUS,
  246. Type: CARD,
  247. Name: cardSpecs[1],
  248. TOpsAtFp16: cardTops,
  249. CardHours: balance / spec.Price,
  250. }
  251. cards = append(cards, card)
  252. }
  253. }
  254. resourceStats := &collector.ResourceStats{
  255. ClusterId: strconv.FormatInt(o.participantId, 10),
  256. Name: o.platform,
  257. Balance: balance,
  258. CardsAvail: cards,
  259. CpuCoreHours: cpuHours,
  260. }
  261. return resourceStats, nil
  262. }
  263. func (o *OctopusLink) GetDatasetsSpecs(ctx context.Context) ([]*collector.DatasetsSpecs, error) {
  264. req := &octopus.GetMyDatasetListReq{
  265. Platform: o.platform,
  266. PageIndex: o.pageIndex,
  267. PageSize: o.pageSize,
  268. }
  269. resp, err := o.octopusRpc.GetMyDatasetList(ctx, req)
  270. if err != nil {
  271. return nil, err
  272. }
  273. if !resp.Success {
  274. return nil, errors.New(resp.Error.Message)
  275. }
  276. specs := []*collector.DatasetsSpecs{}
  277. for _, dataset := range resp.Payload.Datasets {
  278. spec := &collector.DatasetsSpecs{Name: dataset.Name}
  279. specs = append(specs, spec)
  280. }
  281. return specs, nil
  282. }
  283. func (o *OctopusLink) GetAlgorithms(ctx context.Context) ([]*collector.Algorithm, error) {
  284. var algorithms []*collector.Algorithm
  285. req := &octopus.GetMyAlgorithmListReq{
  286. Platform: o.platform,
  287. PageIndex: o.pageIndex,
  288. PageSize: o.pageSize,
  289. }
  290. resp, err := o.octopusRpc.GetMyAlgorithmList(ctx, req)
  291. if err != nil {
  292. return nil, err
  293. }
  294. if !resp.Success {
  295. return nil, errors.New("failed to get algorithms")
  296. }
  297. for _, a := range resp.Payload.Algorithms {
  298. algorithm := &collector.Algorithm{Name: a.AlgorithmName, Platform: OCTOPUS, TaskType: strings.ToLower(a.FrameworkName)}
  299. algorithms = append(algorithms, algorithm)
  300. }
  301. return algorithms, nil
  302. }
  303. func (o *OctopusLink) DownloadAlgorithmCode(ctx context.Context, resourceType string, card string, taskType string, dataset string, algorithm string) (string, error) {
  304. return "", nil
  305. }
  306. func (o *OctopusLink) UploadAlgorithmCode(ctx context.Context, resourceType string, card string, taskType string, dataset string, algorithm string, code string) error {
  307. return nil
  308. }
  309. func (o *OctopusLink) GetTrainingTaskLog(ctx context.Context, taskId string, instanceNum string) (string, error) {
  310. instance, err := strconv.ParseInt(instanceNum, 10, 32)
  311. if err != nil {
  312. return "", err
  313. }
  314. req := &octopus.GetTrainJobLogReq{
  315. Platform: o.platform,
  316. TaskId: taskId,
  317. TaskNum: "task0",
  318. Num: int32(instance),
  319. }
  320. resp, err := o.octopusRpc.GetTrainJobLog(ctx, req)
  321. if err != nil {
  322. return "", err
  323. }
  324. return resp.Content, nil
  325. }
  326. func (o *OctopusLink) GetTrainingTask(ctx context.Context, taskId string) (*collector.Task, error) {
  327. resp, err := o.QueryTask(ctx, taskId)
  328. if err != nil {
  329. return nil, err
  330. }
  331. jobresp := (resp).(*octopus.GetTrainJobResp)
  332. if !jobresp.Success {
  333. return nil, errors.New(jobresp.Error.Message)
  334. }
  335. var task collector.Task
  336. task.Id = jobresp.Payload.TrainJob.Id
  337. task.Start = time.Unix(jobresp.Payload.TrainJob.StartedAt, 0).Format(constants.Layout)
  338. task.End = time.Unix(jobresp.Payload.TrainJob.CompletedAt, 0).Format(constants.Layout)
  339. switch jobresp.Payload.TrainJob.Status {
  340. case "succeeded":
  341. task.Status = constants.Completed
  342. case "failed":
  343. task.Status = constants.Failed
  344. case "running":
  345. task.Status = constants.Running
  346. case "stopped":
  347. task.Status = constants.Stopped
  348. default:
  349. task.Status = "undefined"
  350. }
  351. return &task, nil
  352. }
  353. func (o *OctopusLink) Execute(ctx context.Context, option *option.AiOption) (interface{}, error) {
  354. err := o.GenerateSubmitParams(ctx, option)
  355. if err != nil {
  356. return nil, err
  357. }
  358. task, err := o.SubmitTask(ctx, option.ImageId, option.Cmd, option.Envs, option.Params, option.ResourceId, option.DatasetsId, option.AlgorithmId, option.TaskType)
  359. if err != nil {
  360. return nil, err
  361. }
  362. return task, nil
  363. }
  364. func (o *OctopusLink) GenerateSubmitParams(ctx context.Context, option *option.AiOption) error {
  365. err := o.generateResourceId(ctx, option)
  366. if err != nil {
  367. return err
  368. }
  369. err = o.generateDatasetsId(ctx, option)
  370. if err != nil {
  371. return err
  372. }
  373. err = o.generateImageId(ctx, option)
  374. if err != nil {
  375. return err
  376. }
  377. err = o.generateAlgorithmId(ctx, option)
  378. if err != nil {
  379. return err
  380. }
  381. err = o.generateCmd(option)
  382. if err != nil {
  383. return err
  384. }
  385. err = o.generateEnv(option)
  386. if err != nil {
  387. return err
  388. }
  389. err = o.generateParams(option)
  390. if err != nil {
  391. return err
  392. }
  393. return nil
  394. }
  395. func (o *OctopusLink) generateResourceId(ctx context.Context, option *option.AiOption) error {
  396. if option.ResourceType == "" {
  397. return errors.New("ResourceType not set")
  398. }
  399. req := &octopus.GetResourceSpecsReq{
  400. Platform: o.platform,
  401. ResourcePool: RESOURCE_POOL,
  402. }
  403. specResp, err := o.octopusRpc.GetResourceSpecs(ctx, req)
  404. if err != nil {
  405. return err
  406. }
  407. if !specResp.Success {
  408. return errors.New(specResp.Error.Message)
  409. }
  410. if option.ResourceType == CPU {
  411. for _, spec := range specResp.TrainResourceSpecs {
  412. if spec.Price == 0 {
  413. option.ResourceId = spec.Id
  414. return nil
  415. }
  416. }
  417. }
  418. if option.ResourceType == CARD {
  419. err = setResourceIdByCard(option, specResp, GCU)
  420. if err != nil {
  421. return err
  422. }
  423. return nil
  424. }
  425. return errors.New("failed to get ResourceId")
  426. }
  427. func (o *OctopusLink) generateDatasetsId(ctx context.Context, option *option.AiOption) error {
  428. if option.DatasetsName == "" {
  429. return errors.New("DatasetsName not set")
  430. }
  431. req := &octopus.GetMyDatasetListReq{
  432. Platform: o.platform,
  433. PageIndex: o.pageIndex,
  434. PageSize: o.pageSize,
  435. }
  436. resp, err := o.octopusRpc.GetMyDatasetList(ctx, req)
  437. if err != nil {
  438. return err
  439. }
  440. if !resp.Success {
  441. return errors.New("failed to get DatasetsId")
  442. }
  443. for _, dataset := range resp.Payload.Datasets {
  444. if dataset.Name == option.DatasetsName {
  445. option.DatasetsId = dataset.Id
  446. return nil
  447. }
  448. }
  449. return errors.New("failed to get DatasetsId")
  450. }
  451. func (o *OctopusLink) generateImageId(ctx context.Context, option *option.AiOption) error {
  452. if option.TaskType == "" {
  453. return errors.New("TaskType not set")
  454. }
  455. req := &octopus.GetUserImageListReq{
  456. Platform: o.platform,
  457. PageIndex: o.pageIndex,
  458. PageSize: o.pageSize,
  459. }
  460. resp, err := o.octopusRpc.GetUserImageList(ctx, req)
  461. if err != nil {
  462. return err
  463. }
  464. if !resp.Success {
  465. return errors.New("failed to get imageId")
  466. }
  467. if option.ResourceType == CPU {
  468. for _, img := range resp.Payload.Images {
  469. if img.Image.ImageName == "test-image" {
  470. option.ImageId = img.Image.Id
  471. return nil
  472. }
  473. }
  474. }
  475. preImgReq := &octopus.GetPresetImageListReq{
  476. Platform: o.platform,
  477. PageIndex: o.pageIndex,
  478. PageSize: o.pageSize,
  479. }
  480. preImgResp, err := o.octopusRpc.GetPresetImageList(ctx, preImgReq)
  481. if err != nil {
  482. return err
  483. }
  484. if !preImgResp.Success {
  485. return errors.New("failed to get PresetImages")
  486. }
  487. if option.ResourceType == CARD {
  488. for _, image := range preImgResp.Payload.Images {
  489. if strings.Contains(image.ImageName, cardAliasMap[option.ComputeCard]) {
  490. option.ImageId = image.Id
  491. return nil
  492. }
  493. }
  494. }
  495. return errors.New("failed to get ImageId")
  496. }
  497. func (o *OctopusLink) generateAlgorithmId(ctx context.Context, option *option.AiOption) error {
  498. // temporarily set algorithm to cnn
  499. if option.AlgorithmName == "" {
  500. switch option.DatasetsName {
  501. case "cifar10":
  502. option.AlgorithmName = "cnn"
  503. case "mnist":
  504. option.AlgorithmName = "fcn"
  505. }
  506. }
  507. req := &octopus.GetMyAlgorithmListReq{
  508. Platform: o.platform,
  509. PageIndex: o.pageIndex,
  510. PageSize: o.pageSize,
  511. }
  512. resp, err := o.octopusRpc.GetMyAlgorithmList(ctx, req)
  513. if err != nil {
  514. return err
  515. }
  516. if !resp.Success {
  517. return errors.New("failed to get algorithmId")
  518. }
  519. for _, algorithm := range resp.Payload.Algorithms {
  520. if algorithm.FrameworkName == strings.Title(option.TaskType) {
  521. ns := strings.Split(algorithm.AlgorithmName, UNDERSCORE)
  522. if ns[0] != option.DatasetsName {
  523. continue
  524. }
  525. if ns[1] != option.AlgorithmName {
  526. continue
  527. }
  528. switch option.ResourceType {
  529. case CPU:
  530. if ns[2] != CPU {
  531. continue
  532. }
  533. case CARD:
  534. if ns[2] != strings.ToLower(option.ComputeCard) {
  535. continue
  536. }
  537. }
  538. option.AlgorithmId = algorithm.AlgorithmId
  539. return nil
  540. }
  541. }
  542. if option.AlgorithmId == "" {
  543. return errors.New("Algorithm does not exist")
  544. }
  545. return errors.New("failed to get AlgorithmId")
  546. }
  547. func (o *OctopusLink) generateCmd(option *option.AiOption) error {
  548. if option.Cmd == "" {
  549. switch option.ComputeCard {
  550. case GCU:
  551. option.Cmd = "cd /code; python3 train.py"
  552. default:
  553. option.Cmd = TRAIN_CMD
  554. }
  555. }
  556. return nil
  557. }
  558. func (o *OctopusLink) generateEnv(option *option.AiOption) error {
  559. return nil
  560. }
  561. func (o *OctopusLink) generateParams(option *option.AiOption) error {
  562. if len(option.Params) == 0 {
  563. epoch := "epoch" + COMMA + "1"
  564. option.Params = append(option.Params, epoch)
  565. }
  566. return nil
  567. }
  568. func setResourceIdByCard(option *option.AiOption, specs *octopus.GetResourceSpecsResp, computeCard string) error {
  569. if option.Tops == 0 {
  570. for _, spec := range specs.TrainResourceSpecs {
  571. if spec.Price == 1 {
  572. ns := strings.Split(spec.Name, COMMA)
  573. cardSpecs := strings.Split(ns[0], STAR)
  574. if cardSpecs[1] == computeCard {
  575. option.ResourceId = spec.Id
  576. option.ComputeCard = computeCard
  577. return nil
  578. }
  579. } else {
  580. continue
  581. }
  582. }
  583. } else {
  584. cardNum := math.Ceil(option.Tops / float64(BASE_TOPS))
  585. for _, spec := range specs.TrainResourceSpecs {
  586. if option.Tops < BASE_TOPS {
  587. if spec.Price == 1 {
  588. ns := strings.Split(spec.Name, COMMA)
  589. cardSpecs := strings.Split(ns[0], STAR)
  590. if cardSpecs[1] == computeCard {
  591. option.ResourceId = spec.Id
  592. option.ComputeCard = computeCard
  593. return nil
  594. }
  595. } else {
  596. continue
  597. }
  598. } else {
  599. ns := strings.Split(spec.Name, COMMA)
  600. if len(ns) != 4 {
  601. continue
  602. }
  603. cardSpecs := strings.Split(ns[0], STAR)
  604. if cardSpecs[1] != computeCard {
  605. continue
  606. }
  607. s, err := strconv.ParseFloat(cardSpecs[0], 64)
  608. if err != nil {
  609. return err
  610. }
  611. switch computeCard {
  612. case GCU:
  613. if cardNum == s { // 1, 4, 8
  614. option.ResourceId = spec.Id
  615. option.ComputeCard = computeCard
  616. return nil
  617. }
  618. if 1 < cardNum && cardNum <= 4 && s == 4 {
  619. option.ResourceId = spec.Id
  620. option.ComputeCard = computeCard
  621. return nil
  622. }
  623. if 4 < cardNum && s == 8 {
  624. option.ResourceId = spec.Id
  625. option.ComputeCard = computeCard
  626. return nil
  627. }
  628. case MLU: // 1, 2, 4
  629. if cardNum/2 == s {
  630. option.ResourceId = spec.Id
  631. option.ComputeCard = computeCard
  632. return nil
  633. }
  634. if 1 < cardNum/2 && cardNum/2 <= 2 && s == 2 {
  635. option.ResourceId = spec.Id
  636. option.ComputeCard = computeCard
  637. return nil
  638. }
  639. if 2 < cardNum/2 && s == 4 {
  640. option.ResourceId = spec.Id
  641. option.ComputeCard = computeCard
  642. return nil
  643. }
  644. }
  645. }
  646. }
  647. }
  648. return errors.New("set ResourceId error")
  649. }

PCM is positioned as Software stack over Cloud, aiming to build the standards and ecology of heterogeneous cloud collaboration for JCC in a non intrusive and autonomous peer-to-peer manner.