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

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

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.