modelarts

This commit is contained in:
mhf
2022-09-13 18:14:33 +08:00
parent 5fd3cad876
commit 4f1a1f67b0
36 changed files with 2346 additions and 14 deletions

View File

@@ -0,0 +1,30 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compute
import (
"yunion.io/x/onecloud/cmd/climc/shell"
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
"yunion.io/x/onecloud/pkg/mcclient/options"
"yunion.io/x/onecloud/pkg/mcclient/options/compute"
)
func init() {
cmd := shell.NewResourceCmd(&modules.ModelartsPools).WithKeyword("modelarts-pool")
cmd.List(&compute.ModelartsPoolListOptions{})
cmd.Delete(&options.BaseIdOptions{})
cmd.Create(&compute.ModelartsPoolCreateOption{})
cmd.Perform("syncstatus", &compute.ModelartsPoolSyncstatusOption{})
}

View File

@@ -0,0 +1,26 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compute
import (
"yunion.io/x/onecloud/cmd/climc/shell"
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
"yunion.io/x/onecloud/pkg/mcclient/options/compute"
)
func init() {
cmd := shell.NewResourceCmd(&modules.ModelartsPoolSku).WithKeyword("modelarts-pool-sku")
cmd.List(&compute.ModelartsPoolSkuListOptions{})
}

View File

@@ -0,0 +1,73 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compute
import (
"yunion.io/x/onecloud/pkg/apis"
)
const (
MODELARTS_POOL_STATUS_RUNNING = "running"
MODELARTS_POOL_STATUS_ABNORMAL = "abnormal"
MODELARTS_POOL_STATUS_CREATING = "creating"
MODELARTS_POOL_STATUS_DELETING = "deleting"
MODELARTS_POOL_STATUS_ERROR = "error"
MODELARTS_POOL_STATUS_UNKNOWN = "unknown"
)
type ModelartsPoolCreateInput struct {
apis.StatusInfrasResourceBaseCreateInput
CloudproviderResourceInput
}
type ModelartsPoolUpdateInput struct {
apis.StatusInfrasResourceBaseCreateInput
CloudproviderResourceInput
WorkType string `json:"work_type"`
}
// 资源返回详情
type ModelartsPoolDetails struct {
apis.SVirtualResourceBase
apis.VirtualResourceDetails
apis.SExternalizedResourceBase
SBillingResourceBase
ManagedResourceInfo
}
func (self ModelartsPoolDetails) GetMetricTags() map[string]string {
ret := map[string]string{
"modelarts_pool_id": self.Id,
"modelarts_pool_name": self.Name,
"status": self.Status,
"tenant_id": self.ProjectId,
"brand": self.Brand,
"domain_id": self.DomainId,
"account_id": self.AccountId,
"account": self.Account,
}
return ret
}
type ModelartsPoolListInput struct {
apis.VirtualResourceListInput
apis.ExternalizedResourceBaseListInput
ManagedResourceListInput
apis.DeletePreventableResourceBaseListInput
}
type ModelartsPoolSyncstatusInput struct {
}

View File

@@ -0,0 +1,35 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compute
import "yunion.io/x/onecloud/pkg/apis"
type ModelartsPoolSkuDetails struct {
apis.EnabledStatusStandaloneResourceDetails
ManagedResourceInfo
// CloudregionResourceInfo
}
const (
MODELARTS_POOL_SKU_AVAILABLE = "available"
MODELARTS_POOL_SKU_SOLDOUT = "soldout"
)
type ModelartsPoolSkuListInput struct {
apis.EnabledStatusStandaloneResourceListInput
apis.ExternalizedResourceBaseListInput
ManagedResourceListInput
}

View File

@@ -128,6 +128,10 @@ func (self *SBaseCollectDriver) CollectK8sMetrics(ctx context.Context, manager a
return cloudprovider.ErrNotImplemented
}
func (self *SBaseCollectDriver) CollectModelartsPoolMetrics(ctx context.Context, manager api.CloudproviderDetails, provider cloudprovider.ICloudProvider, res map[string]api.ModelartsPoolDetails, start, end time.Time) error {
return cloudprovider.ErrNotImplemented
}
type SCollectByResourceIdDriver struct {
SBaseCollectDriver
}

View File

@@ -39,6 +39,7 @@ type ICollectDriver interface {
CollectLoadbalancerMetrics(ctx context.Context, manager api.CloudproviderDetails, provider cloudprovider.ICloudProvider, res map[string]api.LoadbalancerDetails, start, end time.Time) error
CollectBucketMetrics(ctx context.Context, manager api.CloudproviderDetails, provider cloudprovider.ICloudProvider, res map[string]api.BucketDetails, start, end time.Time) error
CollectK8sMetrics(ctx context.Context, manager api.CloudproviderDetails, provider cloudprovider.ICloudProvider, res map[string]api.KubeClusterDetails, start, end time.Time) error
CollectModelartsPoolMetrics(ctx context.Context, manager api.CloudproviderDetails, provider cloudprovider.ICloudProvider, res map[string]api.ModelartsPoolDetails, start, end time.Time) error
}
func GetDriver(name string) (ICollectDriver, error) {

View File

@@ -15,7 +15,20 @@
package providerdriver
import (
"context"
"strconv"
"sync"
"time"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudmon/options"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/util/influxdb"
)
type HuaweiCollect struct {
@@ -33,3 +46,72 @@ func (self *HuaweiCollect) IsSupportMetrics() bool {
func init() {
Register(&HuaweiCollect{})
}
func (self *HuaweiCollect) CollectModelartsPoolMetrics(ctx context.Context, manager api.CloudproviderDetails, provider cloudprovider.ICloudProvider, res map[string]api.ModelartsPoolDetails, start, end time.Time) error {
metrics := []influxdb.SMetricData{}
var wg sync.WaitGroup
var mu sync.Mutex
for i := range res {
wg.Add(1)
go func(pool api.ModelartsPoolDetails) {
defer func() {
wg.Done()
}()
opts := &cloudprovider.MetricListOptions{
ResourceType: cloudprovider.METRIC_RESOURCE_TYPE_MODELARTS_POOL,
StartTime: start,
EndTime: end,
}
opts.ResourceId = pool.ExternalId
tags := []influxdb.SKeyValue{}
for k, v := range pool.GetMetricTags() {
tags = append(tags, influxdb.SKeyValue{
Key: k,
Value: v,
})
}
data, err := provider.GetMetrics(opts)
if err != nil {
if errors.Cause(err) != cloudprovider.ErrNotImplemented && errors.Cause(err) != cloudprovider.ErrNotSupported {
log.Errorf("get modelarts_pool %s(%s) error: %v", pool.Name, pool.Id, err)
return
}
return
}
for _, values := range data {
for _, value := range values.Values {
metric := influxdb.SMetricData{
Name: values.MetricType.Name(),
Timestamp: value.Timestamp,
Tags: tags,
Metrics: []influxdb.SKeyValue{
{
Key: values.MetricType.Key(),
Value: strconv.FormatFloat(value.Value, 'E', -1, 64),
},
},
}
for k, v := range value.Tags {
metric.Tags = append(metric.Tags, influxdb.SKeyValue{
Key: k,
Value: v,
})
}
mu.Lock()
metrics = append(metrics, metric)
mu.Unlock()
}
}
}(res[i])
}
wg.Wait()
s := auth.GetAdminSession(ctx, options.Options.Region)
urls, err := s.GetServiceURLs(apis.SERVICE_TYPE_INFLUXDB, options.Options.SessionEndpointType, "")
if err != nil {
return errors.Wrap(err, "GetServiceURLs")
}
log.Infof("send %d modelarts_pool with %d metrics for %s(%s)", len(res), len(metrics), manager.Name, manager.Id)
return influxdb.BatchSendMetrics(urls, options.Options.InfluxDatabase, metrics, false)
}

View File

@@ -342,6 +342,7 @@ type SResources struct {
Buckets TResource
KubeClusters TResource
Storages TResource
ModelartsPool TResource
}
func NewResources() *SResources {
@@ -356,6 +357,7 @@ func NewResources() *SResources {
Loadbalancers: NewBaseResources(&compute.Loadbalancers),
Buckets: NewBaseResources(&compute.Buckets),
KubeClusters: NewBaseResources(&compute.KubeClusters),
ModelartsPool: NewBaseResources(&compute.ModelartsPools),
}
}
@@ -403,6 +405,10 @@ func (self *SResources) Init(ctx context.Context, userCred mcclient.TokenCredent
if err != nil {
errs = append(errs, errors.Wrapf(err, "KubeClusters.init"))
}
err = self.ModelartsPool.init()
if err != nil {
errs = append(errs, errors.Wrapf(err, "ModelartsPool.init"))
}
return errors.NewAggregate(errs)
}()
if err != nil {
@@ -455,7 +461,11 @@ func (self *SResources) IncrementSync(ctx context.Context, userCred mcclient.Tok
}
err = self.KubeClusters.increment()
if err != nil {
errs = append(errs, errors.Wrapf(err, "Buckets.increment"))
errs = append(errs, errors.Wrapf(err, "KubeClusters.increment"))
}
err = self.ModelartsPool.increment()
if err != nil {
errs = append(errs, errors.Wrapf(err, "ModelartsPool.increment"))
}
return errors.NewAggregate(errs)
}()
@@ -510,6 +520,10 @@ func (self *SResources) DecrementSync(ctx context.Context, userCred mcclient.Tok
if err != nil {
errs = append(errs, errors.Wrapf(err, "KubeClusters.decrement"))
}
err = self.ModelartsPool.decrement()
if err != nil {
errs = append(errs, errors.Wrapf(err, "ModelartsPool.decrement"))
}
return errors.NewAggregate(errs)
}()
if err != nil {
@@ -551,6 +565,10 @@ func (self *SResources) UpdateSync(ctx context.Context, userCred mcclient.TokenC
if err != nil {
errs = append(errs, errors.Wrapf(err, "Loadbalancers.update"))
}
err = self.ModelartsPool.update()
if err != nil {
errs = append(errs, errors.Wrapf(err, "ModelartsPool.update"))
}
return errors.NewAggregate(errs)
}()
if err != nil {
@@ -665,6 +683,14 @@ func (self *SResources) CollectMetrics(ctx context.Context, userCred mcclient.To
if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented && errors.Cause(err) != cloudprovider.ErrNotSupported {
log.Errorf("CollectK8sMetrics for %s(%s) error: %v", manager.Name, manager.Provider, err)
}
resources = self.ModelartsPool.getResources(manager.Id)
pools := map[string]api.ModelartsPoolDetails{}
jsonutils.Update(&pools, resources)
err = driver.CollectModelartsPoolMetrics(ctx, manager, provider, pools, startTime, endTime)
if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented && errors.Cause(err) != cloudprovider.ErrNotSupported {
log.Errorf("CollectK8sMetrics for %s(%s) error: %v", manager.Name, manager.Provider, err)
}
}(cloudproviders[i])
}
wg.Wait()

View File

@@ -316,6 +316,11 @@ type ICloudProvider interface {
CreateICloudCDNDomain(opts *CdnCreateOptions) (ICloudCDNDomain, error)
GetMetrics(opts *MetricListOptions) ([]MetricValues, error)
GetIModelartsPools() ([]ICloudModelartsPool, error)
GetIModelartsPoolById(id string) (ICloudModelartsPool, error)
CreateIModelartsPool(pool *ModelartsPoolCreateOption) (ICloudModelartsPool, error)
GetIModelartsPoolSku() ([]ICloudModelartsPoolSku, error)
}
func IsSupportCapability(prod ICloudProvider, capa string) bool {
@@ -398,6 +403,10 @@ func IsSupportTablestore(prod ICloudProvider) bool {
return IsSupportCapability(prod, CLOUD_CAPABILITY_TABLESTORE)
}
func IsSupportModelartsPool(prod ICloudProvider) bool {
return IsSupportCapability(prod, CLOUD_CAPABILITY_MODELARTES)
}
var providerTable map[string]ICloudProviderFactory
func init() {
@@ -620,6 +629,22 @@ func (self *SBaseProvider) GetMetrics(opts *MetricListOptions) ([]MetricValues,
return nil, errors.Wrapf(ErrNotImplemented, "GetMetric")
}
func (self *SBaseProvider) GetIModelartsPools() ([]ICloudModelartsPool, error) {
return nil, errors.Wrapf(ErrNotImplemented, "GetIModelartsPools")
}
func (self *SBaseProvider) GetIModelartsPoolById(id string) (ICloudModelartsPool, error) {
return nil, errors.Wrapf(ErrNotImplemented, "GetIModelartsPoolDetail")
}
func (self *SBaseProvider) CreateIModelartsPool(pool *ModelartsPoolCreateOption) (ICloudModelartsPool, error) {
return nil, errors.Wrapf(ErrNotImplemented, "CreateIModelartsPool")
}
func (self *SBaseProvider) GetIModelartsPoolSku() ([]ICloudModelartsPoolSku, error) {
return nil, errors.Wrapf(ErrNotImplemented, "GetIModelartsPoolSku")
}
func NewBaseProvider(factory ICloudProviderFactory) SBaseProvider {
return SBaseProvider{factory: factory}
}
@@ -910,3 +935,27 @@ func (factory *SPrivateCloudBaseProviderFactory) IsSupportPrepaidResources() boo
func (factory *SPrivateCloudBaseProviderFactory) NeedSyncSkuFromCloud() bool {
return true
}
type ICloudModelartsPool interface {
ICloudResource
IBillingResource
Delete() error
GetProjectId() string
GetInstanceType() string
GetWorkType() string
}
type ICloudModelartsPoolSku interface {
ICloudResource
GetCpuCoreCount() int
GetCpuArch() string
GetStatus() string
GetMemorySizeMB() int
GetPoolType() string
GetGpuSize() int
GetGpuType() string
GetNpuSize() int
GetNpuType() string
}

View File

@@ -70,6 +70,7 @@ const (
CLOUD_CAPABILITY_CONTAINER = "container" // 容器
CLOUD_CAPABILITY_IPV6_GATEWAY = "ipv6_gateway" // IPv6网关
CLOUD_CAPABILITY_TABLESTORE = "tablestore" // 表格存储
CLOUD_CAPABILITY_MODELARTES = "modelarts"
)
const (

View File

@@ -44,15 +44,16 @@ func (key TMetricType) Key() string {
}
const (
METRIC_RESOURCE_TYPE_RDS TResourceType = "rds"
METRIC_RESOURCE_TYPE_SERVER TResourceType = "server"
METRIC_RESOURCE_TYPE_HOST TResourceType = "host"
METRIC_RESOURCE_TYPE_REDIS TResourceType = "redis"
METRIC_RESOURCE_TYPE_LB TResourceType = "lb"
METRIC_RESOURCE_TYPE_BUCKET TResourceType = "bucket"
METRIC_RESOURCE_TYPE_K8S TResourceType = "k8s"
METRIC_RESOURCE_TYPE_STORAGE TResourceType = "storage"
METRIC_RESOURCE_TYPE_CLOUD_ACCOUNT TResourceType = "cloudaccount_balance"
METRIC_RESOURCE_TYPE_RDS TResourceType = "rds"
METRIC_RESOURCE_TYPE_SERVER TResourceType = "server"
METRIC_RESOURCE_TYPE_HOST TResourceType = "host"
METRIC_RESOURCE_TYPE_REDIS TResourceType = "redis"
METRIC_RESOURCE_TYPE_LB TResourceType = "lb"
METRIC_RESOURCE_TYPE_BUCKET TResourceType = "bucket"
METRIC_RESOURCE_TYPE_K8S TResourceType = "k8s"
METRIC_RESOURCE_TYPE_STORAGE TResourceType = "storage"
METRIC_RESOURCE_TYPE_CLOUD_ACCOUNT TResourceType = "cloudaccount_balance"
METRIC_RESOURCE_TYPE_MODELARTS_POOL TResourceType = "modelarts_pool"
)
const (
@@ -252,6 +253,19 @@ const (
K8S_NODE_METRIC_TYPE_DISK_USAGE TMetricType = "k8s_node_disk.used_percent"
K8S_NODE_METRIC_TYPE_NET_BPS_RX TMetricType = "k8s_node_netio.bps_recv"
K8S_NODE_METRIC_TYPE_NET_BPS_TX TMetricType = "k8s_node_netio.bps_sent"
// modelarts专属资源池监控数据
MODELARTS_POOL_METRIC_TYPE_CPU_USAGE TMetricType = "modelarts_pool_cpu.usage_percent"
MODELARTS_POOL_METRIC_TYPE_MEM_USAGE TMetricType = "modelarts_pool_mem.usage_percent"
MODELARTS_POOL_METRIC_TYPE_GPU_MEM_USAGE TMetricType = "modelarts_pool_gpu_mem.usage_percent"
MODELARTS_POOL_METRIC_TYPE_GPU_UTIL TMetricType = "modelarts_pool_gpu_util.percent"
MODELARTS_POOL_METRIC_TYPE_NPU_UTIL TMetricType = "modelarts_pool_npu_util.percent"
MODELARTS_POOL_METRIC_TYPE_NPU_MEM_USAGE TMetricType = "modelarts_pool_npu_mem.usage_percent"
//磁盘可用容量
MODELARTS_POOL_METRIC_TYPE_DISK_AVAILABLE_CAPACITY TMetricType = "modelarts_pool_disk.available_capacity"
MODELARTS_POOL_METRIC_TYPE_DISK_CAPACITY TMetricType = "modelarts_pool_disk.capacity"
MODELARTS_POOL_METRIC_TYPE_DISK_USAGE TMetricType = "modelarts_pool_disk.usage_percent"
)
var (

View File

@@ -0,0 +1,32 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cloudprovider
type ModelartsPoolCreateOption struct {
Name string
PoolDesc string
BillingMode uint
PeriodType uint
PeriodNum uint
AutoRenew uint
InstanceType string
NetworkId string
WorkType string
}
type Azs struct {
Az string `json:"az"`
Count int `json:"count"`
}

View File

@@ -1438,6 +1438,8 @@ func (self *SCloudprovider) RealDelete(ctx context.Context, userCred mcclient.To
CloudproviderRegionManager,
CloudregionManager,
CloudproviderQuotaManager,
ModelartsPoolManager,
ModelartsPoolSkuManager,
} {
err = manager.purgeAll(ctx, userCred, self.Id)
if err != nil {

View File

@@ -1882,6 +1882,11 @@ func syncPublicCloudProviderInfo(
}
}
if cloudprovider.IsSupportModelartsPool(driver) && syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_MODELARTES) {
syncModelartsPools(ctx, userCred, syncResults, provider, driver)
syncModelartsPoolSku(ctx, userCred, syncResults, provider, driver)
}
return nil
}
@@ -2341,3 +2346,27 @@ func syncTablestore(ctx context.Context, userCred mcclient.TokenCredential, sync
}
return nil
}
func syncModelartsPools(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, driver cloudprovider.ICloudProvider) error {
ipools, err := driver.GetIModelartsPools()
if err != nil {
msg := fmt.Sprintf("GetIModelartsPools for provider %s failed %s", err, ipools)
log.Errorf(msg)
return err
}
result := provider.SyncModelartsPools(ctx, userCred, ipools)
log.Infof("SyncModelartsPools for region %s result: %s", provider.GetName(), result.Result())
return nil
}
func syncModelartsPoolSkus(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, driver cloudprovider.ICloudProvider) error {
ipools, err := driver.GetIModelartsPoolSku()
if err != nil {
msg := fmt.Sprintf("GetIModelartsPoolSku for provider %s failed %s", err, ipools)
log.Errorf(msg)
return err
}
result := provider.SyncModelartsPoolSkus(ctx, userCred, ipools)
log.Infof("SyncModelartsPoolSkus for region %s result: %s", provider.GetName(), result.Result())
return nil
}

View File

@@ -0,0 +1,293 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/compare"
"yunion.io/x/sqlchemy"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
type SModelartsPoolSkuManager struct {
db.SExternalizedResourceBaseManager
db.SEnabledStatusStandaloneResourceBaseManager
SManagedResourceBaseManager
}
var ModelartsPoolSkuManager *SModelartsPoolSkuManager
func init() {
ModelartsPoolSkuManager = &SModelartsPoolSkuManager{
SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager(
SModelartsPoolSku{},
"modelarts_pool_skus_tbl",
"modelarts_pool_sku",
"modelarts_pool_skus",
),
}
ModelartsPoolSkuManager.NameRequireAscii = false
ModelartsPoolSkuManager.SetVirtualObject(ModelartsPoolSkuManager)
}
type SModelartsPoolSku struct {
SManagedResourceBase
db.SEnabledStatusStandaloneResourceBase
db.SExternalizedResourceBase
Type string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin"` // 资源规格类型
// CPU 架构 x86|xarm
CpuArch string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin"`
//CPU核心数量
CpuCount int `list:"user" create:"admin_optional" update:"admin"`
// GPU卡类型
GpuType string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin"`
// GPU卡数量
GpuSize int `nullable:"true" list:"user" create:"admin_optional" update:"admin"`
// NPU卡类型
NpuType string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin"`
// NPU卡数量
NpuSize int `nullable:"true" list:"user" create:"admin_optional" update:"admin"`
// 内存
Memory int `nullable:"true" list:"user" create:"admin_optional" update:"admin"`
}
func (manager *SModelartsPoolSkuManager) GetContextManagers() [][]db.IModelManager {
return [][]db.IModelManager{}
}
func (man *SModelartsPoolSkuManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.ModelartsPoolSkuListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = man.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.ListItemFilter")
}
q, err = man.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
}
q, err = man.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
}
return q, nil
}
func (man *SModelartsPoolSkuManager) OrderByExtraFields(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.ModelartsPoolSkuListInput,
) (*sqlchemy.SQuery, error) {
q, err := man.SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
}
q, err = man.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
}
q, err = man.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
}
return q, nil
}
func (man *SModelartsPoolSkuManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
q, err := man.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
q, err = man.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
return q, httperrors.ErrNotFound
}
func (manager *SModelartsPoolSkuManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
objs []interface{},
fields stringutils2.SSortedStrings,
isList bool,
) []api.ModelartsPoolSkuDetails {
rows := make([]api.ModelartsPoolSkuDetails, len(objs))
enabledRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
manRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
for i := range rows {
rows[i] = api.ModelartsPoolSkuDetails{
EnabledStatusStandaloneResourceDetails: enabledRows[i],
ManagedResourceInfo: manRows[i],
}
}
return rows
}
func (manager *SModelartsPoolSkuManager) ListItemExportKeys(ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
keys stringutils2.SSortedStrings,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SEnabledStatusStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemExportKeys")
}
if keys.ContainsAny(manager.SManagedResourceBaseManager.GetExportKeys()...) {
q, err = manager.SManagedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemExportKeys")
}
}
return q, nil
}
func (self *SCloudprovider) GetModelartsPoolSkus() ([]SModelartsPoolSku, error) {
q := ModelartsPoolSkuManager.Query()
ret := []SModelartsPoolSku{}
err := db.FetchModelObjects(ModelartsPoolSkuManager, q, &ret)
if err != nil {
return nil, errors.Wrapf(err, "db.FetchModelObjects")
}
return ret, nil
}
func (self *SCloudprovider) SyncModelartsPoolSkus(ctx context.Context, userCred mcclient.TokenCredential, exts []cloudprovider.ICloudModelartsPoolSku) compare.SyncResult {
// 加锁防止重入
lockman.LockRawObject(ctx, self.Provider, "modelarts-pool-sku")
defer lockman.ReleaseRawObject(ctx, self.Provider, "modelarts-pool-sku")
result := compare.SyncResult{}
dbPoolSku, err := self.GetModelartsPoolSkus()
if err != nil {
result.Error(err)
return result
}
removed := make([]SModelartsPoolSku, 0)
commondb := make([]SModelartsPoolSku, 0)
commonext := make([]cloudprovider.ICloudModelartsPoolSku, 0)
added := make([]cloudprovider.ICloudModelartsPoolSku, 0)
// 本地和云上资源列表进行比对
err = compare.CompareSets(dbPoolSku, exts, &removed, &commondb, &commonext, &added)
if err != nil {
result.Error(err)
return result
}
// 删除云上没有的资源
for i := 0; i < len(removed); i++ {
err := removed[i].Delete(ctx, userCred)
if err != nil {
result.DeleteError(err)
continue
}
result.Delete()
}
// 和云上资源属性进行同步
for i := 0; i < len(commondb); i++ {
log.Errorln("this is commonext", commonext[i])
err := commondb[i].syncWithCloudSku(ctx, userCred, commonext[i])
if err != nil {
result.UpdateError(err)
continue
}
result.Update()
}
// 创建本地没有的云上资源
for i := 0; i < len(added); i++ {
err := self.newFromCloudModelartsPoolSku(ctx, userCred, added[i])
if err != nil {
result.AddError(err)
continue
}
result.Add()
}
return result
}
func (self *SModelartsPoolSku) syncWithCloudSku(ctx context.Context, userCred mcclient.TokenCredential, isku cloudprovider.ICloudModelartsPoolSku) error {
_, err := db.Update(self, func() error {
self.Name = isku.GetName()
self.CpuCount = isku.GetCpuCoreCount()
self.CpuArch = isku.GetCpuArch()
self.Status = isku.GetStatus()
self.Type = isku.GetPoolType()
self.CreatedAt = isku.GetCreatedAt()
self.GpuType = isku.GetGpuType()
self.GpuSize = isku.GetGpuSize()
self.Memory = isku.GetMemorySizeMB()
self.NpuType = isku.GetNpuType()
self.NpuSize = isku.GetNpuSize()
self.ExternalId = isku.GetGlobalId()
return nil
})
return err
}
func (self *SCloudprovider) newFromCloudModelartsPoolSku(ctx context.Context, userCred mcclient.TokenCredential, isku cloudprovider.ICloudModelartsPoolSku) error {
sku := SModelartsPoolSku{}
sku.SetModelManager(ModelartsPoolSkuManager, &sku)
sku.Name = isku.GetName()
sku.CpuCount = isku.GetCpuCoreCount()
sku.CpuArch = isku.GetCpuArch()
sku.Status = isku.GetStatus()
sku.Type = isku.GetPoolType()
sku.CreatedAt = isku.GetCreatedAt()
sku.GpuType = isku.GetGpuType()
sku.GpuSize = isku.GetGpuSize()
sku.Memory = isku.GetMemorySizeMB()
sku.NpuType = isku.GetNpuType()
sku.NpuSize = isku.GetNpuSize()
sku.ExternalId = isku.GetGlobalId()
return ModelartsPoolSkuManager.TableSpec().Insert(ctx, &sku)
}
func syncModelartsPoolSku(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, driver cloudprovider.ICloudProvider) error {
ipools, err := driver.GetIModelartsPoolSku()
if err != nil {
msg := fmt.Sprintf("GetIModelartsPoolsSku for provider %s failed %s", err, ipools)
log.Errorf(msg)
return err
}
result := provider.SyncModelartsPoolSkus(ctx, userCred, ipools)
log.Infof("SyncModelartsPools for region %s result: %s", provider.GetName(), result.Result())
return nil
}

View File

@@ -0,0 +1,428 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"fmt"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/compare"
"yunion.io/x/sqlchemy"
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
type SModelartsPoolManager struct {
// 由于资源是用户资源因此定义为Virtual资源
db.SVirtualResourceBaseManager
db.SExternalizedResourceBaseManager
SDeletePreventableResourceBaseManager
SManagedResourceBaseManager
}
var ModelartsPoolManager *SModelartsPoolManager
func init() {
ModelartsPoolManager = &SModelartsPoolManager{
SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
SModelartsPool{},
"modelarts_pools_tbl",
"modelarts_pool",
"modelarts_pools",
),
}
ModelartsPoolManager.SetVirtualObject(ModelartsPoolManager)
}
type SModelartsPool struct {
db.SVirtualResourceBase
db.SExternalizedResourceBase
SManagedResourceBase
SBillingResourceBase
SDeletePreventableResourceBase
InstanceType string `width:"72" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"`
WorkType string `width:"72" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"`
// CPU 架构 x86|xarm
CpuArch string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin"`
}
func (manager *SModelartsPoolManager) GetContextManagers() [][]db.IModelManager {
return [][]db.IModelManager{}
}
// Pool实例列表
func (man *SModelartsPoolManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.ModelartsPoolListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = man.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
}
q, err = man.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
}
q, err = man.SDeletePreventableResourceBaseManager.ListItemFilter(ctx, q, userCred, query.DeletePreventableResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SDeletePreventableResourceBaseManager.ListItemFilter")
}
q, err = man.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
}
return q, nil
}
func (man *SModelartsPoolManager) OrderByExtraFields(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.ModelartsPoolListInput,
) (*sqlchemy.SQuery, error) {
q, err := man.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
}
q, err = man.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
}
return q, nil
}
func (man *SModelartsPoolManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
q, err := man.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
q, err = man.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
return q, httperrors.ErrNotFound
}
func (man *SModelartsPoolManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.ModelartsPoolCreateInput) (api.ModelartsPoolCreateInput, error) {
return input, nil
}
func (manager *SModelartsPoolManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
objs []interface{},
fields stringutils2.SSortedStrings,
isList bool,
) []api.ModelartsPoolDetails {
rows := make([]api.ModelartsPoolDetails, len(objs))
virtRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
manRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
for i := range rows {
rows[i] = api.ModelartsPoolDetails{
VirtualResourceDetails: virtRows[i],
ManagedResourceInfo: manRows[i],
}
}
return rows
}
func (manager *SModelartsPoolManager) ListItemExportKeys(ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
keys stringutils2.SSortedStrings,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemExportKeys")
}
if keys.ContainsAny(manager.SManagedResourceBaseManager.GetExportKeys()...) {
q, err = manager.SManagedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemExportKeys")
}
}
return q, nil
}
func (self *SCloudprovider) GetPools() ([]SModelartsPool, error) {
q := ModelartsPoolManager.Query().Equals("manager_id", self.Id)
ret := []SModelartsPool{}
err := db.FetchModelObjects(ModelartsPoolManager, q, &ret)
if err != nil {
return nil, errors.Wrapf(err, "db.FetchModelObjects")
}
return ret, nil
}
func (self *SCloudprovider) SyncModelartsPools(ctx context.Context, userCred mcclient.TokenCredential, exts []cloudprovider.ICloudModelartsPool) compare.SyncResult {
// 加锁防止重入
lockman.LockRawObject(ctx, ModelartsPoolManager.KeywordPlural(), fmt.Sprintf("%s", self.Id))
defer lockman.ReleaseRawObject(ctx, ModelartsPoolManager.KeywordPlural(), fmt.Sprintf("%s", self.Id))
result := compare.SyncResult{}
dbPools, err := self.GetPools()
if err != nil {
result.Error(err)
return result
}
removed := make([]SModelartsPool, 0)
commondb := make([]SModelartsPool, 0)
commonext := make([]cloudprovider.ICloudModelartsPool, 0)
added := make([]cloudprovider.ICloudModelartsPool, 0)
// 本地和云上资源列表进行比对
err = compare.CompareSets(dbPools, exts, &removed, &commondb, &commonext, &added)
if err != nil {
result.Error(err)
return result
}
// 删除云上没有的资源
for i := 0; i < len(removed); i++ {
err := removed[i].syncRemoveCloudModelartsPool(ctx, userCred)
if err != nil {
result.DeleteError(err)
continue
}
result.Delete()
}
// 和云上资源属性进行同步
for i := 0; i < len(commondb); i++ {
err := commondb[i].SyncWithCloudModelartsPool(ctx, userCred, commonext[i])
if err != nil {
result.UpdateError(err)
continue
}
result.Update()
}
// 创建本地没有的云上资源
for i := 0; i < len(added); i++ {
_, err := self.newFromCloudModelartsPool(ctx, userCred, self, added[i])
if err != nil {
result.AddError(err)
continue
}
result.Add()
}
return result
}
// 判断资源是否可以删除
func (self *SModelartsPool) ValidateDeleteCondition(ctx context.Context) error {
if self.DisableDelete.IsTrue() {
return httperrors.NewInvalidStatusError("ModelartsPool is locked, cannot delete")
}
return self.SStatusStandaloneResourceBase.ValidateDeleteCondition(ctx, nil)
}
func (self *SModelartsPool) syncRemoveCloudModelartsPool(ctx context.Context, userCred mcclient.TokenCredential) error {
return self.RealDelete(ctx, userCred)
}
func (self *SModelartsPool) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
self.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
self.StartCreateTask(ctx, userCred, "")
}
func (self *SModelartsPool) StartCreateTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
var err = func() error {
params := jsonutils.NewDict()
task, err := taskman.TaskManager.NewTask(ctx, "ModelartsPoolCreateTask", self, userCred, params, parentTaskId, "", nil)
if err != nil {
return errors.Wrapf(err, "NewTask")
}
return task.ScheduleRun(nil)
}()
if err != nil {
self.SetStatus(userCred, api.MODELARTS_POOL_STATUS_ERROR, err.Error())
return err
}
self.SetStatus(userCred, api.MODELARTS_POOL_STATUS_CREATING, "")
return nil
}
func (modelarts *SModelartsPool) PerformSyncstatus(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input api.ModelartsPoolSyncstatusInput,
) (jsonutils.JSONObject, error) {
var openTask = true
count, err := taskman.TaskManager.QueryTasksOfObject(modelarts, time.Now().Add(-3*time.Minute), &openTask).CountWithError()
if err != nil {
return nil, err
}
if count > 0 {
return nil, httperrors.NewBadRequestError("ModelartsPool has %d task active, can't sync status", count)
}
return nil, StartResourceSyncStatusTask(ctx, userCred, modelarts, "ModelartsPoolSyncstatusTask", "")
}
func (self *SModelartsPool) StartSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
return StartResourceSyncStatusTask(ctx, userCred, self, "ModelartsPoolSyncstatusTask", parentTaskId)
}
func (self *SModelartsPool) GetCloudproviderId() string {
return self.ManagerId
}
func (self *SModelartsPool) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
return nil
}
func (self *SModelartsPool) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
return self.SVirtualResourceBase.Delete(ctx, userCred)
}
// 进入删除任务
func (self *SModelartsPool) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
return self.StartDeleteTask(ctx, userCred, "")
}
func (self *SModelartsPool) GetICloudModelartsPool(ctx context.Context) (cloudprovider.ICloudModelartsPool, error) {
iProvider, err := self.GetDriver(ctx)
if err != nil {
return nil, errors.Wrap(err, "self.GetDriver")
}
return iProvider.GetIModelartsPoolById(self.ExternalId)
}
func (self *SModelartsPool) StartDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
task, err := taskman.TaskManager.NewTask(ctx, "ModelartsPoolDeleteTask", self, userCred, nil, parentTaskId, "", nil)
if err != nil {
return err
}
self.SetStatus(userCred, api.MODELARTS_POOL_STATUS_DELETING, "")
task.ScheduleRun(nil)
return nil
}
// 获取云上对应的资源
func (self *SModelartsPool) GetIModelartsPool() (cloudprovider.ICloudModelartsPool, error) {
if len(self.ExternalId) == 0 {
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "empty externalId")
}
iProvider, err := self.GetDriver(context.Background())
if err != nil {
return nil, errors.Wrap(err, "self.GetDriver")
}
return iProvider.GetIModelartsPoolById(self.ExternalId)
}
// 同步资源属性
func (self *SModelartsPool) SyncWithCloudModelartsPool(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudModelartsPool) error {
instanceName := ext.GetInstanceType()
sku := SModelartsPoolSku{}
err := ModelartsPoolSkuManager.Query().Equals("name", instanceName).First(&sku)
if err != nil {
return errors.Wrapf(err, "get modelartsPoolSku")
}
diff, err := db.UpdateWithLock(ctx, self, func() error {
self.Status = ext.GetStatus()
self.BillingType = ext.GetBillingType()
self.InstanceType = instanceName
self.WorkType = ext.GetWorkType()
self.CpuArch = sku.CpuArch
return nil
})
if err != nil {
return errors.Wrapf(err, "db.Update")
}
err = syncVirtualResourceMetadata(ctx, userCred, self, ext)
if err != nil {
return errors.Wrapf(err, "syncVirtualResourceMetadata")
}
if provider := self.GetCloudprovider(); provider != nil {
SyncCloudProject(userCred, self, provider.GetOwnerId(), ext, provider.Id)
}
db.OpsLog.LogSyncUpdate(self, diff, userCred)
return nil
}
func (self *SCloudprovider) newFromCloudModelartsPool(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, ext cloudprovider.ICloudModelartsPool) (*SModelartsPool, error) {
pool := SModelartsPool{}
pool.SetModelManager(ModelartsPoolManager, &pool)
pool.ExternalId = ext.GetGlobalId()
pool.ManagerId = provider.Id
pool.IsEmulated = ext.IsEmulated()
pool.Status = ext.GetStatus()
pool.WorkType = ext.GetWorkType()
pool.InstanceType = ext.GetInstanceType()
if createdAt := ext.GetCreatedAt(); !createdAt.IsZero() {
pool.CreatedAt = createdAt
}
sku := SModelartsPoolSku{}
err := ModelartsPoolSkuManager.Query().Equals("Name", pool.InstanceType).First(&sku)
if err != nil {
return nil, errors.Wrapf(err, "ModelartsPoolSkuManager get cpuArch")
}
pool.CpuArch = sku.CpuArch
pool.BillingType = ext.GetBillingType()
if pool.BillingType == billing_api.BILLING_TYPE_PREPAID {
if expired := ext.GetExpiredAt(); !expired.IsZero() {
pool.ExpiredAt = expired
}
pool.AutoRenew = ext.IsAutoRenew()
}
err = func() error {
// 这里加锁是为了防止名称重复
lockman.LockRawObject(ctx, ModelartsPoolManager.Keyword(), "name")
defer lockman.ReleaseRawObject(ctx, ModelartsPoolManager.Keyword(), "name")
pool.Name, err = db.GenerateName(ctx, ModelartsPoolManager, provider.GetOwnerId(), ext.GetName())
if err != nil {
return errors.Wrapf(err, "db.GenerateName")
}
return ModelartsPoolManager.TableSpec().Insert(ctx, &pool)
}()
if err != nil {
return nil, errors.Wrapf(err, "newFromCloudModelartsPool.Insert")
}
// 同步标签
syncVirtualResourceMetadata(ctx, userCred, &pool, ext)
// 同步项目归属
SyncCloudProject(userCred, &pool, provider.GetOwnerId(), ext, provider.Id)
db.OpsLog.LogEvent(&pool, db.ACT_CREATE, pool.GetShortDesc(ctx), userCred)
return &pool, nil
}

View File

@@ -2149,3 +2149,39 @@ func (manager *STablestoreManager) purgeAll(ctx context.Context, userCred mcclie
}
return nil
}
func (manager *SModelartsPoolManager) purgeAll(ctx context.Context, userCred mcclient.TokenCredential, providerId string) error {
ess := []SModelartsPool{}
err := fetchByManagerId(manager, providerId, &ess)
if err != nil {
return errors.Wrapf(err, "fetchByManagerId")
}
for i := range ess {
lockman.LockObject(ctx, &ess[i])
defer lockman.ReleaseObject(ctx, &ess[i])
err := ess[i].RealDelete(ctx, userCred)
if err != nil {
return errors.Wrapf(err, "modelarts pool delete")
}
}
return nil
}
func (manager *SModelartsPoolSkuManager) purgeAll(ctx context.Context, userCred mcclient.TokenCredential, providerId string) error {
poolSku := []SModelartsPoolSku{}
err := fetchByManagerId(manager, providerId, &poolSku)
if err != nil {
return errors.Wrapf(err, "fetchByManagerId")
}
for i := range poolSku {
lockman.LockObject(ctx, &poolSku[i])
defer lockman.ReleaseObject(ctx, &poolSku[i])
err := poolSku[i].Delete(ctx, userCred)
if err != nil {
return errors.Wrapf(err, "modelarts pool delete")
}
}
return nil
}

View File

@@ -247,6 +247,9 @@ func InitHandlers(app *appsrv.Application) {
models.NetTapServiceManager,
models.NetTapFlowManager,
models.ModelartsPoolManager,
models.ModelartsPoolSkuManager,
} {
db.RegisterModelManager(manager)
handler := db.NewModelHandler(manager)

View File

@@ -0,0 +1,82 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tasks
import (
"context"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/logclient"
)
type ModelartsPoolCreateTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(ModelartsPoolCreateTask{})
}
func (self *ModelartsPoolCreateTask) taskFailed(ctx context.Context, pool *models.SModelartsPool, err error) {
pool.SetStatus(self.UserCred, api.MODELARTS_POOL_STATUS_UNKNOWN, err.Error())
logclient.AddActionLogWithStartable(self, pool, logclient.ACT_ALLOCATE, err, self.UserCred, false)
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
}
func (self *ModelartsPoolCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
pool := obj.(*models.SModelartsPool)
opts := &cloudprovider.ModelartsPoolCreateOption{
Name: pool.Name,
InstanceType: pool.InstanceType,
WorkType: pool.WorkType,
}
iProvider, err := pool.GetDriver(ctx)
if err != nil {
self.taskFailed(ctx, pool, errors.Wrapf(err, "pool.GetDriver"))
return
}
ipool, err := iProvider.CreateIModelartsPool(opts)
if err != nil {
self.taskFailed(ctx, pool, errors.Wrapf(err, "iProvider.CreateIModelartsPool"))
return
}
err = db.SetExternalId(pool, self.GetUserCred(), ipool.GetGlobalId())
if err != nil {
self.taskFailed(ctx, pool, errors.Wrapf(err, "db.SetExternalId"))
return
}
err = cloudprovider.WaitStatusWithDelay(ipool, api.MODELARTS_POOL_STATUS_RUNNING, 30*time.Second, 15*time.Second, 600*time.Second)
if err != nil {
self.taskFailed(ctx, pool, errors.Wrapf(err, "db.WaitStatusWithDelay"))
return
}
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
Obj: self,
Action: notifyclient.ActionCreate,
})
pool.StartSyncstatus(ctx, self.GetUserCred(), self.GetTaskId())
}

View File

@@ -0,0 +1,80 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tasks
import (
"context"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/logclient"
)
type ModelartsPoolDeleteTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(ModelartsPoolDeleteTask{})
}
func (self *ModelartsPoolDeleteTask) taskFailed(ctx context.Context, mp *models.SModelartsPool, err error) {
mp.SetStatus(self.UserCred, api.NAS_STATUS_DELETE_FAILED, err.Error())
db.OpsLog.LogEvent(mp, db.ACT_DELETE_FAIL, err, self.UserCred)
logclient.AddActionLogWithStartable(self, mp, logclient.ACT_DELOCATE, err, self.UserCred, false)
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
}
func (self *ModelartsPoolDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
pool := obj.(*models.SModelartsPool)
if len(pool.ExternalId) == 0 {
self.taskComplete(ctx, pool)
return
}
iMp, err := pool.GetIModelartsPool()
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
self.taskComplete(ctx, pool)
return
}
self.taskFailed(ctx, pool, errors.Wrapf(err, "iMp.GetIModelartsPoolById"))
return
}
err = iMp.Delete()
if err != nil {
self.taskFailed(ctx, pool, errors.Wrapf(err, "iMp.Delete"))
return
}
cloudprovider.WaitDeleted(iMp, time.Second*10, time.Minute*5)
self.taskComplete(ctx, pool)
}
func (self *ModelartsPoolDeleteTask) taskComplete(ctx context.Context, pool *models.SModelartsPool) {
pool.RealDelete(ctx, self.GetUserCred())
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
Obj: self,
Action: notifyclient.ActionDelete,
})
self.SetStageComplete(ctx, nil)
}

View File

@@ -0,0 +1,61 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tasks
import (
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/logclient"
)
type ModelartsPoolSyncstatusTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(ModelartsPoolSyncstatusTask{})
}
func (self *ModelartsPoolSyncstatusTask) taskFailed(ctx context.Context, modelarts *models.SModelartsPool, err error) {
modelarts.SetStatus(self.GetUserCred(), api.MODELARTS_POOL_STATUS_UNKNOWN, err.Error())
db.OpsLog.LogEvent(modelarts, db.ACT_SYNC_STATUS, err, self.GetUserCred())
logclient.AddActionLogWithContext(ctx, modelarts, logclient.ACT_SYNC_STATUS, err, self.UserCred, false)
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
}
func (self *ModelartsPoolSyncstatusTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
modelarts := obj.(*models.SModelartsPool)
pool, err := modelarts.GetIModelartsPool()
if err != nil {
self.taskFailed(ctx, modelarts, errors.Wrapf(err, "modelarts.GetIModelartsPool()"))
return
}
err = modelarts.SyncWithCloudModelartsPool(ctx, self.UserCred, pool)
if err != nil {
self.taskFailed(ctx, modelarts, errors.Wrap(err, "snetwork.SyncWithCloudModelartsPool()"))
return
}
logclient.AddActionLogWithStartable(self, modelarts, logclient.ACT_SYNC_STATUS, nil, self.UserCred, true)
self.SetStageComplete(ctx, nil)
}

View File

@@ -25,10 +25,12 @@
package apis
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (

View File

@@ -4,6 +4,7 @@ package apis
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"

View File

@@ -0,0 +1,36 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compute
import (
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
type ModelartsPoolSkuManager struct {
modulebase.ResourceManager
}
var (
ModelartsPoolSku ModelartsPoolSkuManager
)
func init() {
ModelartsPoolSku = ModelartsPoolSkuManager{modules.NewComputeManager("modelarts_pool_sku", "modelarts_pool_skus",
[]string{},
[]string{})}
modules.RegisterCompute(&ModelartsPoolSku)
}

View File

@@ -0,0 +1,36 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compute
import (
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
type ModelartsPoolManager struct {
modulebase.ResourceManager
}
var (
ModelartsPools ModelartsPoolManager
)
func init() {
ModelartsPools = ModelartsPoolManager{modules.NewComputeManager("modelarts_pool", "modelarts_pools",
[]string{},
[]string{})}
modules.RegisterCompute(&ModelartsPools)
}

View File

@@ -0,0 +1,78 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compute
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
type ModelartsPoolListOptions struct {
options.BaseListOptions
}
func (opts *ModelartsPoolListOptions) Params() (jsonutils.JSONObject, error) {
return options.ListStructToParams(opts)
}
type ModelartsPoolIdOption struct {
ID string `help:"ModelartsPool Id"`
}
func (opts *ModelartsPoolIdOption) GetId() string {
return opts.ID
}
func (opts *ModelartsPoolIdOption) Params() (jsonutils.JSONObject, error) {
return nil, nil
}
type ModelartsPoolCreateOption struct {
Name string `help:"Name"`
ManagerId string `help:"Manager Id"`
InstanceType string `help:"Instance Type"`
WorkType string `help:"Work Type"`
CpuArch string `help:"Cpu Arch"`
}
func (opts *ModelartsPoolCreateOption) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(opts), nil
}
type ModelartsPoolUpdateOption struct {
ID string `help:"Id"`
WorkType string `help:"Work Type"`
}
func (opts *ModelartsPoolUpdateOption) GetId() string {
return opts.ID
}
func (opts *ModelartsPoolUpdateOption) Params() (jsonutils.JSONObject, error) {
return options.ListStructToParams(opts)
}
type ModelartsPoolSyncstatusOption struct {
ID string `help:"Id"`
}
func (opts *ModelartsPoolSyncstatusOption) GetId() string {
return opts.ID
}
func (opts *ModelartsPoolSyncstatusOption) Params() (jsonutils.JSONObject, error) {
return options.ListStructToParams(opts)
}

View File

@@ -0,0 +1,29 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compute
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
type ModelartsPoolSkuListOptions struct {
options.BaseListOptions
}
func (opts *ModelartsPoolSkuListOptions) Params() (jsonutils.JSONObject, error) {
return options.ListStructToParams(opts)
}

View File

@@ -112,7 +112,7 @@ func (self *SBaseManager) _list(request requests.IRequest, responseKey string) (
rets, err := body.GetArray(responseKey)
if err != nil {
return nil, err
return nil, errors.Wrapf(err, "body.GetArray %s", responseKey)
}
total, _ := body.Int("count")
// if err != nil {

View File

@@ -52,6 +52,8 @@ const (
ServiceNameCES ServiceNameType = "ces" // 监控服务 CloudEye
ServiceNameEPS ServiceNameType = "eps" // 企业项目
ServiceNameModelarts ServiceNameType = "modelarts" // AI ModelArts
ServiceNameSFSTurbo ServiceNameType = "sfs-turbo" // 文件系统
)

View File

@@ -19,6 +19,7 @@ import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"sync"
"time"
@@ -28,6 +29,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/gotypes"
"yunion.io/x/pkg/util/timeutils"
api "yunion.io/x/onecloud/pkg/apis/compute"
@@ -228,6 +230,55 @@ func (self *SHuaweiClient) monitorPost(resource string, params map[string]interf
return self.request(httputils.POST, url, nil, params)
}
func (self *SHuaweiClient) modelartsPoolNetworkList(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) {
uri := fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v1/%s/networks", self.clientRegion, self.projectId)
return self.request(httputils.GET, uri, url.Values{}, params)
}
func (self *SHuaweiClient) modelartsPoolNetworkCreate(params map[string]interface{}) (jsonutils.JSONObject, error) {
uri := fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v1/%s/networks", self.clientRegion, self.projectId)
return self.request(httputils.POST, uri, url.Values{}, params)
}
func (self *SHuaweiClient) modelartsPoolById(poolName string, params map[string]interface{}) (jsonutils.JSONObject, error) {
uri := fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v2/%s/pools/%s", self.clientRegion, self.projectId, poolName)
return self.request(httputils.GET, uri, url.Values{}, params)
}
func (self *SHuaweiClient) modelartsPoolList(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) {
uri := fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v2/%s/%s", self.clientRegion, self.projectId, resource)
return self.request(httputils.GET, uri, url.Values{}, params)
}
func (self *SHuaweiClient) modelartsPoolCreate(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) {
uri := fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v2/%s/%s", self.clientRegion, self.projectId, resource)
return self.request(httputils.POST, uri, url.Values{}, params)
}
func (self *SHuaweiClient) modelartsPoolDelete(resource, poolName string, params map[string]interface{}) (jsonutils.JSONObject, error) {
uri := fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v2/%s/pools/%s", self.clientRegion, self.projectId, poolName)
return self.request(httputils.DELETE, uri, url.Values{}, params)
}
func (self *SHuaweiClient) modelartsPoolUpdate(poolName string, params map[string]interface{}) (jsonutils.JSONObject, error) {
uri := fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v2/%s/pools/%s", self.clientRegion, self.projectId, poolName)
urlValue := url.Values{}
urlValue.Add("time_range", "")
urlValue.Add("statistics", "")
urlValue.Add("period", "")
return self.patchRequest(httputils.PATCH, uri, urlValue, params)
}
func (self *SHuaweiClient) modelartsPoolMonitor(poolName string, params map[string]interface{}) (jsonutils.JSONObject, error) {
uri := fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v2/%s/pools/%s/monitor", self.clientRegion, self.projectId, poolName)
return self.request(httputils.GET, uri, url.Values{}, params)
}
func (self *SHuaweiClient) modelartsResourceflavors(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) {
uri := fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v1/%s/%s", self.clientRegion, self.projectId, resource)
return self.request(httputils.GET, uri, url.Values{}, params)
}
func (self *SHuaweiClient) lbGet(regionId, resource string) (jsonutils.JSONObject, error) {
uri := fmt.Sprintf("https://elb.%s.myhuaweicloud.com/v2/%s/%s", regionId, self.projectId, resource)
return self.request(httputils.GET, uri, url.Values{}, nil)
@@ -280,7 +331,7 @@ type akClient struct {
func (self *akClient) Do(req *http.Request) (*http.Response, error) {
req.Header.Del("Accept")
if req.Method == string(httputils.GET) || req.Method == string(httputils.DELETE) {
if req.Method == string(httputils.GET) || req.Method == string(httputils.DELETE) || req.Method == string(httputils.PATCH) {
req.Header.Del("Content-Length")
}
aksk.Sign(req, self.aksk)
@@ -310,6 +361,9 @@ func (self *SHuaweiClient) request(method httputils.THttpMethod, url string, que
if len(self.projectId) > 0 {
header.Set("X-Project-Id", self.projectId)
}
// if len(self.ownerId) > 0 {
// header.Set("X-Domain-Id", self.ownerId)
// }
_, resp, err := httputils.JSONRequest(client, context.Background(), method, url, header, body, self.debug)
if err != nil {
if e, ok := err.(*httputils.JSONClientError); ok && e.Code == 404 {
@@ -690,6 +744,7 @@ func (self *SHuaweiClient) GetCapabilities() []string {
cloudprovider.CLOUD_CAPABILITY_NAT,
cloudprovider.CLOUD_CAPABILITY_NAS,
cloudprovider.CLOUD_CAPABILITY_QUOTA + cloudprovider.READ_ONLY_SUFFIX,
cloudprovider.CLOUD_CAPABILITY_MODELARTES,
}
// huawei objectstore is shared across projects(subscriptions)
// to avoid multiple project access the same bucket
@@ -757,3 +812,34 @@ func (self *SHuaweiClient) initOwner() error {
self.ownerId = ownerId
return nil
}
func (self *SHuaweiClient) patchRequest(method httputils.THttpMethod, url string, query url.Values, params map[string]interface{}) (jsonutils.JSONObject, error) {
client := self.getAkClient()
if len(query) > 0 {
url = fmt.Sprintf("%s?%s", url, query.Encode())
}
var body jsonutils.JSONObject = nil
if len(params) > 0 {
body = jsonutils.Marshal(params)
}
header := http.Header{}
if len(self.projectId) > 0 {
header.Set("X-Project-Id", self.projectId)
}
var bodystr string
if !gotypes.IsNil(body) {
bodystr = body.String()
}
jbody := strings.NewReader(bodystr)
header.Set("Content-Length", strconv.FormatInt(int64(len(bodystr)), 10))
header.Set("Content-Type", "application/merge-patch+json")
resp, err := httputils.Request(client, context.Background(), method, url, header, jbody, self.debug)
_, respValue, err := httputils.ParseJSONResponse(bodystr, resp, err, self.debug)
if err != nil {
if e, ok := err.(*httputils.JSONClientError); ok && e.Code == 404 {
return nil, errors.Wrapf(cloudprovider.ErrNotFound, err.Error())
}
return nil, err
}
return respValue, err
}

View File

@@ -0,0 +1,350 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package huawei
import (
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud"
"yunion.io/x/onecloud/pkg/util/billing"
)
type SModelartsPool struct {
client *SHuaweiClient
multicloud.SResourceBase
Metadata SModelartsPoolMetadata `json:"metadata"`
Spec SModelartsPoolSpec `json:"spec"`
Status SModelartsPoolStatus `json:"status"`
InstanceType string
WorkType string
}
type SModelartsPoolMetadata struct {
Name string `json:"name"`
CreationTimestamp string `json:"creationTimestamp"`
Labels SModelartsPoolMeatadataLabel
Annotations SModelartsPoolMetadataAnnotations `json:"annotations"`
}
type SModelartsPoolMeatadataLabel struct {
WorkspaceId string `json:"os.modelarts/workspace.id"`
Name string `json:"os.modelarts/name"`
ResourceId string `json:"os.modelarts/resource.id"`
}
type SModelartsPoolMetadataAnnotations struct {
Describe string `json:"os.modelarts/description"`
BillingType string `json:"os.modelarts/billing.mode"`
BillingCycle string `json:"os.modelarts/period.num"`
BillingPeriodType string `json:"os.modelarts/period.type"`
BillingMod string `json:"os.modelarts/charging.mode"`
BillingRenew string `json:"os.modelarts/auto.renew"`
OrderId string `json:"os.modelarts/order.id"`
}
type SModelartsPoolSpec struct {
Type string `json:"type"`
Scope []string `json:"scope"`
Resource []SModelartsPoolResource `json:"resources"`
}
type SModelartsPoolResource struct {
Flavor string `json:"flavor"`
Count int `json:"count"`
cloudprovider.Azs
}
type SModelartsPoolStatus struct {
Phase string `json:"phase"`
Message string `json:"message"`
}
type SModelartsPoolNetwork struct {
Metadata SModelartsPoolNetworkMetadata `json:"metadata"`
}
type SModelartsPoolNetworkMetadata struct {
Name string `json:"name"`
CreationTimestamp string `json:"creationTimestamp"`
}
func (self *SHuaweiClient) GetIModelartsPools() ([]cloudprovider.ICloudModelartsPool, error) {
pools := make([]SModelartsPool, 0)
resObj, err := self.modelartsPoolList("pools", nil)
if err != nil {
return nil, errors.Wrap(err, "region.GetPools")
}
err = resObj.Unmarshal(&pools, "items")
if err != nil {
return nil, errors.Wrap(err, "resObj unmarshal")
}
res := make([]cloudprovider.ICloudModelartsPool, len(pools))
for i := 0; i < len(pools); i++ {
pools[i].client = self
res[i] = &pools[i]
}
return res, nil
}
func (self *SHuaweiClient) CreateIModelartsPool(args *cloudprovider.ModelartsPoolCreateOption) (cloudprovider.ICloudModelartsPool, error) {
netObj, err := self.modelartsPoolNetworkList("network", nil)
if err != nil {
return nil, errors.Wrap(err, "SHuaweiClient.GetPools")
}
netRes := make([]SModelartsPoolNetwork, 0)
netObj.Unmarshal(&netRes, "items")
netId := ""
if len(netRes) != 0 {
netId = netRes[0].Metadata.Name
} else {
createNetObj, err := self.CreatePoolNetworks()
if err != nil {
return nil, errors.Wrap(err, "SHuaweiClient.CreatePoolNetworks")
}
netId, _ = createNetObj.GetString("metadata", "name")
}
scopeArr := strings.Split(args.WorkType, ",")
params := map[string]interface{}{
"apiVersion": "v2",
"kind": "Pool",
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"os.modelarts/name": args.Name,
"os.modelarts/workspace.id": "0",
},
},
"spec": map[string]interface{}{
"type": "Dedicate",
"scope": scopeArr,
"network": map[string]interface{}{
"name": netId,
},
"resources": []map[string]interface{}{
{
"flavor": args.InstanceType,
"count": 1,
},
},
},
}
obj, err := self.modelartsPoolCreate("pools", params)
if err != nil {
return nil, errors.Wrap(err, "SHuaweiClient.GetPools")
}
pool := &SModelartsPool{}
obj.Unmarshal(&pool)
res := []cloudprovider.ICloudModelartsPool{}
for i := 0; i < 1; i++ {
pool.client = self
res = append(res, pool)
}
return res[0], nil
}
func (self *SHuaweiClient) DeletePool(poolName string) (jsonutils.JSONObject, error) {
return self.modelartsPoolDelete("pools", poolName, nil)
}
func (self *SHuaweiClient) GetIModelartsPoolById(poolId string) (cloudprovider.ICloudModelartsPool, error) {
obj, err := self.modelartsPoolById(poolId, nil)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "")
}
return nil, errors.Wrap(err, "region.modelartsPoolByName")
}
pool := &SModelartsPool{}
obj.Unmarshal(&pool)
res := []cloudprovider.ICloudModelartsPool{}
for i := 0; i < 1; i++ {
pool.client = self
res = append(res, pool)
}
return res[0], nil
}
func (self *SHuaweiClient) MonitorPool(poolId string) (*SModelartsMetrics, error) {
resObj, err := self.modelartsPoolMonitor(poolId, nil)
if err != nil {
return nil, errors.Wrapf(err, "send request error")
}
metrics := SModelartsMetrics{}
err = resObj.Unmarshal(&metrics)
if err != nil {
return nil, errors.Wrapf(err, "unmarsh error")
}
return &metrics, nil
}
type SModelartsMetrics struct {
Metrics []SModelartsMetric `json:"metrics"`
}
type SModelartsMetric struct {
Metric SModelartsMetricInfo `json:"metric"`
Datapoints []SModelartsDataPoints `json:"dataPoints"`
}
type SModelartsMetricInfo struct {
Dimensions []SModelartsDimensions `json:"dimensions"`
MetricName string
Namespace string
}
type SModelartsDimensions struct {
Name string
Value string
}
type SModelartsDataPoints struct {
Timestamp int64
Unit string
Statistics []ModelartsStatistics
}
type ModelartsStatistics struct {
Statistic string
Value float64
}
func (self *SHuaweiClient) GetPoolNetworks(poolName string) (jsonutils.JSONObject, error) {
return self.modelartsPoolNetworkList(poolName, nil)
}
func (self *SHuaweiClient) CreatePoolNetworks() (jsonutils.JSONObject, error) {
params := map[string]interface{}{
"apiVersion": "v1",
"kind": "Network",
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"os.modelarts/name": "test",
"os.modelarts/workspace.id": "0",
},
},
"spec": map[string]interface{}{
"cidr": "192.168.20.0/24",
},
}
return self.modelartsPoolNetworkCreate(params)
}
func (self *SModelartsPool) GetCreatedAt() time.Time {
ret, _ := time.Parse("2006-01-02T15:04:05CST", self.Metadata.CreationTimestamp)
if !ret.IsZero() {
ret = ret.Add(time.Hour * 8)
}
return ret
}
func (self *SModelartsPool) GetGlobalId() string {
return self.Metadata.Name
}
func (self *SModelartsPool) GetId() string {
return self.Metadata.Name
}
func (self *SModelartsPool) GetName() string {
return self.Metadata.Labels.Name
}
func (self *SModelartsPool) GetStatus() string {
return strings.ToLower(self.Status.Phase)
}
func (self *SModelartsPool) GetSysTags() map[string]string {
return nil
}
func (self *SModelartsPool) GetTags() (map[string]string, error) {
return nil, nil
}
func (self *SModelartsPool) IsEmulated() bool {
return false
}
func (self *SModelartsPool) GetBillingType() string {
if self.Metadata.Annotations.BillingType == "1" {
return billing_api.BILLING_TYPE_PREPAID
} else {
return billing_api.BILLING_TYPE_POSTPAID
}
return self.Metadata.Annotations.BillingType
}
// 获取资源归属项目Id
func (self *SModelartsPool) GetProjectId() string {
return self.Metadata.Name
}
func (self *SModelartsPool) GetExpiredAt() time.Time {
ret, _ := time.Parse("2006-01-02T15:04:05CST", self.Metadata.CreationTimestamp)
if !ret.IsZero() {
ret = ret.Add(time.Hour * 8)
}
return ret
}
func (self *SModelartsPool) IsAutoRenew() bool {
return false
}
func (self *SModelartsPool) Renew(bc billing.SBillingCycle) error {
return nil
}
func (self *SModelartsPool) SetAutoRenew(bc billing.SBillingCycle) error {
return nil
}
func (self *SModelartsPool) Refresh() error {
pool, err := self.client.modelartsPoolById(self.GetId(), nil)
if err != nil {
return errors.Wrapf(err, "GetModelartsPool(%s)", self.GetId())
}
return jsonutils.Update(self, pool)
}
func (self *SModelartsPool) SetTags(tags map[string]string, replace bool) error {
return nil
}
func (self *SModelartsPool) Delete() error {
_, err := self.client.DeletePool(self.GetId())
if err != nil {
return err
}
return nil
}
func (self *SModelartsPool) GetInstanceType() string {
return self.Spec.Resource[0].Flavor
}
func (self *SModelartsPool) GetWorkType() string {
return strings.Join(self.Spec.Scope, ",")
}

View File

@@ -0,0 +1,130 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package huawei
import (
"strconv"
"time"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud"
)
type SModelartsPoolSku struct {
multicloud.SResourceBase
multicloud.HuaweiTags
region *SRegion
Kind string `json:"kind"`
Spec SModelartsResourceflavorsSpec `json:"spec"`
Status SModelartsResourceflavorsStatus `json:"status"`
}
type SModelartsResourceflavorsSpec struct {
BillingCode string `json:"billingCode"`
BillingModes []int `json:"billingMods"`
Cpu int `json:"cpu"`
CpuArch string `json:"cpuArch"`
Gpu SModelartsResourceflavorsGpuSpec `json:"gpu"`
Npu SModelartsResourceflavorsGpuSpec `json:"npu"`
Memory string `json:"memory"`
Type string `json:"type"`
}
type SModelartsResourceflavorsGpuSpec struct {
Size int `json:"size"`
Type string `json:"type"`
}
type SModelartsResourceflavorsStatus struct {
Phase map[string]interface{} `json:"phase"`
}
func (self *SHuaweiClient) GetIModelartsPoolSku() ([]cloudprovider.ICloudModelartsPoolSku, error) {
params := make(map[string]interface{})
resourceflavors := make([]SModelartsPoolSku, 0)
obj, err := self.modelartsResourceflavors("resourceflavors", params)
if err != nil {
return nil, errors.Wrap(err, "region.modelartsResourceflavors")
}
obj.Unmarshal(&resourceflavors, "items")
res := make([]cloudprovider.ICloudModelartsPoolSku, len(resourceflavors))
for i := 0; i < len(resourceflavors); i++ {
res[i] = &resourceflavors[i]
}
return res, nil
}
func (self *SModelartsPoolSku) GetCreatedAt() time.Time {
createdAt, _ := time.Parse("2006-01-02T15:04:05CST", time.Now().Format("2006-01-02T15:04:05CST"))
return createdAt
}
func (self *SModelartsPoolSku) GetGlobalId() string {
return self.Spec.BillingCode
}
func (self *SModelartsPoolSku) GetId() string {
return self.Spec.BillingCode
}
func (self *SModelartsPoolSku) GetName() string {
return self.Spec.BillingCode
}
func (self *SModelartsPoolSku) GetCpuArch() string {
return self.Spec.CpuArch
}
func (self *SModelartsPoolSku) GetCpuCoreCount() int {
return self.Spec.Cpu
}
func (self *SModelartsPoolSku) GetMemorySizeMB() int {
size, _ := strconv.Atoi(self.Spec.Memory[:len(self.Spec.Memory)-2])
return size * 1024
}
func (self *SModelartsPoolSku) GetStatus() string {
for _, v := range self.Status.Phase {
if v == "normal" {
return compute.MODELARTS_POOL_SKU_AVAILABLE
}
}
return compute.MODELARTS_POOL_SKU_SOLDOUT
}
func (self *SModelartsPoolSku) GetGpuSize() int {
return self.Spec.Gpu.Size
}
func (self *SModelartsPoolSku) GetGpuType() string {
return self.Spec.Gpu.Type
}
func (self *SModelartsPoolSku) GetNpuSize() int {
return self.Spec.Npu.Size
}
func (self *SModelartsPoolSku) GetNpuType() string {
return self.Spec.Npu.Type
}
func (self *SModelartsPoolSku) GetPoolType() string {
return self.Spec.Type
}

View File

@@ -478,6 +478,70 @@ func (self *SHuaweiClient) getLoadbalancerMetrics(opts *cloudprovider.MetricList
return result, nil
}
func (self *SHuaweiClient) getModelartsPoolMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
resp, err := self.modelartsPoolMonitor(opts.ResourceId, nil)
if err != nil {
return nil, err
}
metricData := []SModelartsMetric{}
err = resp.Unmarshal(&metricData, "metrics")
if err != nil {
return nil, errors.Wrapf(err, "resp.Unmarshal")
}
result := []cloudprovider.MetricValues{}
for i := range metricData {
isMB := false
if metricData[i].Datapoints[0].Unit == "Megabytes" {
isMB = true
metricData[i].Datapoints[0].Unit = "Bytes"
}
ret := cloudprovider.MetricValues{
Id: opts.ResourceId,
Unit: metricData[i].Datapoints[0].Unit,
Values: []cloudprovider.MetricValue{},
}
tags := map[string]string{}
switch metricData[i].Metric.MetricName {
case "cpuUsage":
ret.MetricType = cloudprovider.MODELARTS_POOL_METRIC_TYPE_CPU_USAGE
case "memUsedRate":
ret.MetricType = cloudprovider.MODELARTS_POOL_METRIC_TYPE_MEM_USAGE
case "gpuUtil":
ret.MetricType = cloudprovider.MODELARTS_POOL_METRIC_TYPE_GPU_UTIL
case "gpuMemUsage":
ret.MetricType = cloudprovider.MODELARTS_POOL_METRIC_TYPE_GPU_MEM_USAGE
case "npuUtil":
ret.MetricType = cloudprovider.MODELARTS_POOL_METRIC_TYPE_NPU_UTIL
case "npuMemUsage":
ret.MetricType = cloudprovider.MODELARTS_POOL_METRIC_TYPE_NPU_MEM_USAGE
case "diskAvailableCapacity":
ret.MetricType = cloudprovider.MODELARTS_POOL_METRIC_TYPE_DISK_AVAILABLE_CAPACITY
case "diskCapacity":
ret.MetricType = cloudprovider.MODELARTS_POOL_METRIC_TYPE_DISK_CAPACITY
case "diskUsedRate":
ret.MetricType = cloudprovider.MODELARTS_POOL_METRIC_TYPE_DISK_USAGE
default:
log.Warningf("invalid metricName %s for %s %s", metricData[i].Metric.MetricName, opts.ResourceType, opts.ResourceId)
continue
}
for _, value := range metricData[i].Datapoints {
if isMB {
value.Statistics[0].Value *= 1024
}
if value.Statistics[0].Value == -1 {
value.Statistics[0].Value = 0
}
metricValue := cloudprovider.MetricValue{
Value: value.Statistics[0].Value,
Timestamp: time.UnixMilli(value.Timestamp),
Tags: tags,
}
ret.Values = append(ret.Values, metricValue)
}
result = append(result, ret)
}
return result, nil
}
func (self *SHuaweiClient) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
switch opts.ResourceType {
case cloudprovider.METRIC_RESOURCE_TYPE_SERVER:
@@ -490,6 +554,8 @@ func (self *SHuaweiClient) GetMetrics(opts *cloudprovider.MetricListOptions) ([]
return self.getBucketMetrics(opts)
case cloudprovider.METRIC_RESOURCE_TYPE_LB:
return self.getLoadbalancerMetrics(opts)
case cloudprovider.METRIC_RESOURCE_TYPE_MODELARTS_POOL:
return self.getModelartsPoolMetrics(opts)
default:
return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "%s", opts.ResourceType)
}

View File

@@ -305,6 +305,22 @@ func (self *SHuaweiProvider) CreateICloudSAMLProvider(opts *cloudprovider.SAMLPr
return sp, nil
}
func (self *SHuaweiProvider) CreateIModelartsPool(args *cloudprovider.ModelartsPoolCreateOption) (cloudprovider.ICloudModelartsPool, error) {
return self.client.CreateIModelartsPool(args)
}
func (self *SHuaweiProvider) GetIModelartsPoolById(poolId string) (cloudprovider.ICloudModelartsPool, error) {
return self.client.GetIModelartsPoolById(poolId)
}
func (self *SHuaweiProvider) GetIModelartsPools() ([]cloudprovider.ICloudModelartsPool, error) {
return self.client.GetIModelartsPools()
}
func (self *SHuaweiProvider) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
return self.client.GetMetrics(opts)
}
func (self *SHuaweiProvider) GetIModelartsPoolSku() ([]cloudprovider.ICloudModelartsPoolSku, error) {
return self.client.GetIModelartsPoolSku()
}

View File

@@ -0,0 +1,78 @@
// @@ -0,0 +1,46 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package shell
import (
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud/huawei"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type ModelartsPoolListOption struct {
PoolId string `help:"Pool Id"`
}
shellutils.R(&ModelartsPoolListOption{}, "modelarts-pool-list", "List Modelarts Pool", func(cli *huawei.SRegion, args *ModelartsPoolListOption) error {
pools, err := cli.GetClient().GetIModelartsPools()
if err != nil {
return err
}
printList(pools, len(pools), 0, 0, nil)
return nil
})
shellutils.R(&ModelartsPoolListOption{}, "modelarts-pool-detail", "List pool", func(cli *huawei.SRegion, args *ModelartsPoolListOption) error {
pools, err := cli.GetClient().GetIModelartsPoolById(args.PoolId)
if err != nil {
return err
}
log.Infoln(pools)
return nil
})
shellutils.R(&cloudprovider.ModelartsPoolCreateOption{}, "modelarts-pool-create", "Create Modelarts Pool", func(cli *huawei.SRegion, args *cloudprovider.ModelartsPoolCreateOption) error {
res, err := cli.GetClient().CreateIModelartsPool(args)
if err != nil {
return err
}
// printList(pools, len(pools), 0, 0, nil)
log.Infoln("this is res:", res)
return nil
})
shellutils.R(&ModelartsPoolListOption{}, "modelarts-pool-delete", "Delete Modelarts Pool", func(cli *huawei.SRegion, args *ModelartsPoolListOption) error {
res, err := cli.GetClient().DeletePool(args.PoolId)
if err != nil {
return err
}
// printList(pools, len(pools), 0, 0, nil)
log.Infoln("this is res:", res)
return nil
})
shellutils.R(&ModelartsPoolListOption{}, "modelarts-pool-monitor", "Delete Modelarts Pool", func(cli *huawei.SRegion, args *ModelartsPoolListOption) error {
res, err := cli.GetClient().MonitorPool(args.PoolId)
if err != nil {
return err
}
// log.Println("this is res:", res)
printList(res.Metrics, len(res.Metrics), 0, 0, nil)
return nil
})
}

View File

@@ -0,0 +1,35 @@
// @@ -0,0 +1,46 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package shell
import (
"yunion.io/x/onecloud/pkg/multicloud/huawei"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type ModelartsResourceflavorsListOption struct {
PoolName string `help:"Pool Name"`
}
shellutils.R(&ModelartsResourceflavorsListOption{}, "modelarts-sku-list", "List Modelarts Pool", func(cli *huawei.SRegion, args *ModelartsResourceflavorsListOption) error {
resourceflavors, err := cli.GetClient().GetIModelartsPoolSku()
if err != nil {
return err
}
printList(resourceflavors, len(resourceflavors), 0, 0, nil)
return nil
})
}