modelarts.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package hcso
  15. import (
  16. "strings"
  17. "time"
  18. "yunion.io/x/jsonutils"
  19. "yunion.io/x/pkg/errors"
  20. "yunion.io/x/pkg/util/billing"
  21. "yunion.io/x/pkg/utils"
  22. billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
  23. "yunion.io/x/cloudmux/pkg/apis/compute"
  24. "yunion.io/x/cloudmux/pkg/cloudprovider"
  25. "yunion.io/x/cloudmux/pkg/multicloud"
  26. )
  27. type SModelartsPool struct {
  28. region *SRegion
  29. multicloud.SResourceBase
  30. multicloud.SBillingBase
  31. Metadata SModelartsPoolMetadata `json:"metadata"`
  32. Spec SModelartsPoolSpec `json:"spec"`
  33. Status SModelartsPoolStatus `json:"status"`
  34. InstanceType string
  35. WorkType string
  36. }
  37. type SModelartsPoolMetadata struct {
  38. Name string `json:"name"`
  39. CreationTimestamp string `json:"creationTimestamp"`
  40. Labels SModelartsPoolMeatadataLabel
  41. Annotations SModelartsPoolMetadataAnnotations `json:"annotations"`
  42. }
  43. type SModelartsPoolMeatadataLabel struct {
  44. WorkspaceId string `json:"os.modelarts/workspace.id"`
  45. Name string `json:"os.modelarts/name"`
  46. ResourceId string `json:"os.modelarts/resource.id"`
  47. }
  48. type SModelartsPoolMetadataAnnotations struct {
  49. Describe string `json:"os.modelarts/description"`
  50. BillingType string `json:"os.modelarts/billing.mode"`
  51. BillingCycle string `json:"os.modelarts/period.num"`
  52. BillingPeriodType string `json:"os.modelarts/period.type"`
  53. BillingMod string `json:"os.modelarts/charging.mode"`
  54. BillingRenew string `json:"os.modelarts/auto.renew"`
  55. OrderId string `json:"os.modelarts/order.id"`
  56. }
  57. type SModelartsPoolSpec struct {
  58. Type string `json:"type"`
  59. Scope []string `json:"scope"`
  60. Resource []SModelartsPoolResource `json:"resources"`
  61. }
  62. type SModelartsPoolResource struct {
  63. Flavor string `json:"flavor"`
  64. Count int `json:"count"`
  65. cloudprovider.Azs
  66. }
  67. type SNodeStatus struct {
  68. Creating []SNodeFlavor `json:"creating"`
  69. Available []SNodeFlavor `json:"available"`
  70. Abnormal []SNodeFlavor `json:"abnormal"`
  71. Deleting []SNodeFlavor `json:"deleting"`
  72. }
  73. type SNodeFlavor struct {
  74. Flavor string `json:"flavor"`
  75. Count int `json:"count"`
  76. }
  77. type SModelartsPoolStatus struct {
  78. Phase string `json:"phase"`
  79. Message string `json:"message"`
  80. Resource SNodeStatus `json:"resources"`
  81. }
  82. type SModelartsPoolNetwork struct {
  83. Metadata SModelartsPoolNetworkMetadata `json:"metadata"`
  84. Spec SModelartsNetworkSpce `json:"spec"`
  85. Status SModelartsNetworkStatus `json:"status"`
  86. }
  87. type SModelartsNetworkSpce struct {
  88. Cidr string `json:"cidr"`
  89. }
  90. type SModelartsNetworkStatus struct {
  91. Phase string `json:"phase"`
  92. }
  93. type SModelartsPoolNetworkMetadata struct {
  94. Name string `json:"name"`
  95. CreationTimestamp string `json:"creationTimestamp"`
  96. }
  97. func (self *SRegion) GetIModelartsPools() ([]cloudprovider.ICloudModelartsPool, error) {
  98. pools := make([]SModelartsPool, 0)
  99. resObj, err := self.client.modelartsPoolList(nil)
  100. if err != nil {
  101. return nil, errors.Wrap(err, "region.GetPools")
  102. }
  103. err = resObj.Unmarshal(&pools, "items")
  104. if err != nil {
  105. return nil, errors.Wrap(err, "resObj unmarshal")
  106. }
  107. res := make([]cloudprovider.ICloudModelartsPool, len(pools))
  108. for i := 0; i < len(pools); i++ {
  109. pools[i].region = self
  110. res[i] = &pools[i]
  111. }
  112. return res, nil
  113. }
  114. func (self *SRegion) CreateIModelartsPool(args *cloudprovider.ModelartsPoolCreateOption, callback func(id string)) (cloudprovider.ICloudModelartsPool, error) {
  115. if len(args.Cidr) == 0 {
  116. args.Cidr = "192.168.128.0/17"
  117. }
  118. netObj, err := self.client.modelartsPoolNetworkList(nil)
  119. if err != nil {
  120. return nil, errors.Wrap(err, "SHuaweiClient.GetPools")
  121. }
  122. netRes := make([]SModelartsPoolNetwork, 0)
  123. netObj.Unmarshal(&netRes, "items")
  124. netId := ""
  125. for _, net := range netRes {
  126. if net.Spec.Cidr == args.Cidr {
  127. netId = net.Metadata.Name
  128. }
  129. }
  130. if len(netId) == 0 {
  131. createNetObj, err := self.client.CreatePoolNetworks(args.Cidr)
  132. if err != nil {
  133. return nil, errors.Wrap(err, "SHuaweiClient.CreatePoolNetworks")
  134. }
  135. netId, _ = createNetObj.GetString("metadata", "name")
  136. for i := 0; i < 10; i++ {
  137. netDetailObj, err := self.client.modelartsPoolNetworkDetail(netId)
  138. if err != nil {
  139. return nil, errors.Wrap(err, "SHuaweiClient.NetworkDetail")
  140. }
  141. netStatus, _ := netDetailObj.GetString("status", "phase")
  142. if netStatus == "Active" {
  143. break
  144. } else {
  145. time.Sleep(10 * time.Second)
  146. }
  147. }
  148. }
  149. scopeArr := strings.Split(args.WorkType, ",")
  150. params := map[string]interface{}{
  151. "apiVersion": "v2",
  152. "kind": "Pool",
  153. "metadata": map[string]interface{}{
  154. "labels": map[string]interface{}{
  155. "os.modelarts/name": args.Name,
  156. "os.modelarts/workspace.id": "0",
  157. },
  158. },
  159. "spec": map[string]interface{}{
  160. "type": "Dedicate",
  161. "scope": scopeArr,
  162. "network": map[string]interface{}{
  163. "name": netId,
  164. },
  165. "resources": []map[string]interface{}{
  166. {
  167. "flavor": args.InstanceType,
  168. "count": args.NodeCount,
  169. },
  170. },
  171. },
  172. }
  173. obj, err := self.client.modelartsPoolCreate(params)
  174. if err != nil {
  175. return nil, errors.Wrap(err, "SHuaweiClient.CreatePools")
  176. }
  177. pool := &SModelartsPool{
  178. region: self,
  179. }
  180. obj.Unmarshal(pool)
  181. if callback != nil {
  182. callback(pool.GetId())
  183. }
  184. // 对于新建后可能会存在一段时间list查不到
  185. time.Sleep(2 * time.Minute)
  186. return self.waitCreate(pool)
  187. }
  188. func (region *SRegion) waitCreate(pool *SModelartsPool) (cloudprovider.ICloudModelartsPool, error) {
  189. startTime := time.Now()
  190. for time.Since(startTime) < 2*time.Hour {
  191. pool.RefreshForCreate()
  192. if utils.IsInStringArray(pool.GetStatus(), []string{compute.MODELARTS_POOL_STATUS_RUNNING, compute.MODELARTS_POOL_STATUS_CREATE_FAILED}) {
  193. return pool, nil
  194. }
  195. time.Sleep(15 * time.Second)
  196. }
  197. return nil, errors.ErrTimeout
  198. }
  199. func (self *SRegion) GetIModelartsPoolsWithStatus(status string) ([]jsonutils.JSONObject, error) {
  200. resObj, err := self.client.modelartsPoolListWithStatus("pools", status, nil)
  201. if err != nil {
  202. return nil, err
  203. }
  204. return resObj.GetArray("items")
  205. }
  206. func (self *SRegion) DeletePool(poolName string) (jsonutils.JSONObject, error) {
  207. return self.client.modelartsPoolDelete(poolName, nil)
  208. }
  209. func (self *SRegion) GetIModelartsPoolById(poolId string) (cloudprovider.ICloudModelartsPool, error) {
  210. obj, err := self.client.modelartsPoolById(poolId)
  211. if err != nil {
  212. if strings.Contains(err.Error(), "not found") {
  213. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "")
  214. }
  215. return nil, errors.Wrap(err, "region.modelartsPoolByName")
  216. }
  217. pool := &SModelartsPool{
  218. region: self,
  219. }
  220. obj.Unmarshal(pool)
  221. return pool, nil
  222. }
  223. func (self *SRegion) MonitorPool(poolId string) (*SModelartsMetrics, error) {
  224. resObj, err := self.client.modelartsPoolMonitor(poolId, nil)
  225. if err != nil {
  226. return nil, errors.Wrapf(err, "send request error")
  227. }
  228. metrics := SModelartsMetrics{}
  229. err = resObj.Unmarshal(&metrics)
  230. if err != nil {
  231. return nil, errors.Wrapf(err, "unmarsh error")
  232. }
  233. return &metrics, nil
  234. }
  235. type SModelartsMetrics struct {
  236. Metrics []SModelartsMetric `json:"metrics"`
  237. }
  238. type SModelartsMetric struct {
  239. Metric SModelartsMetricInfo `json:"metric"`
  240. Datapoints []SModelartsDataPoints `json:"dataPoints"`
  241. }
  242. type SModelartsMetricInfo struct {
  243. Dimensions []SModelartsDimensions `json:"dimensions"`
  244. MetricName string
  245. Namespace string
  246. }
  247. type SModelartsDimensions struct {
  248. Name string
  249. Value string
  250. }
  251. type SModelartsDataPoints struct {
  252. Timestamp int64
  253. Unit string
  254. Statistics []ModelartsStatistics
  255. }
  256. type ModelartsStatistics struct {
  257. Statistic string
  258. Value float64
  259. }
  260. func (self *SHuaweiClient) CreatePoolNetworks(cidr string) (jsonutils.JSONObject, error) {
  261. params := map[string]interface{}{
  262. "apiVersion": "v1",
  263. "kind": "Network",
  264. "metadata": map[string]interface{}{
  265. "labels": map[string]interface{}{
  266. "os.modelarts/name": "test",
  267. "os.modelarts/workspace.id": "0",
  268. },
  269. },
  270. "spec": map[string]interface{}{
  271. "cidr": cidr,
  272. },
  273. }
  274. return self.modelartsPoolNetworkCreate(params)
  275. }
  276. func (self *SModelartsPool) GetCreatedAt() time.Time {
  277. ret, _ := time.Parse("2006-01-02T15:04:05CST", self.Metadata.CreationTimestamp)
  278. if !ret.IsZero() {
  279. ret = ret.Add(time.Hour * 8)
  280. }
  281. return ret
  282. }
  283. func (self *SModelartsPool) GetGlobalId() string {
  284. return self.Metadata.Name
  285. }
  286. func (self *SModelartsPool) GetId() string {
  287. return self.Metadata.Name
  288. }
  289. func (self *SModelartsPool) GetName() string {
  290. return self.Metadata.Labels.Name
  291. }
  292. func (self *SModelartsPool) GetStatus() string {
  293. res := strings.ToLower(self.Status.Phase)
  294. availableCount := 0
  295. for _, node := range self.Status.Resource.Available {
  296. availableCount += node.Count
  297. }
  298. switch {
  299. case res == compute.MODELARTS_POOL_STATUS_RUNNING && availableCount == self.GetNodeCount():
  300. res = compute.MODELARTS_POOL_STATUS_RUNNING
  301. case res == compute.MODELARTS_POOL_STATUS_DELETING:
  302. res = compute.MODELARTS_POOL_STATUS_DELETING
  303. case res == compute.MODELARTS_POOL_STATUS_ERROR:
  304. res = compute.MODELARTS_POOL_STATUS_ERROR
  305. case (res == compute.MODELARTS_POOL_STATUS_RUNNING && len(self.Status.Resource.Creating) != 0) || res == compute.MODELARTS_POOL_STATUS_CREATING:
  306. res = compute.MODELARTS_POOL_STATUS_CREATING
  307. case self.Status.Phase == "CreationFailed":
  308. res = compute.MODELARTS_POOL_STATUS_CREATE_FAILED
  309. case self.Status.Phase == "SeclingFailed":
  310. res = compute.MODELARTS_POOL_STATUS_CHANGE_CONFIG_FAILED
  311. default:
  312. res = compute.MODELARTS_POOL_STATUS_UNKNOWN
  313. }
  314. return res
  315. }
  316. func (self *SModelartsPool) GetSysTags() map[string]string {
  317. return nil
  318. }
  319. func (self *SModelartsPool) GetTags() (map[string]string, error) {
  320. return nil, nil
  321. }
  322. func (self *SModelartsPool) IsEmulated() bool {
  323. return false
  324. }
  325. func (self *SModelartsPool) GetBillingType() string {
  326. if self.Metadata.Annotations.BillingType == "1" {
  327. return billing_api.BILLING_TYPE_PREPAID
  328. } else {
  329. return billing_api.BILLING_TYPE_POSTPAID
  330. }
  331. }
  332. // 获取资源归属项目Id
  333. func (self *SModelartsPool) GetProjectId() string {
  334. return self.Metadata.Name
  335. }
  336. func (self *SModelartsPool) GetExpiredAt() time.Time {
  337. ret, _ := time.Parse("2006-01-02T15:04:05CST", self.Metadata.CreationTimestamp)
  338. if !ret.IsZero() {
  339. ret = ret.Add(time.Hour * 8)
  340. }
  341. return ret
  342. }
  343. func (self *SModelartsPool) IsAutoRenew() bool {
  344. return false
  345. }
  346. func (self *SModelartsPool) Renew(bc billing.SBillingCycle) error {
  347. return nil
  348. }
  349. func (self *SModelartsPool) SetAutoRenew(bc billing.SBillingCycle) error {
  350. return nil
  351. }
  352. func (self *SModelartsPool) RefreshForCreate() error {
  353. self.Status.Resource = SNodeStatus{}
  354. pool, err := self.region.client.modelartsPoolById(self.GetId())
  355. if err == nil {
  356. return pool.Unmarshal(self)
  357. }
  358. if errors.Cause(err) != errors.ErrNotFound {
  359. return err
  360. }
  361. pools := make([]SModelartsPool, 0)
  362. resObj, err := self.region.client.modelartsPoolListWithStatus("pools", "failed", nil)
  363. if err != nil {
  364. return errors.Wrap(err, "modelartsPoolListWithStatus")
  365. }
  366. err = resObj.Unmarshal(&pools, "items")
  367. if err != nil {
  368. return errors.Wrap(err, "resObj unmarshal")
  369. }
  370. for _, pool := range pools {
  371. if pool.GetId() == self.GetId() {
  372. self.Status.Phase = "CreationFailed"
  373. return jsonutils.Update(self, pool)
  374. }
  375. }
  376. return err
  377. }
  378. func (self *SModelartsPool) Refresh() error {
  379. self.Status.Resource = SNodeStatus{}
  380. pool, err := self.region.client.modelartsPoolById(self.GetId())
  381. if err != nil {
  382. return err
  383. }
  384. return pool.Unmarshal(self)
  385. }
  386. func (self *SModelartsPool) SetTags(tags map[string]string, replace bool) error {
  387. return nil
  388. }
  389. func (self *SModelartsPool) Delete() error {
  390. _, err := self.region.DeletePool(self.GetId())
  391. if err != nil {
  392. return err
  393. }
  394. return nil
  395. }
  396. func (self *SModelartsPool) GetStatusMessage() string {
  397. return self.Status.Message
  398. }
  399. func (self *SModelartsPool) GetInstanceType() string {
  400. return self.Spec.Resource[0].Flavor
  401. }
  402. func (self *SModelartsPool) GetWorkType() string {
  403. return strings.Join(self.Spec.Scope, ",")
  404. }
  405. func (self *SModelartsPool) GetNodeCount() int {
  406. if len(self.Spec.Resource) < 1 {
  407. return 0
  408. }
  409. nodeCount := 0
  410. for _, v := range self.Spec.Resource {
  411. nodeCount += v.Count
  412. }
  413. return nodeCount
  414. }
  415. func (self *SModelartsPool) ChangeConfig(opts *cloudprovider.ModelartsPoolChangeConfigOptions) error {
  416. //{"spec":{"resources":[{"flavor":"modelarts.kat1.8xlarge","count":2}]}}
  417. res := []map[string]interface{}{}
  418. for _, re := range self.Spec.Resource {
  419. res = append(res, map[string]interface{}{
  420. "flavor": re.Flavor,
  421. "count": opts.NodeCount,
  422. })
  423. }
  424. params := map[string]interface{}{
  425. "spec": map[string]interface{}{
  426. "resources": res,
  427. },
  428. }
  429. _, err := self.region.client.modelartsPoolUpdate(self.Metadata.Name, params)
  430. return err
  431. }