yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcs/redis.go (about)

     1  // Copyright 2019 Yunion
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package hcs
    16  
    17  import (
    18  	"fmt"
    19  	"net/url"
    20  	"strings"
    21  	"time"
    22  
    23  	"yunion.io/x/jsonutils"
    24  	"yunion.io/x/log"
    25  	"yunion.io/x/onecloud/pkg/util/billing"
    26  	"yunion.io/x/pkg/errors"
    27  
    28  	billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
    29  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    30  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    31  	"yunion.io/x/cloudmux/pkg/multicloud"
    32  )
    33  
    34  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423020.html
    35  type SElasticcache struct {
    36  	multicloud.SElasticcacheBase
    37  	HcsTags
    38  
    39  	region *SRegion
    40  
    41  	Name                  string   `json:"name"`
    42  	Engine                string   `json:"engine"`
    43  	CapacityGB            int      `json:"capacity"`
    44  	IP                    string   `json:"ip"`
    45  	DomainName            string   `json:"domainName"`
    46  	Port                  int      `json:"port"`
    47  	Status                string   `json:"status"`
    48  	Libos                 bool     `json:"libos"`
    49  	Description           string   `json:"description"`
    50  	Task                  string   `json:"task"`
    51  	MaxMemoryMB           int      `json:"max_memory"`
    52  	UsedMemoryMB          int      `json:"used_memory"`
    53  	InstanceId            string   `json:"instance_id"`
    54  	ResourceSpecCode      string   `json:"resource_spec_code"`
    55  	EngineVersion         string   `json:"engine_version"`
    56  	InternalVersion       string   `json:"internal_version"`
    57  	ChargingMode          int      `json:"charging_mode"`
    58  	CapacityMinor         string   `json:"capacity_minor"`
    59  	VpcId                 string   `json:"vpc_id"`
    60  	VpcName               string   `json:"vpc_name"`
    61  	TaskStatus            string   `json:"task_status"`
    62  	CreatedAt             string   `json:"created_at"`
    63  	ErrorCode             string   `json:"error_code"`
    64  	UserId                string   `json:"user_id"`
    65  	UserName              string   `json:"user_name"`
    66  	MaintainBegin         string   `json:"maintain_begin"`
    67  	MaintainEnd           string   `json:"maintain_end"`
    68  	NoPasswordAccess      string   `json:"no_password_access"`
    69  	AccessUser            string   `json:"access_user"`
    70  	EnablePublicip        bool     `json:"enable_publicip"`
    71  	PublicipId            string   `json:"publicip_id"`
    72  	PublicipAddress       string   `json:"publicip_address"`
    73  	EnableSSL             bool     `json:"enable_ssl"`
    74  	ServiceUpgrade        bool     `json:"service_upgrade"`
    75  	ServiceTaskId         string   `json:"service_task_id"`
    76  	IsFree                string   `json:"is_free"`
    77  	EnterpriseProjectId   string   `json:"enterprise_project_id"`
    78  	AvailableZones        []string `json:"available_zones"`
    79  	SubnetId              string   `json:"subnet_id"`
    80  	SecurityGroupId       string   `json:"security_group_id"`
    81  	BackendAddrs          []string `json:"backend_addrs"`
    82  	ProductId             string   `json:"product_id"`
    83  	SecurityGroupName     string   `json:"security_group_name"`
    84  	SubnetName            string   `json:"subnet_name"`
    85  	OrderId               string   `json:"order_id"`
    86  	SubnetCIdR            string   `json:"subnet_cidr"`
    87  	InstanceBackupPolicy  string   `json:"instance_backup_policy"`
    88  	EnterpriseProjectName string   `json:"enterprise_project_name"`
    89  }
    90  
    91  func (self *SElasticcache) GetId() string {
    92  	return self.InstanceId
    93  }
    94  
    95  func (self *SElasticcache) GetName() string {
    96  	return self.Name
    97  }
    98  
    99  func (self *SElasticcache) GetGlobalId() string {
   100  	return self.GetId()
   101  }
   102  
   103  func (self *SElasticcache) GetProjectId() string {
   104  	return self.EnterpriseProjectId
   105  }
   106  
   107  func (self *SElasticcache) Refresh() error {
   108  	cache, err := self.region.GetElasticCache(self.GetId())
   109  	if err != nil {
   110  		return errors.Wrap(err, "Elasticcache.Refresh.GetElasticCache")
   111  	}
   112  
   113  	err = jsonutils.Update(self, cache)
   114  	if err != nil {
   115  		return errors.Wrap(err, "Elasticcache.Refresh.Update")
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  func (self *SElasticcache) GetStatus() string {
   122  	switch self.Status {
   123  	case "RUNNING":
   124  		return api.ELASTIC_CACHE_STATUS_RUNNING
   125  	case "CREATING":
   126  		return api.ELASTIC_CACHE_STATUS_DEPLOYING
   127  	case "CREATEFAILED":
   128  		return api.ELASTIC_CACHE_STATUS_CREATE_FAILED
   129  	case "ERROR":
   130  		return api.ELASTIC_CACHE_STATUS_ERROR
   131  	case "RESTARTING":
   132  		return api.ELASTIC_CACHE_STATUS_RESTARTING
   133  	case "FROZEN":
   134  		return api.ELASTIC_CACHE_STATUS_UNAVAILABLE
   135  	case "EXTENDING":
   136  		return api.ELASTIC_CACHE_STATUS_CHANGING
   137  	case "RESTORING":
   138  		return api.ELASTIC_CACHE_STATUS_TRANSFORMING // ?
   139  	case "FLUSHING":
   140  		return api.ELASTIC_CACHE_STATUS_FLUSHING
   141  	}
   142  
   143  	return ""
   144  }
   145  
   146  func (self *SElasticcache) GetBillingType() string {
   147  	return billing_api.BILLING_TYPE_POSTPAID
   148  }
   149  
   150  func (self *SElasticcache) GetCreatedAt() time.Time {
   151  	var createtime time.Time
   152  	if len(self.CreatedAt) > 0 {
   153  		createtime, _ = time.Parse("2006-01-02T15:04:05.000Z", self.CreatedAt)
   154  	}
   155  
   156  	return createtime
   157  }
   158  
   159  func (self *SElasticcache) GetInstanceType() string {
   160  	return self.ResourceSpecCode
   161  }
   162  
   163  func (self *SElasticcache) GetCapacityMB() int {
   164  	return self.CapacityGB * 1024
   165  }
   166  
   167  func (self *SElasticcache) GetArchType() string {
   168  	if strings.Contains(self.ResourceSpecCode, "single") {
   169  		return api.ELASTIC_CACHE_ARCH_TYPE_SINGLE
   170  	}
   171  	if strings.Contains(self.ResourceSpecCode, "ha") {
   172  		return api.ELASTIC_CACHE_ARCH_TYPE_MASTER
   173  	}
   174  	if strings.Contains(self.ResourceSpecCode, "cluster") {
   175  		return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER
   176  	}
   177  	if strings.Contains(self.ResourceSpecCode, "proxy") {
   178  		return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER
   179  	}
   180  	return ""
   181  }
   182  
   183  func (self *SElasticcache) GetNodeType() string {
   184  	// single(单副本) | double(双副本)
   185  	if strings.Contains(self.ResourceSpecCode, "single") {
   186  		return "single"
   187  	}
   188  	return "double"
   189  }
   190  
   191  func (self *SElasticcache) GetEngine() string {
   192  	return self.Engine
   193  }
   194  
   195  func (self *SElasticcache) GetEngineVersion() string {
   196  	return self.EngineVersion
   197  }
   198  
   199  func (self *SElasticcache) GetVpcId() string {
   200  	return self.VpcId
   201  }
   202  
   203  func (self *SElasticcache) GetZoneId() string {
   204  	if len(self.AvailableZones) > 0 {
   205  		zone, err := self.region.GetZoneById(self.AvailableZones[0])
   206  		if err != nil {
   207  			log.Errorf("elasticcache.GetZoneId %s", err)
   208  			return ""
   209  		}
   210  		return zone.GetGlobalId()
   211  	}
   212  	return ""
   213  }
   214  
   215  func (self *SElasticcache) GetNetworkType() string {
   216  	return api.LB_NETWORK_TYPE_VPC
   217  }
   218  
   219  func (self *SElasticcache) GetNetworkId() string {
   220  	return self.SubnetId
   221  }
   222  
   223  func (self *SElasticcache) GetPrivateDNS() string {
   224  	return self.DomainName
   225  }
   226  
   227  func (self *SElasticcache) GetPrivateIpAddr() string {
   228  	return self.IP
   229  }
   230  
   231  func (self *SElasticcache) GetPrivateConnectPort() int {
   232  	return self.Port
   233  }
   234  
   235  func (self *SElasticcache) GetPublicDNS() string {
   236  	return self.PublicipAddress
   237  }
   238  
   239  func (self *SElasticcache) GetPublicIpAddr() string {
   240  	return self.PublicipAddress
   241  }
   242  
   243  func (self *SElasticcache) GetPublicConnectPort() int {
   244  	return self.Port
   245  }
   246  
   247  func (self *SElasticcache) GetMaintainStartTime() string {
   248  	return self.MaintainBegin
   249  }
   250  
   251  func (self *SElasticcache) GetMaintainEndTime() string {
   252  	return self.MaintainEnd
   253  }
   254  
   255  func (self *SElasticcache) GetICloudElasticcacheAccounts() ([]cloudprovider.ICloudElasticcacheAccount, error) {
   256  	iaccounts := []cloudprovider.ICloudElasticcacheAccount{}
   257  	iaccount := &SElasticcacheAccount{cacheDB: self}
   258  	iaccounts = append(iaccounts, iaccount)
   259  	return iaccounts, nil
   260  }
   261  
   262  func (self *SElasticcache) GetICloudElasticcacheAcls() ([]cloudprovider.ICloudElasticcacheAcl, error) {
   263  	// 华为云使用安全组做访问控制。目前未支持
   264  	return []cloudprovider.ICloudElasticcacheAcl{}, nil
   265  }
   266  
   267  func (self *SElasticcache) GetICloudElasticcacheBackups() ([]cloudprovider.ICloudElasticcacheBackup, error) {
   268  	start := self.GetCreatedAt().Format("20060102150405")
   269  	end := time.Now().Format("20060102150405")
   270  	backups, err := self.region.GetElasticCacheBackups(self.GetId(), start, end)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  
   275  	ibackups := make([]cloudprovider.ICloudElasticcacheBackup, len(backups))
   276  	for i := range backups {
   277  		backups[i].cacheDB = self
   278  		ibackups[i] = &backups[i]
   279  	}
   280  
   281  	return ibackups, nil
   282  }
   283  
   284  func (self *SElasticcache) GetICloudElasticcacheParameters() ([]cloudprovider.ICloudElasticcacheParameter, error) {
   285  	parameters, err := self.region.GetElasticCacheParameters(self.GetId())
   286  	if err != nil {
   287  		return nil, err
   288  	}
   289  
   290  	iparameters := make([]cloudprovider.ICloudElasticcacheParameter, len(parameters))
   291  	for i := range parameters {
   292  		parameters[i].cacheDB = self
   293  		iparameters[i] = &parameters[i]
   294  	}
   295  
   296  	return iparameters, nil
   297  }
   298  
   299  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423035.html
   300  func (self *SRegion) GetElasticCacheBackups(instanceId, startTime, endTime string) ([]SElasticcacheBackup, error) {
   301  	params := url.Values{}
   302  	params.Set("beginTime", startTime)
   303  	params.Set("endTime", endTime)
   304  	res := fmt.Sprintf("instances/%s/backups", instanceId)
   305  	ret := []SElasticcacheBackup{}
   306  	return ret, self.redisList(res, params, &ret)
   307  }
   308  
   309  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423027.html
   310  func (self *SRegion) GetElasticCacheParameters(instanceId string) ([]SElasticcacheParameter, error) {
   311  	params := url.Values{}
   312  	res := fmt.Sprintf("instances/%s/configs", instanceId)
   313  	ret := []SElasticcacheParameter{}
   314  	return ret, self.redisList(res, params, &ret)
   315  }
   316  
   317  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423044.html
   318  func (self *SRegion) GetElasticCaches() ([]SElasticcache, error) {
   319  	params := url.Values{}
   320  	ret := []SElasticcache{}
   321  	return ret, self.redisList("instances", params, &ret)
   322  }
   323  
   324  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423020.html
   325  func (self *SRegion) GetElasticCache(instanceId string) (*SElasticcache, error) {
   326  	cache := &SElasticcache{region: self}
   327  	res := fmt.Sprintf("instances/%s", instanceId)
   328  	return cache, self.redisGet(res, cache)
   329  }
   330  
   331  func (self *SRegion) GetIElasticcacheById(id string) (cloudprovider.ICloudElasticcache, error) {
   332  	ec, err := self.GetElasticCache(id)
   333  	if err != nil {
   334  		return nil, errors.Wrapf(err, "GetElasticCache(%s)", id)
   335  	}
   336  	return ec, nil
   337  }
   338  
   339  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423047.html
   340  func (self *SRegion) zoneNameToDcsZoneIds(zoneIds []string) ([]string, error) {
   341  	type Z struct {
   342  		Id                   string `json:"id"`
   343  		Code                 string `json:"code"`
   344  		Name                 string `json:"name"`
   345  		Port                 string `json:"port"`
   346  		ResourceAvailability string `json:"resource_availability"`
   347  	}
   348  
   349  	rs := []Z{}
   350  	err := self.redisList("availableZones", url.Values{}, &rs)
   351  	if err != nil {
   352  		return nil, errors.Wrapf(err, "availableZones")
   353  	}
   354  	zoneMap := map[string]string{}
   355  	for i := range rs {
   356  		if rs[i].ResourceAvailability == "true" {
   357  			zoneMap[rs[i].Code] = rs[i].Id
   358  		}
   359  	}
   360  
   361  	ret := []string{}
   362  	for _, zone := range zoneIds {
   363  		if id, ok := zoneMap[zone]; ok {
   364  			ret = append(ret, id)
   365  		} else {
   366  			return nil, errors.Wrap(fmt.Errorf("zone %s not found or not available", zone), "region.zoneNameToDcsZoneIds")
   367  		}
   368  	}
   369  
   370  	return ret, nil
   371  }
   372  
   373  func (self *SRegion) CreateElasticcache(opts *cloudprovider.SCloudElasticCacheInput) (*SElasticcache, error) {
   374  	params := map[string]interface{}{
   375  		"name":               opts.InstanceName,
   376  		"engine":             opts.Engine,
   377  		"engine_version":     opts.EngineVersion,
   378  		"capacity":           opts.CapacityGB,
   379  		"vpc_id":             opts.VpcId,
   380  		"subnet_id":          opts.NetworkId,
   381  		"product_id":         opts.InstanceType,
   382  		"spec_code":          opts.InstanceType,
   383  		"available_zones":    opts.ZoneIds,
   384  		"no_password_access": true,
   385  		"enable_publicip":    false,
   386  	}
   387  	if len(opts.SecurityGroupIds) > 0 {
   388  		params["security_group_id"] = opts.SecurityGroupIds[0]
   389  	}
   390  
   391  	if len(opts.ProjectId) > 0 {
   392  		params["enterprise_project_id"] = opts.ProjectId
   393  	}
   394  
   395  	if len(opts.Password) > 0 {
   396  		params["no_password_access"] = false
   397  		params["password"] = opts.Password
   398  		if opts.Engine == "Memcache" {
   399  			params["access_user"] = opts.UserName
   400  		}
   401  	}
   402  
   403  	if len(opts.EipId) > 0 {
   404  		params["enable_publicip"] = true
   405  		params["publicip_id"] = opts.EipId
   406  	}
   407  
   408  	if len(opts.PrivateIpAddress) > 0 {
   409  		params["private_ip"] = opts.PrivateIpAddress
   410  	}
   411  
   412  	if len(opts.MaintainBegin) > 0 {
   413  		params["maintain_begin"] = opts.MaintainBegin
   414  		params["maintain_end"] = opts.MaintainEnd
   415  	}
   416  
   417  	ret := struct {
   418  		InstanceId string
   419  	}{}
   420  	err := self.redisCreate("instances", params, &ret)
   421  	if err != nil {
   422  		return nil, errors.Wrap(err, "redisCreate")
   423  	}
   424  	return self.GetElasticCache(ret.InstanceId)
   425  }
   426  
   427  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423019.html
   428  func (self *SRegion) CreateIElasticcaches(opts *cloudprovider.SCloudElasticCacheInput) (cloudprovider.ICloudElasticcache, error) {
   429  	cache, err := self.CreateElasticcache(opts)
   430  	if err != nil {
   431  		return nil, err
   432  	}
   433  	return cache, nil
   434  }
   435  
   436  func (self *SElasticcache) Restart() error {
   437  	return nil
   438  }
   439  
   440  func (self *SElasticcache) Delete() error {
   441  	return self.region.DeleteElasticcache(self.InstanceId)
   442  }
   443  
   444  func (self *SRegion) DeleteElasticcache(id string) error {
   445  	res := fmt.Sprintf("instances/%s", id)
   446  	return self.redisDelete(res)
   447  }
   448  
   449  func (self *SElasticcache) ChangeInstanceSpec(spec string) error {
   450  	return cloudprovider.ErrNotImplemented
   451  }
   452  
   453  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423021.html
   454  func (self *SElasticcache) SetMaintainTime(maintainStartTime, maintainEndTime string) error {
   455  	return cloudprovider.ErrNotImplemented
   456  }
   457  
   458  // https://support.huaweicloud.com/usermanual-dcs/dcs-zh-ug-180314001.html
   459  // 目前只有Redis3.0版本密码模式的实例支持通过公网访问Redis实例,其他版本暂不支持公网访问。
   460  // todo: 目前没找到api
   461  func (self *SElasticcache) AllocatePublicConnection(port int) (string, error) {
   462  	return "", cloudprovider.ErrNotSupported
   463  }
   464  
   465  // todo: 目前没找到api
   466  func (self *SElasticcache) ReleasePublicConnection() error {
   467  	return cloudprovider.ErrNotSupported
   468  }
   469  
   470  func (self *SElasticcache) CreateAccount(account cloudprovider.SCloudElasticCacheAccountInput) (cloudprovider.ICloudElasticcacheAccount, error) {
   471  	return nil, cloudprovider.ErrNotSupported
   472  }
   473  
   474  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423031.html
   475  
   476  func (self *SElasticcache) CreateAcl(aclName, securityIps string) (cloudprovider.ICloudElasticcacheAcl, error) {
   477  	return nil, cloudprovider.ErrNotSupported
   478  }
   479  
   480  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423029.html
   481  func (self *SElasticcache) UpdateInstanceParameters(config jsonutils.JSONObject) error {
   482  	return cloudprovider.ErrNotImplemented
   483  }
   484  
   485  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423033.html
   486  func (self *SElasticcache) CreateBackup(desc string) (cloudprovider.ICloudElasticcacheBackup, error) {
   487  	return nil, cloudprovider.ErrNotSupported
   488  }
   489  
   490  func backupPeriodTrans(config cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) *jsonutils.JSONArray {
   491  	segs := strings.Split(config.PreferredBackupPeriod, ",")
   492  	ret := jsonutils.NewArray()
   493  	for _, seg := range segs {
   494  		switch seg {
   495  		case "Monday":
   496  			ret.Add(jsonutils.NewString("1"))
   497  		case "Tuesday":
   498  			ret.Add(jsonutils.NewString("2"))
   499  		case "Wednesday":
   500  			ret.Add(jsonutils.NewString("3"))
   501  		case "Thursday":
   502  			ret.Add(jsonutils.NewString("4"))
   503  		case "Friday":
   504  			ret.Add(jsonutils.NewString("5"))
   505  		case "Saturday":
   506  			ret.Add(jsonutils.NewString("6"))
   507  		case "Sunday":
   508  			ret.Add(jsonutils.NewString("7"))
   509  		}
   510  	}
   511  
   512  	return ret
   513  }
   514  
   515  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423021.html
   516  func (self *SElasticcache) UpdateBackupPolicy(config cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) error {
   517  	return nil
   518  }
   519  
   520  // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423030.html
   521  // 当前版本,只有DCS2.0实例支持清空数据功能,即flush操作。
   522  func (self *SElasticcache) FlushInstance(input cloudprovider.SCloudElasticCacheFlushInstanceInput) error {
   523  	return nil
   524  }
   525  
   526  // SElasticcacheAccount => ResetPassword
   527  func (self *SElasticcache) UpdateAuthMode(noPwdAccess bool, password string) error {
   528  	return cloudprovider.ErrNotSupported
   529  }
   530  
   531  func (self *SElasticcache) GetAuthMode() string {
   532  	switch self.NoPasswordAccess {
   533  	case "true":
   534  		return "off"
   535  	default:
   536  		return "on"
   537  	}
   538  }
   539  
   540  func (self *SElasticcache) GetSecurityGroupIds() ([]string, error) {
   541  	return nil, cloudprovider.ErrNotSupported
   542  }
   543  
   544  func (self *SElasticcache) GetICloudElasticcacheAccount(accountId string) (cloudprovider.ICloudElasticcacheAccount, error) {
   545  	accounts, err := self.GetICloudElasticcacheAccounts()
   546  	if err != nil {
   547  		return nil, errors.Wrap(err, "Elasticcache.GetICloudElasticcacheAccount.Accounts")
   548  	}
   549  
   550  	for i := range accounts {
   551  		account := accounts[i]
   552  		if account.GetGlobalId() == accountId {
   553  			return account, nil
   554  		}
   555  	}
   556  
   557  	return nil, cloudprovider.ErrNotFound
   558  }
   559  
   560  func (self *SElasticcache) GetICloudElasticcacheAcl(aclId string) (cloudprovider.ICloudElasticcacheAcl, error) {
   561  	return nil, cloudprovider.ErrNotSupported
   562  }
   563  
   564  func (self *SElasticcache) GetICloudElasticcacheBackup(backupId string) (cloudprovider.ICloudElasticcacheBackup, error) {
   565  	backups, err := self.GetICloudElasticcacheBackups()
   566  	if err != nil {
   567  		return nil, err
   568  	}
   569  
   570  	for _, backup := range backups {
   571  		if backup.GetId() == backupId {
   572  			return backup, nil
   573  		}
   574  	}
   575  
   576  	return nil, cloudprovider.ErrNotFound
   577  }
   578  
   579  func (instance *SElasticcache) SetTags(tags map[string]string, replace bool) error {
   580  	return cloudprovider.ErrNotImplemented
   581  }
   582  
   583  func (self *SElasticcache) UpdateSecurityGroups(secgroupIds []string) error {
   584  	return errors.Wrap(cloudprovider.ErrNotSupported, "UpdateSecurityGroups")
   585  }
   586  
   587  func (self *SElasticcache) Renew(bc billing.SBillingCycle) error {
   588  	return cloudprovider.ErrNotSupported
   589  }
   590  
   591  func (self *SElasticcache) SetAutoRenew(bc billing.SBillingCycle) error {
   592  	return cloudprovider.ErrNotSupported
   593  }
   594  
   595  func (self *SRegion) GetIElasticcaches() ([]cloudprovider.ICloudElasticcache, error) {
   596  	caches, err := self.GetElasticCaches()
   597  	if err != nil {
   598  		return nil, err
   599  	}
   600  
   601  	ret := []cloudprovider.ICloudElasticcache{}
   602  	for i := range caches {
   603  		caches[i].region = self
   604  		ret = append(ret, &caches[i])
   605  	}
   606  
   607  	return ret, nil
   608  }