diff --git a/pkg/apis/compute/elasticcache.go b/pkg/apis/compute/elasticcache.go index ed2de18aeb..aaa27e4f05 100644 --- a/pkg/apis/compute/elasticcache.go +++ b/pkg/apis/compute/elasticcache.go @@ -175,7 +175,7 @@ type ElasticcacheCreateInput struct { NetworkType string `json:"network_type"` // 弹性缓存Engine - // enum: redis, memcache + // enum: redis, memcached // required: true Engine string `json:"engine"` diff --git a/pkg/apis/compute/elasticcache_const.go b/pkg/apis/compute/elasticcache_const.go index b296c923e0..c9c411fe93 100644 --- a/pkg/apis/compute/elasticcache_const.go +++ b/pkg/apis/compute/elasticcache_const.go @@ -51,6 +51,8 @@ const ( ELASTIC_CACHE_SET_AUTO_RENEW = "set_auto_renew" //(设置自动续费) ELASTIC_CACHE_SET_AUTO_RENEW_FAILED = "set_auto_renew_failed" //(设置自动续费失败) + ELASTIC_CACHE_ENGINE_REDIS = "redis" + ELASTIC_CACHE_ENGINE_MEMCACHED = "memcached" ) const ( diff --git a/pkg/multicloud/qcloud/elasticcache_instance.go b/pkg/multicloud/qcloud/elasticcache_instance.go index c29d98d92e..8937425d01 100644 --- a/pkg/multicloud/qcloud/elasticcache_instance.go +++ b/pkg/multicloud/qcloud/elasticcache_instance.go @@ -357,7 +357,7 @@ func (self *SElasticcache) GetNodeType() string { } func (self *SElasticcache) GetEngine() string { - return "redis" + return api.ELASTIC_CACHE_ENGINE_REDIS } func (self *SElasticcache) GetEngineVersion() string { diff --git a/pkg/multicloud/qcloud/memcached.go b/pkg/multicloud/qcloud/memcached.go new file mode 100644 index 0000000000..fd70c09354 --- /dev/null +++ b/pkg/multicloud/qcloud/memcached.go @@ -0,0 +1,314 @@ +// 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 qcloud + +import ( + "fmt" + "strconv" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + billing_api "yunion.io/x/onecloud/pkg/apis/billing" + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" + "yunion.io/x/onecloud/pkg/util/billing" +) + +type SMemcached struct { + multicloud.SResourceBase + multicloud.QcloudTags + region *SRegion + + InstanceId string `json:"InstanceId"` + InstanceName string `json:"InstanceName"` + AppId int `json:"AppId"` + ProjectId int `json:"ProjectId"` + InstanceDesc string `json:"InstanceDesc"` + Vip string `json:"Vip"` + Vport int `json:"Vport"` + Status int `json:"Status"` + AutoRenewFlag int `json:"AutoRenewFlag"` + VpcId int `json:"VpcId"` + SubnetId int `json:"SubnetId"` + PayMode int `json:"PayMode"` + ZoneId int `json:"ZoneId"` + Expire int `json:"Expire"` + RegionId int `json:"RegionId"` + AddTimeStamp time.Time `json:"AddTimeStamp"` + ModtimeStamp time.Time `json:"ModTimeStamp"` + IsolateTimesSamp time.Time `json:"IsolateTimeStamp"` + UniqVpcId string `json:"UniqVpcId"` + UniqSubnetId string `json:"UniqSubnetId"` + DeadlineTimeStamp string `json:"DeadlineTimeStamp"` + SetId int `json:"SetId"` + CmemId int `json:"CmemId"` +} + +func (self *SMemcached) GetId() string { + return self.InstanceId +} + +func (self *SMemcached) GetName() string { + return self.InstanceName +} + +func (self *SMemcached) GetGlobalId() string { + return self.GetId() +} + +func (self *SMemcached) GetStatus() string { + switch self.Status { + case 0: + return api.ELASTIC_CACHE_STATUS_DEPLOYING + case 1: + return api.ELASTIC_CACHE_STATUS_RUNNING + case 2: + return api.ELASTIC_CACHE_STATUS_CREATE_FAILED + case 4, 5, 6, 7: + return api.ELASTIC_CACHE_STATUS_RELEASING + default: + return fmt.Sprintf("%d", self.Status) + } +} + +func (self *SMemcached) GetProjectId() string { + return strconv.Itoa(self.ProjectId) +} + +func (self *SMemcached) GetBillingType() string { + // 计费模式:0-按量计费,1-包年包月 + if self.PayMode == 1 { + return billing_api.BILLING_TYPE_PREPAID + } else { + return billing_api.BILLING_TYPE_POSTPAID + } +} + +func (self *SMemcached) GetCreatedAt() time.Time { + return self.AddTimeStamp +} + +func (self *SMemcached) GetExpiredAt() time.Time { + return time.Time{} +} + +func (self *SMemcached) SetAutoRenew(autoRenew bool) error { + return cloudprovider.ErrNotSupported +} + +func (self *SMemcached) IsAutoRenew() bool { + return self.AutoRenewFlag == 1 +} + +func (self *SMemcached) GetInstanceType() string { + return "" +} + +func (self *SMemcached) GetCapacityMB() int { + return 0 +} + +func (self *SMemcached) GetArchType() string { + return api.ELASTIC_CACHE_NODE_TYPE_SINGLE +} + +func (self *SMemcached) GetNodeType() string { + return api.ELASTIC_CACHE_ARCH_TYPE_SINGLE +} + +func (self *SMemcached) GetEngine() string { + return api.ELASTIC_CACHE_ENGINE_MEMCACHED +} + +func (self *SMemcached) GetEngineVersion() string { + return "latest" +} + +func (self *SMemcached) GetVpcId() string { + return self.UniqVpcId +} + +func (self *SMemcached) GetZoneId() string { + return fmt.Sprintf("%s/%s-%d", self.region.GetGlobalId(), self.region.Region, self.ZoneId%10) +} + +func (self *SMemcached) GetNetworkType() string { + if len(self.UniqSubnetId) > 0 { + return api.LB_NETWORK_TYPE_VPC + } + return api.LB_NETWORK_TYPE_CLASSIC +} + +func (self *SMemcached) GetNetworkId() string { + return self.UniqSubnetId +} + +func (self *SMemcached) GetPrivateDNS() string { + return "" +} + +func (self *SMemcached) GetPrivateIpAddr() string { + return self.Vip +} + +func (self *SMemcached) GetPrivateConnectPort() int { + return self.Vport +} + +func (self *SMemcached) GetPublicDNS() string { + return "" +} + +func (self *SMemcached) GetPublicIpAddr() string { + return "" +} + +func (self *SMemcached) GetPublicConnectPort() int { + return 0 +} + +func (self *SMemcached) GetMaintainStartTime() string { + return "" +} + +func (self *SMemcached) GetMaintainEndTime() string { + return "" +} + +func (self *SMemcached) GetAuthMode() string { + return "off" +} + +func (self *SMemcached) GetSecurityGroupIds() ([]string, error) { + return []string{}, nil +} + +func (self *SMemcached) GetICloudElasticcacheAccounts() ([]cloudprovider.ICloudElasticcacheAccount, error) { + return []cloudprovider.ICloudElasticcacheAccount{}, nil +} + +func (self *SMemcached) GetICloudElasticcacheAcls() ([]cloudprovider.ICloudElasticcacheAcl, error) { + return []cloudprovider.ICloudElasticcacheAcl{}, nil +} + +func (self *SMemcached) GetICloudElasticcacheBackups() ([]cloudprovider.ICloudElasticcacheBackup, error) { + return []cloudprovider.ICloudElasticcacheBackup{}, nil +} + +func (self *SMemcached) GetICloudElasticcacheParameters() ([]cloudprovider.ICloudElasticcacheParameter, error) { + return []cloudprovider.ICloudElasticcacheParameter{}, nil +} + +func (self *SMemcached) GetICloudElasticcacheAccount(accountId string) (cloudprovider.ICloudElasticcacheAccount, error) { + return nil, cloudprovider.ErrNotFound +} + +func (self *SMemcached) GetICloudElasticcacheAcl(aclId string) (cloudprovider.ICloudElasticcacheAcl, error) { + return nil, cloudprovider.ErrNotFound +} + +func (self *SMemcached) GetICloudElasticcacheBackup(backupId string) (cloudprovider.ICloudElasticcacheBackup, error) { + return nil, cloudprovider.ErrNotFound +} + +func (self *SMemcached) Restart() error { + return errors.Wrap(cloudprovider.ErrNotSupported, "Restart") +} + +func (self *SMemcached) Delete() error { + return cloudprovider.ErrNotSupported +} + +func (self *SMemcached) CleanupInstance() error { + return cloudprovider.ErrNotSupported +} + +func (self *SMemcached) ChangeInstanceSpec(spec string) error { + return cloudprovider.ErrNotSupported +} + +func (self *SMemcached) SetMaintainTime(maintainStartTime, maintainEndTime string) error { + return cloudprovider.ErrNotSupported +} + +func (self *SMemcached) AllocatePublicConnection(port int) (string, error) { + return "", cloudprovider.ErrNotSupported +} + +func (self *SMemcached) ReleasePublicConnection() error { + return errors.Wrap(cloudprovider.ErrNotSupported, "ReleasePublicConnection") +} + +func (self *SMemcached) CreateAccount(account cloudprovider.SCloudElasticCacheAccountInput) (cloudprovider.ICloudElasticcacheAccount, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SMemcached) CreateAcl(aclName, securityIps string) (cloudprovider.ICloudElasticcacheAcl, error) { + return nil, errors.Wrap(cloudprovider.ErrNotSupported, "CreateAcl") +} + +func (self *SMemcached) CreateBackup(desc string) (cloudprovider.ICloudElasticcacheBackup, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SMemcached) FlushInstance(input cloudprovider.SCloudElasticCacheFlushInstanceInput) error { + return cloudprovider.ErrNotSupported +} + +func (self *SMemcached) UpdateAuthMode(noPasswordAccess bool, password string) error { + return cloudprovider.ErrNotSupported +} + +func (self *SMemcached) UpdateInstanceParameters(config jsonutils.JSONObject) error { + return cloudprovider.ErrNotSupported +} + +func (self *SMemcached) UpdateBackupPolicy(config cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) error { + return cloudprovider.ErrNotSupported +} + +func (self *SMemcached) UpdateSecurityGroups(secgroupIds []string) error { + return cloudprovider.ErrNotSupported +} + +func (self *SMemcached) Renew(bc billing.SBillingCycle) error { + return cloudprovider.ErrNotSupported +} + +func (self *SRegion) GetMemcaches(ids []string, limit, offset int) ([]SMemcached, int, error) { + params := map[string]string{} + if limit <= 0 { + limit = 100 + } + params["Limit"] = fmt.Sprintf("%d", limit) + params["Offset"] = fmt.Sprintf("%d", offset) + for i, id := range ids { + params[fmt.Sprintf("InstanceIds.%d", i)] = id + } + resp, err := self.memcachedRequest("DescribeInstances", params) + if err != nil { + return nil, 0, errors.Wrapf(err, "DescribeInstances") + } + ret := []SMemcached{} + err = resp.Unmarshal(&ret, "InstanceList") + if err != nil { + return nil, 0, errors.Wrapf(err, "resp.Unmarshal InstanceList") + } + total, _ := resp.Float("TotalCount") + return ret, int(total), nil +} diff --git a/pkg/multicloud/qcloud/qcloud.go b/pkg/multicloud/qcloud/qcloud.go index c4d85a6c27..3be7384ad8 100644 --- a/pkg/multicloud/qcloud/qcloud.go +++ b/pkg/multicloud/qcloud/qcloud.go @@ -60,6 +60,7 @@ const ( QCLOUD_POSTGRES_API_VERSION = "2017-03-12" QCLOUD_SQLSERVER_API_VERSION = "2018-03-28" QCLOUD_REDIS_API_VERSION = "2018-04-12" + QCLOUD_MEMCACHED_API_VERSION = "2019-03-18" QCLOUD_SSL_API_VERSION = "2019-12-05" QCLOUD_CDN_API_VERSION = "2018-06-06" ) @@ -176,6 +177,12 @@ func redisRequest(client *common.Client, apiName string, params map[string]strin return _jsonRequest(client, domain, QCLOUD_REDIS_API_VERSION, apiName, params, debug, true) } +// memcached +func memcachedRequest(client *common.Client, apiName string, params map[string]string, debug bool) (jsonutils.JSONObject, error) { + domain := apiDomain("memcached", params) + return _jsonRequest(client, domain, QCLOUD_MEMCACHED_API_VERSION, apiName, params, debug, true) +} + // loadbalancer服务 api 3.0 func clbRequest(client *common.Client, apiName string, params map[string]string, debug bool) (jsonutils.JSONObject, error) { domain := apiDomain("clb", params) @@ -623,6 +630,15 @@ func (client *SQcloudClient) redisRequest(apiName string, params map[string]stri return redisRequest(cli, apiName, params, client.debug) } +func (client *SQcloudClient) memcachedRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + cli, err := client.getDefaultClient() + if err != nil { + return nil, err + } + + return memcachedRequest(cli, apiName, params, client.debug) +} + func (client *SQcloudClient) mariadbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { cli, err := client.getDefaultClient() if err != nil { diff --git a/pkg/multicloud/qcloud/region.go b/pkg/multicloud/qcloud/region.go index 1104d4f65d..fb3f8b264c 100644 --- a/pkg/multicloud/qcloud/region.go +++ b/pkg/multicloud/qcloud/region.go @@ -686,6 +686,11 @@ func (self *SRegion) redisRequest(apiName string, params map[string]string) (jso return self.client.redisRequest(apiName, params) } +func (self *SRegion) memcachedRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + params["Region"] = self.Region + return self.client.memcachedRequest(apiName, params) +} + func (self *SRegion) GetNetworks(ids []string, vpcId string, offset int, limit int) ([]SNetwork, int, error) { if limit > 50 || limit <= 0 { limit = 50 @@ -994,10 +999,41 @@ func (region *SRegion) GetIElasticcaches() ([]cloudprovider.ICloudElasticcache, ret = append(ret, &cache) } + mems := []SMemcached{} + offset := 0 + for { + part, total, err := region.GetMemcaches(nil, 100, offset) + if err != nil { + return nil, errors.Wrapf(err, "GetMemcaches") + } + mems = append(mems, part...) + if len(mems) >= total { + break + } + offset += len(part) + } + for i := range mems { + mems[i].region = region + ret = append(ret, &mems[i]) + } + return ret, nil } func (region *SRegion) GetIElasticcacheById(id string) (cloudprovider.ICloudElasticcache, error) { + if strings.HasPrefix(id, "cmem-") { + memcacheds, _, err := region.GetMemcaches([]string{id}, 1, 0) + if err != nil { + return nil, errors.Wrapf(err, "GetMemcaches") + } + for i := range memcacheds { + if memcacheds[i].GetGlobalId() == id { + memcacheds[i].region = region + return &memcacheds[i], nil + } + } + return nil, errors.Wrapf(cloudprovider.ErrNotFound, id) + } caches, err := region.GetCloudElasticcaches(id) if err != nil { return nil, errors.Wrap(err, "GetCloudElasticcaches") diff --git a/pkg/multicloud/qcloud/shell/memcached.go b/pkg/multicloud/qcloud/shell/memcached.go new file mode 100644 index 0000000000..35127ff8e2 --- /dev/null +++ b/pkg/multicloud/qcloud/shell/memcached.go @@ -0,0 +1,22 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/qcloud" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type MemcachedListOptions struct { + Ids []string + Offset int + Limit int + } + shellutils.R(&MemcachedListOptions{}, "memcached-list", "List memcached", func(cli *qcloud.SRegion, args *MemcachedListOptions) error { + memcacheds, _, err := cli.GetMemcaches(args.Ids, args.Limit, args.Offset) + if err != nil { + return err + } + printList(memcacheds, 0, 0, 0, []string{}) + return nil + }) +}