diff --git a/cmd/climc/shell/compute/modelarts_pool.go b/cmd/climc/shell/compute/modelarts_pool.go new file mode 100644 index 0000000000..12c6c20a3e --- /dev/null +++ b/cmd/climc/shell/compute/modelarts_pool.go @@ -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{}) +} diff --git a/cmd/climc/shell/compute/modelarts_pool_sku.go b/cmd/climc/shell/compute/modelarts_pool_sku.go new file mode 100644 index 0000000000..5e8e23363f --- /dev/null +++ b/cmd/climc/shell/compute/modelarts_pool_sku.go @@ -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{}) +} diff --git a/pkg/apis/compute/modelarts_pool.go b/pkg/apis/compute/modelarts_pool.go new file mode 100644 index 0000000000..21a4108aba --- /dev/null +++ b/pkg/apis/compute/modelarts_pool.go @@ -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 { +} diff --git a/pkg/apis/compute/modelarts_pool_sku.go b/pkg/apis/compute/modelarts_pool_sku.go new file mode 100644 index 0000000000..0a7ffe4d43 --- /dev/null +++ b/pkg/apis/compute/modelarts_pool_sku.go @@ -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 +} diff --git a/pkg/cloudmon/providerdriver/base.go b/pkg/cloudmon/providerdriver/base.go index 8b402c7168..e86949b94a 100644 --- a/pkg/cloudmon/providerdriver/base.go +++ b/pkg/cloudmon/providerdriver/base.go @@ -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 } diff --git a/pkg/cloudmon/providerdriver/driver.go b/pkg/cloudmon/providerdriver/driver.go index 305d17ff73..fecd6925c5 100644 --- a/pkg/cloudmon/providerdriver/driver.go +++ b/pkg/cloudmon/providerdriver/driver.go @@ -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) { diff --git a/pkg/cloudmon/providerdriver/huawei.go b/pkg/cloudmon/providerdriver/huawei.go index f8250f39b8..1c9f3eff08 100644 --- a/pkg/cloudmon/providerdriver/huawei.go +++ b/pkg/cloudmon/providerdriver/huawei.go @@ -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) +} diff --git a/pkg/cloudmon/resources/resources.go b/pkg/cloudmon/resources/resources.go index cd4ce54a37..5384d94c02 100644 --- a/pkg/cloudmon/resources/resources.go +++ b/pkg/cloudmon/resources/resources.go @@ -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() diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index f98650abb8..9728e2c7be 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -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 +} diff --git a/pkg/cloudprovider/consts.go b/pkg/cloudprovider/consts.go index 57a7e92fbc..aac8332d77 100644 --- a/pkg/cloudprovider/consts.go +++ b/pkg/cloudprovider/consts.go @@ -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 ( diff --git a/pkg/cloudprovider/metrics.go b/pkg/cloudprovider/metrics.go index 80d459f64c..cb81bc1a70 100644 --- a/pkg/cloudprovider/metrics.go +++ b/pkg/cloudprovider/metrics.go @@ -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 ( diff --git a/pkg/cloudprovider/modelarts_pool.go b/pkg/cloudprovider/modelarts_pool.go new file mode 100644 index 0000000000..1769785d04 --- /dev/null +++ b/pkg/cloudprovider/modelarts_pool.go @@ -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"` +} diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 7b92e4ff28..6d1ce20100 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -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 { diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index 077aed563d..cd9c6125ff 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -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 +} diff --git a/pkg/compute/models/modelarts_pool_sku.go b/pkg/compute/models/modelarts_pool_sku.go new file mode 100644 index 0000000000..54b55344ee --- /dev/null +++ b/pkg/compute/models/modelarts_pool_sku.go @@ -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 +} diff --git a/pkg/compute/models/modelarts_pools.go b/pkg/compute/models/modelarts_pools.go new file mode 100644 index 0000000000..64c6a9a295 --- /dev/null +++ b/pkg/compute/models/modelarts_pools.go @@ -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 +} diff --git a/pkg/compute/models/purge.go b/pkg/compute/models/purge.go index 22d3488519..a43adf776c 100644 --- a/pkg/compute/models/purge.go +++ b/pkg/compute/models/purge.go @@ -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 +} diff --git a/pkg/compute/service/handlers.go b/pkg/compute/service/handlers.go index 6442a73c91..e8807d338c 100644 --- a/pkg/compute/service/handlers.go +++ b/pkg/compute/service/handlers.go @@ -247,6 +247,9 @@ func InitHandlers(app *appsrv.Application) { models.NetTapServiceManager, models.NetTapFlowManager, + + models.ModelartsPoolManager, + models.ModelartsPoolSkuManager, } { db.RegisterModelManager(manager) handler := db.NewModelHandler(manager) diff --git a/pkg/compute/tasks/modelarts_pool_create_task.go b/pkg/compute/tasks/modelarts_pool_create_task.go new file mode 100644 index 0000000000..6a693688c2 --- /dev/null +++ b/pkg/compute/tasks/modelarts_pool_create_task.go @@ -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()) +} diff --git a/pkg/compute/tasks/modelarts_pool_delete_task.go b/pkg/compute/tasks/modelarts_pool_delete_task.go new file mode 100644 index 0000000000..91e6c52d08 --- /dev/null +++ b/pkg/compute/tasks/modelarts_pool_delete_task.go @@ -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) +} diff --git a/pkg/compute/tasks/modelarts_syncstatus_task.go b/pkg/compute/tasks/modelarts_syncstatus_task.go new file mode 100644 index 0000000000..8a616b5d75 --- /dev/null +++ b/pkg/compute/tasks/modelarts_syncstatus_task.go @@ -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) +} diff --git a/pkg/hostman/hostdeployer/apis/deploy.pb.go b/pkg/hostman/hostdeployer/apis/deploy.pb.go index eefa50cf35..3afd9ad113 100644 --- a/pkg/hostman/hostdeployer/apis/deploy.pb.go +++ b/pkg/hostman/hostdeployer/apis/deploy.pb.go @@ -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 ( diff --git a/pkg/hostman/hostdeployer/apis/deploy_grpc.pb.go b/pkg/hostman/hostdeployer/apis/deploy_grpc.pb.go index c66aa6146a..8bfbd6bf12 100644 --- a/pkg/hostman/hostdeployer/apis/deploy_grpc.pb.go +++ b/pkg/hostman/hostdeployer/apis/deploy_grpc.pb.go @@ -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" diff --git a/pkg/mcclient/modules/compute/mod_modelart_pool_sku.go b/pkg/mcclient/modules/compute/mod_modelart_pool_sku.go new file mode 100644 index 0000000000..39e633c80a --- /dev/null +++ b/pkg/mcclient/modules/compute/mod_modelart_pool_sku.go @@ -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) +} diff --git a/pkg/mcclient/modules/compute/mod_modelart_pools.go b/pkg/mcclient/modules/compute/mod_modelart_pools.go new file mode 100644 index 0000000000..8eb7c495d7 --- /dev/null +++ b/pkg/mcclient/modules/compute/mod_modelart_pools.go @@ -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) +} diff --git a/pkg/mcclient/options/compute/modelarts_pool.go b/pkg/mcclient/options/compute/modelarts_pool.go new file mode 100644 index 0000000000..17e4488037 --- /dev/null +++ b/pkg/mcclient/options/compute/modelarts_pool.go @@ -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) +} diff --git a/pkg/mcclient/options/compute/modelarts_pool_sku.go b/pkg/mcclient/options/compute/modelarts_pool_sku.go new file mode 100644 index 0000000000..52b07c1603 --- /dev/null +++ b/pkg/mcclient/options/compute/modelarts_pool_sku.go @@ -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) +} diff --git a/pkg/multicloud/huawei/client/modules/manager_base.go b/pkg/multicloud/huawei/client/modules/manager_base.go index fb3ac600d0..a8763415f2 100644 --- a/pkg/multicloud/huawei/client/modules/manager_base.go +++ b/pkg/multicloud/huawei/client/modules/manager_base.go @@ -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 { diff --git a/pkg/multicloud/huawei/client/modules/manager_resource.go b/pkg/multicloud/huawei/client/modules/manager_resource.go index a109ad29de..927a514bbb 100644 --- a/pkg/multicloud/huawei/client/modules/manager_resource.go +++ b/pkg/multicloud/huawei/client/modules/manager_resource.go @@ -52,6 +52,8 @@ const ( ServiceNameCES ServiceNameType = "ces" // 监控服务 CloudEye ServiceNameEPS ServiceNameType = "eps" // 企业项目 + ServiceNameModelarts ServiceNameType = "modelarts" // AI ModelArts + ServiceNameSFSTurbo ServiceNameType = "sfs-turbo" // 文件系统 ) diff --git a/pkg/multicloud/huawei/huawei.go b/pkg/multicloud/huawei/huawei.go index e0d4b62a95..69b12878a1 100644 --- a/pkg/multicloud/huawei/huawei.go +++ b/pkg/multicloud/huawei/huawei.go @@ -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 +} diff --git a/pkg/multicloud/huawei/modelarts_pool.go b/pkg/multicloud/huawei/modelarts_pool.go new file mode 100644 index 0000000000..89dc73b105 --- /dev/null +++ b/pkg/multicloud/huawei/modelarts_pool.go @@ -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, ",") +} diff --git a/pkg/multicloud/huawei/modelarts_pool_sku.go b/pkg/multicloud/huawei/modelarts_pool_sku.go new file mode 100644 index 0000000000..40c057d37d --- /dev/null +++ b/pkg/multicloud/huawei/modelarts_pool_sku.go @@ -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 +} diff --git a/pkg/multicloud/huawei/monitor.go b/pkg/multicloud/huawei/monitor.go index bb30dd99a9..c3757de967 100644 --- a/pkg/multicloud/huawei/monitor.go +++ b/pkg/multicloud/huawei/monitor.go @@ -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) } diff --git a/pkg/multicloud/huawei/provider/provider.go b/pkg/multicloud/huawei/provider/provider.go index 1cf6343438..e226f79e0c 100644 --- a/pkg/multicloud/huawei/provider/provider.go +++ b/pkg/multicloud/huawei/provider/provider.go @@ -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() +} diff --git a/pkg/multicloud/huawei/shell/modelarts_pool.go b/pkg/multicloud/huawei/shell/modelarts_pool.go new file mode 100644 index 0000000000..c72f3c0fd4 --- /dev/null +++ b/pkg/multicloud/huawei/shell/modelarts_pool.go @@ -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 + }) +} diff --git a/pkg/multicloud/huawei/shell/modelarts_pool_sku.go b/pkg/multicloud/huawei/shell/modelarts_pool_sku.go new file mode 100644 index 0000000000..cfcd24e0ca --- /dev/null +++ b/pkg/multicloud/huawei/shell/modelarts_pool_sku.go @@ -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 + }) +}