yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/elasticcache_instance.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 qcloud
    16  
    17  import (
    18  	"fmt"
    19  	"strconv"
    20  	"strings"
    21  	"time"
    22  
    23  	"github.com/pkg/errors"
    24  
    25  	"yunion.io/x/jsonutils"
    26  	"yunion.io/x/log"
    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  	"yunion.io/x/onecloud/pkg/util/billing"
    33  )
    34  
    35  type SElasticcache struct {
    36  	multicloud.SElasticcacheBase
    37  	QcloudTags
    38  	region *SRegion
    39  
    40  	ClientLimit      int
    41  	ClientLimitMax   int
    42  	ClientLimitMin   int
    43  	MaintenanceTime  *MaintenanceTime `json:"maintenance_time"`
    44  	NoAuth           *bool            `json:"no_auth"`
    45  	NodeSet          []NodeSet        `json:"NodeSet"`
    46  	Appid            int64            `json:"Appid"`
    47  	AutoRenewFlag    int64            `json:"AutoRenewFlag"`
    48  	BillingMode      int64            `json:"BillingMode"`
    49  	CloseTime        string           `json:"CloseTime"`
    50  	Createtime       time.Time        `json:"Createtime"`
    51  	DeadlineTime     string           `json:"DeadlineTime"`
    52  	Engine           string           `json:"Engine"`
    53  	InstanceID       string           `json:"InstanceId"`
    54  	InstanceName     string           `json:"InstanceName"`
    55  	InstanceNode     []interface{}    `json:"InstanceNode"`
    56  	InstanceTitle    string           `json:"InstanceTitle"`
    57  	OfflineTime      string           `json:"OfflineTime"`
    58  	Port             int              `json:"Port"`
    59  	PriceID          int64            `json:"PriceId"`
    60  	ProductType      string           `json:"ProductType"`
    61  	ProjectID        int              `json:"ProjectId"`
    62  	RedisReplicasNum int              `json:"RedisReplicasNum"`
    63  	RedisShardNum    int64            `json:"RedisShardNum"`
    64  	RedisShardSize   int64            `json:"RedisShardSize"` // MB
    65  	DiskSize         int64            `json:"disk_size"`
    66  	RegionID         int64            `json:"RegionId"`
    67  	Size             int              `json:"Size"`
    68  	SizeUsed         int64            `json:"SizeUsed"`
    69  	SlaveReadWeight  int64            `json:"SlaveReadWeight"`
    70  	Status           int              `json:"Status"`
    71  	SubStatus        int64            `json:"SubStatus"`
    72  	SubnetID         int64            `json:"SubnetId"`
    73  	Type             int              `json:"Type"`
    74  	UniqSubnetID     string           `json:"UniqSubnetId"`
    75  	UniqVpcID        string           `json:"UniqVpcId"`
    76  	VpcID            int64            `json:"VpcId"`
    77  	WANIP            string           `json:"WanIp"`
    78  	ZoneID           int              `json:"ZoneId"`
    79  }
    80  
    81  func (self *SElasticcache) SetTags(tags map[string]string, replace bool) error {
    82  	return self.region.SetResourceTags("redis", "instance", []string{self.InstanceID}, tags, replace)
    83  }
    84  
    85  type MaintenanceTime struct {
    86  	StartTime string `json:"start_time"`
    87  	EndTime   string `json:"end_time"`
    88  }
    89  
    90  type NodeSet struct {
    91  	NodeID   int64 `json:"NodeId"`
    92  	NodeType int64 `json:"NodeType"`
    93  	ZoneID   int64 `json:"ZoneId"`
    94  }
    95  
    96  var zoneMaps = map[int]string{
    97  	100001: "ap-guangzhou-1",
    98  	100002: "ap-guangzhou-2",
    99  	100003: "ap-guangzhou-3",
   100  	100004: "ap-guangzhou-4",
   101  	100006: "ap-guangzhou-6",
   102  	110001: "ap-shenzhen-fsi-1",
   103  	110002: "ap-shenzhen-fsi-2",
   104  	110003: "ap-shenzhen-fsi-3",
   105  	200001: "ap-shanghai-1",
   106  	200002: "ap-shanghai-2",
   107  	200003: "ap-shanghai-3",
   108  	200004: "ap-shanghai-4",
   109  	200005: "ap-shanghai-5",
   110  	200006: "ap-shanghai-6",
   111  	200007: "ap-shanghai-7",
   112  	700001: "ap-shanghai-fsi-1",
   113  	700002: "ap-shanghai-fsi-2",
   114  	700003: "ap-shanghai-fsi-3",
   115  	330001: "ap-nanjing-1",
   116  	330002: "ap-nanjing-2",
   117  	330003: "ap-nanjing-3",
   118  	800001: "ap-beijing-1",
   119  	800002: "ap-beijing-2",
   120  	800003: "ap-beijing-3",
   121  	800004: "ap-beijing-4",
   122  	800005: "ap-beijing-5",
   123  	800006: "ap-beijing-6",
   124  	800007: "ap-beijing-7",
   125  	460001: "ap-beijing-fsi-1",
   126  	360001: "ap-tianjin-1",
   127  	360002: "ap-tianjin-2",
   128  	160001: "ap-chengdu-1",
   129  	160002: "ap-chengdu-2",
   130  	190001: "ap-chongqing-1",
   131  	300001: "ap-hongkong-1",
   132  	300002: "ap-hongkong-2",
   133  	300003: "ap-hongkong-3",
   134  	390001: "ap-taipei-1",
   135  	900001: "ap-singapore-1",
   136  	230001: "ap-bangkok-1",
   137  	210001: "ap-mumbai-1",
   138  	210002: "ap-mumbai-2",
   139  	180001: "ap-seoul-1",
   140  	180002: "ap-seoul-2",
   141  	250001: "ap-tokyo-1",
   142  	150001: "na-siliconvalley-1",
   143  	150002: "na-siliconvalley-2",
   144  	220001: "na-ashburn-1",
   145  	220002: "na-ashburn-2",
   146  	400001: "na-toronto-1",
   147  	170001: "eu-frankfurt-1",
   148  	240001: "eu-moscow-1",
   149  }
   150  
   151  var zoneIdMaps = map[string]int{
   152  	"ap-guangzhou-1":     100001,
   153  	"ap-guangzhou-2":     100002,
   154  	"ap-guangzhou-3":     100003,
   155  	"ap-guangzhou-4":     100004,
   156  	"ap-guangzhou-6":     100006,
   157  	"ap-shenzhen-fsi-1":  110001,
   158  	"ap-shenzhen-fsi-2":  110002,
   159  	"ap-shenzhen-fsi-3":  110003,
   160  	"ap-shanghai-1":      200001,
   161  	"ap-shanghai-2":      200002,
   162  	"ap-shanghai-3":      200003,
   163  	"ap-shanghai-4":      200004,
   164  	"ap-shanghai-5":      200005,
   165  	"ap-shanghai-6":      200006,
   166  	"ap-shanghai-7":      200007,
   167  	"ap-shanghai-fsi-1":  700001,
   168  	"ap-shanghai-fsi-2":  700002,
   169  	"ap-shanghai-fsi-3":  700003,
   170  	"ap-nanjing-1":       330001,
   171  	"ap-nanjing-2":       330002,
   172  	"ap-nanjing-3":       330003,
   173  	"ap-beijing-1":       800001,
   174  	"ap-beijing-2":       800002,
   175  	"ap-beijing-3":       800003,
   176  	"ap-beijing-4":       800004,
   177  	"ap-beijing-5":       800005,
   178  	"ap-beijing-6":       800006,
   179  	"ap-beijing-7":       800007,
   180  	"ap-beijing-fsi-1":   460001,
   181  	"ap-tianjin-1":       360001,
   182  	"ap-tianjin-2":       360002,
   183  	"ap-chengdu-1":       160001,
   184  	"ap-chengdu-2":       160002,
   185  	"ap-chongqing-1":     190001,
   186  	"ap-hongkong-1":      300001,
   187  	"ap-hongkong-2":      300002,
   188  	"ap-hongkong-3":      300003,
   189  	"ap-taipei-1":        390001,
   190  	"ap-singapore-1":     900001,
   191  	"ap-bangkok-1":       230001,
   192  	"ap-mumbai-1":        210001,
   193  	"ap-mumbai-2":        210002,
   194  	"ap-seoul-1":         180001,
   195  	"ap-seoul-2":         180002,
   196  	"ap-tokyo-1":         250001,
   197  	"na-siliconvalley-1": 150001,
   198  	"na-siliconvalley-2": 150002,
   199  	"na-ashburn-1":       220001,
   200  	"na-ashburn-2":       220002,
   201  	"na-toronto-1":       400001,
   202  	"eu-frankfurt-1":     170001,
   203  	"eu-moscow-1":        240001,
   204  }
   205  
   206  func (self *SElasticcache) GetId() string {
   207  	return self.InstanceID
   208  }
   209  
   210  func (self *SElasticcache) GetName() string {
   211  	return self.InstanceName
   212  }
   213  
   214  func (self *SElasticcache) GetGlobalId() string {
   215  	return self.GetId()
   216  }
   217  
   218  // https://cloud.tencent.com/document/api/239/20022#InstanceSet
   219  // 实例当前状态,0:待初始化;1:实例在流程中;2:实例运行中;-2:实例已隔离;-3:实例待删除
   220  func (self *SElasticcache) GetStatus() string {
   221  	switch self.Status {
   222  	case 2:
   223  		return api.ELASTIC_CACHE_STATUS_RUNNING
   224  	case 0:
   225  		return api.ELASTIC_CACHE_STATUS_DEPLOYING
   226  	case -3:
   227  		return api.ELASTIC_CACHE_STATUS_RELEASING
   228  	case -2:
   229  		return api.ELASTIC_CACHE_STATUS_UNAVAILABLE
   230  	case 1:
   231  		return api.ELASTIC_CACHE_STATUS_CHANGING
   232  	}
   233  
   234  	return ""
   235  }
   236  
   237  func (self *SElasticcache) GetConnections() int {
   238  	return self.ClientLimit
   239  }
   240  
   241  func (self *SElasticcache) Refresh() error {
   242  	cache, err := self.region.GetIElasticcacheById(self.GetId())
   243  	if err != nil {
   244  		return errors.Wrap(err, "Elasticcache.Refresh.GetElasticCache")
   245  	}
   246  
   247  	err = jsonutils.Update(self, cache)
   248  	if err != nil {
   249  		return errors.Wrap(err, "Elasticcache.Refresh.Update")
   250  	}
   251  
   252  	return nil
   253  }
   254  
   255  func (self *SElasticcache) IsEmulated() bool {
   256  	return false
   257  }
   258  
   259  func (self *SElasticcache) GetProjectId() string {
   260  	return strconv.Itoa(self.ProjectID)
   261  }
   262  
   263  func (self *SElasticcache) GetBillingType() string {
   264  	// 计费模式:0-按量计费,1-包年包月
   265  	if self.BillingMode == 1 {
   266  		return billing_api.BILLING_TYPE_PREPAID
   267  	} else {
   268  		return billing_api.BILLING_TYPE_POSTPAID
   269  	}
   270  }
   271  
   272  func (self *SElasticcache) GetCreatedAt() time.Time {
   273  	return self.Createtime
   274  }
   275  
   276  func (self *SElasticcache) GetExpiredAt() time.Time {
   277  	if self.DeadlineTime == "0000-00-00 00:00:00" || len(self.DeadlineTime) == 0 {
   278  		return time.Time{}
   279  	}
   280  
   281  	t, err := time.Parse("2006-01-02 15:04:05", self.DeadlineTime)
   282  	if err != nil {
   283  		log.Debugf("GetExpiredAt.Parse %s", err)
   284  		return time.Time{}
   285  	}
   286  
   287  	return t
   288  }
   289  
   290  // https://cloud.tencent.com/document/product/239/31785
   291  func (self *SElasticcache) SetAutoRenew(bc billing.SBillingCycle) error {
   292  	params := map[string]string{}
   293  	params["Operation"] = "modifyAutoRenew"
   294  	params["InstanceIds.0"] = self.GetId()
   295  	if bc.AutoRenew {
   296  		params["AutoRenews.0"] = "1"
   297  	} else {
   298  		params["AutoRenews.0"] = "0"
   299  	}
   300  
   301  	_, err := self.region.redisRequest("ModifyInstance", params)
   302  	if err != nil {
   303  		return errors.Wrap(err, "ModifyInstance")
   304  	}
   305  
   306  	return nil
   307  }
   308  
   309  func (self *SElasticcache) IsAutoRenew() bool {
   310  	// 实例是否设置自动续费标识,1:设置自动续费;0:未设置自动续费
   311  	return self.AutoRenewFlag == 1
   312  }
   313  
   314  //  redis:master:s1:r5:m1g:v4.0
   315  // 实例类型:2 – Redis2.8内存版(标准架构),3 – CKV 3.2内存版(标准架构),4 – CKV 3.2内存版(集群架构)
   316  // ,6 – Redis4.0内存版(标准架构),7 – Redis4.0内存版(集群架构),
   317  //  8 – Redis5.0内存版(标准架构),9 – Redis5.0内存版(集群架构)
   318  func (self *SElasticcache) GetInstanceType() string {
   319  	segs := make([]string, 6)
   320  	segs[0] = "redis"
   321  	switch self.Type {
   322  	case 2, 3, 6, 8:
   323  		segs[1] = "master"
   324  	case 4, 7, 9, 10:
   325  		segs[1] = "cluster"
   326  	case 5:
   327  		segs[1] = "single"
   328  	default:
   329  		segs[1] = strconv.Itoa(self.Type)
   330  	}
   331  
   332  	segs[2] = fmt.Sprintf("s%d", self.RedisShardNum)
   333  	segs[3] = fmt.Sprintf("r%d", self.RedisReplicasNum)
   334  	if self.DiskSize > 0 {
   335  		segs[4] = fmt.Sprintf("m%dg-d%dg", self.Size/1024, self.DiskSize)
   336  	} else {
   337  		segs[4] = fmt.Sprintf("m%dg", self.Size/1024)
   338  	}
   339  	segs[5] = fmt.Sprintf("v%s", self.GetEngineVersion())
   340  
   341  	return strings.Join(segs, ":")
   342  }
   343  
   344  func (self *SElasticcache) GetCapacityMB() int {
   345  	return self.Size
   346  }
   347  
   348  func (self *SElasticcache) GetArchType() string {
   349  	// 产品类型:standalone – 标准版,cluster – 集群版
   350  	switch self.ProductType {
   351  	case "standalone":
   352  		return api.ELASTIC_CACHE_ARCH_TYPE_MASTER
   353  	case "cluster":
   354  		return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER
   355  	}
   356  
   357  	return ""
   358  }
   359  
   360  func (self *SElasticcache) GetNodeType() string {
   361  	// single(单副本) | double(双副本)
   362  	switch self.RedisReplicasNum {
   363  	case 1:
   364  		return "single"
   365  	case 2:
   366  		return "double"
   367  	case 3:
   368  		return "three"
   369  	case 4:
   370  		return "four"
   371  	case 5:
   372  		return "five"
   373  	case 6:
   374  		return "six"
   375  	}
   376  
   377  	return strconv.Itoa(self.RedisReplicasNum)
   378  }
   379  
   380  func (self *SElasticcache) GetEngine() string {
   381  	return api.ELASTIC_CACHE_ENGINE_REDIS
   382  }
   383  
   384  func (self *SElasticcache) GetEngineVersion() string {
   385  	switch self.Type {
   386  	case 1, 2, 5:
   387  		return "2.8"
   388  	case 3, 4:
   389  		return "3.2"
   390  	case 6, 7, 10:
   391  		return "4.0"
   392  	case 8, 9:
   393  		return "5.0"
   394  	}
   395  
   396  	return "unknown"
   397  }
   398  
   399  func (self *SElasticcache) GetVpcId() string {
   400  	return self.UniqVpcID
   401  }
   402  
   403  // https://cloud.tencent.com/document/product/239/4106
   404  func (self *SElasticcache) GetZoneId() string {
   405  	zone := zoneMaps[self.ZoneID]
   406  	if len(zone) == 0 {
   407  		net, err := self.region.GetNetwork(self.UniqSubnetID)
   408  		if err != nil {
   409  			log.Warningf("zone %d not found. zone map needed update.", self.ZoneID)
   410  			log.Debugf("GetNetwork %s %s", self.UniqSubnetID, err)
   411  			return ""
   412  		}
   413  
   414  		zone = net.Zone
   415  	}
   416  
   417  	z, err := self.region.getZoneById(zone)
   418  	if err != nil {
   419  		log.Debugf("getZoneById %s", err)
   420  		return ""
   421  	}
   422  
   423  	return z.GetGlobalId()
   424  }
   425  
   426  func (self *SElasticcache) GetNetworkType() string {
   427  	if len(self.UniqVpcID) > 0 {
   428  		return api.LB_NETWORK_TYPE_VPC
   429  	} else {
   430  		return api.LB_NETWORK_TYPE_CLASSIC
   431  	}
   432  }
   433  
   434  func (self *SElasticcache) GetNetworkId() string {
   435  	return self.UniqSubnetID
   436  }
   437  
   438  func (self *SElasticcache) GetPrivateDNS() string {
   439  	return self.WANIP
   440  }
   441  
   442  func (self *SElasticcache) GetPrivateIpAddr() string {
   443  	return self.WANIP
   444  }
   445  
   446  func (self *SElasticcache) GetPrivateConnectPort() int {
   447  	return self.Port
   448  }
   449  
   450  func (self *SElasticcache) GetPublicDNS() string {
   451  	return ""
   452  }
   453  
   454  func (self *SElasticcache) GetPublicIpAddr() string {
   455  	return ""
   456  }
   457  
   458  func (self *SElasticcache) GetPublicConnectPort() int {
   459  	return self.Port
   460  }
   461  
   462  // https://cloud.tencent.com/document/api/239/46336
   463  func (self *SElasticcache) getMaintenanceTime() error {
   464  	params := map[string]string{}
   465  	params["InstanceId"] = self.GetId()
   466  	params["Region"] = self.region.GetId()
   467  	resp, err := self.region.client.redisRequest("DescribeMaintenanceWindow", params)
   468  	if err != nil {
   469  		return errors.Wrap(err, "DescribeMaintenanceWindow")
   470  	}
   471  
   472  	self.MaintenanceTime = &MaintenanceTime{}
   473  	err = resp.Unmarshal(self.MaintenanceTime)
   474  	if err != nil {
   475  		return errors.Wrap(err, "err.MaintenanceTime")
   476  	}
   477  
   478  	return nil
   479  }
   480  
   481  func (self *SElasticcache) GetMaintainStartTime() string {
   482  	if self.MaintenanceTime == nil {
   483  		if err := self.getMaintenanceTime(); err != nil {
   484  			log.Debugf("getMaintenanceTime %s", err)
   485  			return ""
   486  		}
   487  	}
   488  
   489  	return self.MaintenanceTime.StartTime
   490  }
   491  
   492  func (self *SElasticcache) GetMaintainEndTime() string {
   493  	if self.MaintenanceTime == nil {
   494  		if err := self.getMaintenanceTime(); err != nil {
   495  			log.Debugf("getMaintenanceTime %s", err)
   496  			return ""
   497  		}
   498  	}
   499  
   500  	return self.MaintenanceTime.EndTime
   501  }
   502  
   503  func (self *SElasticcache) GetAuthMode() string {
   504  	if self.NoAuth != nil && *self.NoAuth == false {
   505  		return "on"
   506  	}
   507  
   508  	return "off"
   509  }
   510  
   511  func (self *SElasticcache) GetSecurityGroupIds() ([]string, error) {
   512  	ss, err := self.region.GetCloudElasticcacheSecurityGroups(self.GetId())
   513  	if err != nil {
   514  		return nil, errors.Wrap(err, "GetCloudElasticcacheSecurityGroups")
   515  	}
   516  
   517  	ret := make([]string, len(ss))
   518  	for i := range ss {
   519  		ret[i] = ss[i].SecurityGroupID
   520  	}
   521  
   522  	return ret, nil
   523  }
   524  
   525  func (self *SElasticcache) GetICloudElasticcacheAccounts() ([]cloudprovider.ICloudElasticcacheAccount, error) {
   526  	accounts, err := self.getCloudElasticcacheAccounts()
   527  	if err != nil {
   528  		return nil, errors.Wrap(err, "GetCloudElasticcacheAccounts")
   529  	}
   530  
   531  	iaccounts := make([]cloudprovider.ICloudElasticcacheAccount, len(accounts))
   532  	for i := range accounts {
   533  		account := accounts[i]
   534  		account.cacheDB = self
   535  		iaccounts[i] = &account
   536  	}
   537  
   538  	return iaccounts, nil
   539  }
   540  
   541  func (self *SElasticcache) GetICloudElasticcacheAcls() ([]cloudprovider.ICloudElasticcacheAcl, error) {
   542  	return nil, cloudprovider.ErrNotSupported
   543  }
   544  
   545  func (self *SElasticcache) GetICloudElasticcacheBackups() ([]cloudprovider.ICloudElasticcacheBackup, error) {
   546  	backups, err := self.region.GetCloudElasticcacheBackups(self.GetId())
   547  	if err != nil {
   548  		return nil, errors.Wrap(err, "GetCloudElasticcacheBackups")
   549  	}
   550  
   551  	ibackups := make([]cloudprovider.ICloudElasticcacheBackup, len(backups))
   552  	for i := range backups {
   553  		backup := backups[i]
   554  		backup.cacheDB = self
   555  		ibackups[i] = &backup
   556  	}
   557  
   558  	return ibackups, nil
   559  }
   560  
   561  func (self *SElasticcache) GetICloudElasticcacheParameters() ([]cloudprovider.ICloudElasticcacheParameter, error) {
   562  	parameters, err := self.region.GetCloudElasticcacheParameters(self.GetId())
   563  	if err != nil {
   564  		return nil, errors.Wrap(err, "GetCloudElasticcacheParameters")
   565  	}
   566  
   567  	ret := []cloudprovider.ICloudElasticcacheParameter{}
   568  	for i := range parameters {
   569  		parameter := parameters[i]
   570  		parameter.cacheDB = self
   571  		ret = append(ret, &parameter)
   572  	}
   573  
   574  	return ret, nil
   575  }
   576  
   577  func (self *SElasticcache) GetICloudElasticcacheAccount(accountId string) (cloudprovider.ICloudElasticcacheAccount, error) {
   578  	accounts, err := self.getCloudElasticcacheAccounts()
   579  	if err != nil {
   580  		return nil, errors.Wrap(err, "GetCloudElasticcacheAccounts")
   581  	}
   582  
   583  	for i := range accounts {
   584  		account := accounts[i]
   585  		if account.GetGlobalId() == accountId {
   586  			account.cacheDB = self
   587  			return &account, nil
   588  		}
   589  	}
   590  
   591  	return nil, cloudprovider.ErrNotFound
   592  }
   593  
   594  func (self *SElasticcache) GetICloudElasticcacheAcl(aclId string) (cloudprovider.ICloudElasticcacheAcl, error) {
   595  	return nil, cloudprovider.ErrNotSupported
   596  }
   597  
   598  func (self *SElasticcache) GetICloudElasticcacheBackup(backupId string) (cloudprovider.ICloudElasticcacheBackup, error) {
   599  	backups, err := self.region.GetCloudElasticcacheBackups(self.InstanceID)
   600  	if err != nil {
   601  		return nil, errors.Wrap(err, "GetCloudElasticcacheBackups")
   602  	}
   603  
   604  	for i := range backups {
   605  		backup := backups[i]
   606  		if backup.GetGlobalId() == backupId {
   607  			backup.cacheDB = self
   608  			return &backup, nil
   609  		}
   610  	}
   611  
   612  	return nil, cloudprovider.ErrNotFound
   613  }
   614  
   615  func (self *SElasticcache) GetICloudElasticcacheBackupByReadmark(readmark string) (cloudprovider.ICloudElasticcacheBackup, error) {
   616  	backups, err := self.region.GetCloudElasticcacheBackups(self.InstanceID)
   617  	if err != nil {
   618  		return nil, errors.Wrap(err, "GetCloudElasticcacheBackups")
   619  	}
   620  
   621  	for i := range backups {
   622  		backup := backups[i]
   623  		if backup.Remark == readmark {
   624  			backup.cacheDB = self
   625  			return &backup, nil
   626  		}
   627  	}
   628  
   629  	return nil, cloudprovider.ErrNotFound
   630  }
   631  
   632  func (self *SElasticcache) Restart() error {
   633  	return errors.Wrap(cloudprovider.ErrNotSupported, "Restart")
   634  }
   635  
   636  // https://cloud.tencent.com/document/product/239/34440
   637  func (self *SElasticcache) Delete() error {
   638  	requiredStatus := ""
   639  	if self.GetBillingType() == billing_api.BILLING_TYPE_POSTPAID {
   640  		if err := self.DestroyPostpaidInstance(); err != nil {
   641  			return errors.Wrap(err, "DestroyPostpaidInstance")
   642  		}
   643  
   644  		requiredStatus = api.ELASTIC_CACHE_STATUS_RELEASING
   645  	} else {
   646  		if err := self.DestroyPrepaidInstance(); err != nil {
   647  			return errors.Wrap(err, "DestroyPrepaidInstance")
   648  		}
   649  
   650  		requiredStatus = api.ELASTIC_CACHE_STATUS_UNAVAILABLE
   651  	}
   652  
   653  	err := cloudprovider.WaitStatus(self, requiredStatus, 5*time.Second, 300*time.Second)
   654  	if err != nil {
   655  		return errors.Wrap(err, fmt.Sprintf("WaitStatus.%s", requiredStatus))
   656  	}
   657  
   658  	err = self.CleanupInstance()
   659  	if err != nil {
   660  		return errors.Wrap(err, "CleanupInstance")
   661  	}
   662  
   663  	return nil
   664  }
   665  
   666  // https://cloud.tencent.com/document/product/239/34439
   667  func (self *SElasticcache) DestroyPrepaidInstance() error {
   668  	if self.GetStatus() == api.ELASTIC_CACHE_STATUS_UNAVAILABLE {
   669  		return nil
   670  	}
   671  
   672  	params := map[string]string{}
   673  	params["InstanceId"] = self.GetId()
   674  	_, err := self.region.redisRequest("DestroyPrepaidInstance", params)
   675  	if err != nil {
   676  		return errors.Wrap(err, "DestroyPrepaidInstance")
   677  	}
   678  
   679  	return nil
   680  }
   681  
   682  // https://cloud.tencent.com/document/product/239/34439
   683  func (self *SElasticcache) DestroyPostpaidInstance() error {
   684  	if self.GetStatus() == api.ELASTIC_CACHE_STATUS_RELEASING {
   685  		return nil
   686  	}
   687  
   688  	params := map[string]string{}
   689  	params["InstanceId"] = self.GetId()
   690  	_, err := self.region.redisRequest("DestroyPostpaidInstance", params)
   691  	if err != nil {
   692  		return errors.Wrap(err, "DestroyPostpaidInstance")
   693  	}
   694  
   695  	return nil
   696  }
   697  
   698  // https://cloud.tencent.com/document/product/239/34442
   699  func (self *SElasticcache) CleanupInstance() error {
   700  	params := map[string]string{}
   701  	params["InstanceId"] = self.GetId()
   702  	_, err := self.region.redisRequest("CleanUpInstance", params)
   703  	if err != nil {
   704  		return errors.Wrap(err, "CleanUpInstance")
   705  	}
   706  
   707  	return nil
   708  }
   709  
   710  // https://cloud.tencent.com/document/product/239/20013
   711  func (self *SElasticcache) ChangeInstanceSpec(spec string) error {
   712  	s, err := parseLocalInstanceSpec(spec)
   713  	if err != nil {
   714  		return errors.Wrap(err, "parseLocalInstanceSpec")
   715  	}
   716  
   717  	params := map[string]string{}
   718  	params["InstanceId"] = self.GetId()
   719  	params["MemSize"] = fmt.Sprintf("%d", s.MemSizeMB)
   720  	params["RedisShardNum"] = s.RedisShardNum
   721  	params["RedisReplicasNum"] = s.RedisReplicasNum
   722  	_, err = self.region.redisRequest("UpgradeInstance", params)
   723  	if err != nil {
   724  		return errors.Wrap(err, "UpgradeInstance")
   725  	}
   726  
   727  	return nil
   728  }
   729  
   730  // https://cloud.tencent.com/document/product/239/46335
   731  func (self *SElasticcache) SetMaintainTime(maintainStartTime, maintainEndTime string) error {
   732  	params := map[string]string{}
   733  	params["InstanceId"] = self.GetId()
   734  	params["StartTime"] = maintainStartTime
   735  	params["EndTime"] = maintainEndTime
   736  	_, err := self.region.redisRequest("ModifyMaintenanceWindow", params)
   737  	if err != nil {
   738  		return errors.Wrap(err, "ModifyMaintenanceWindow")
   739  	}
   740  
   741  	return nil
   742  }
   743  
   744  func (self *SElasticcache) AllocatePublicConnection(port int) (string, error) {
   745  	return "", errors.Wrap(cloudprovider.ErrNotSupported, "AllocatePublicConnection")
   746  }
   747  
   748  func (self *SElasticcache) ReleasePublicConnection() error {
   749  	return errors.Wrap(cloudprovider.ErrNotSupported, "ReleasePublicConnection")
   750  }
   751  
   752  // https://cloud.tencent.com/document/product/239/38926
   753  func (self *SElasticcache) CreateAccount(account cloudprovider.SCloudElasticCacheAccountInput) (cloudprovider.ICloudElasticcacheAccount, error) {
   754  	params := map[string]string{}
   755  	params["InstanceId"] = self.GetId()
   756  	params["AccountName"] = account.AccountName
   757  	params["AccountPassword"] = account.AccountPassword
   758  	params["Privilege"] = account.AccountPrivilege
   759  	params["ReadonlyPolicy.0"] = "master"
   760  	if len(account.Description) > 0 {
   761  		params["Remark"] = account.Description
   762  	}
   763  
   764  	_, err := self.region.redisRequest("CreateInstanceAccount", params)
   765  	if err != nil {
   766  		return nil, errors.Wrap(err, "CreateInstanceAccount")
   767  	}
   768  
   769  	accountId := ""
   770  	cloudprovider.Wait(5*time.Second, 60*time.Second, func() (bool, error) {
   771  		accounts, err := self.GetICloudElasticcacheAccounts()
   772  		if err != nil {
   773  			log.Debugf("GetICloudElasticcacheAccounts %s", err)
   774  			return false, nil
   775  		}
   776  
   777  		for i := range accounts {
   778  			if accounts[i].GetName() == account.AccountName {
   779  				accountId = accounts[i].GetGlobalId()
   780  				return true, nil
   781  			}
   782  		}
   783  
   784  		return false, nil
   785  	})
   786  
   787  	return self.GetICloudElasticcacheAccount(accountId)
   788  }
   789  
   790  func (self *SElasticcache) CreateAcl(aclName, securityIps string) (cloudprovider.ICloudElasticcacheAcl, error) {
   791  	return nil, errors.Wrap(cloudprovider.ErrNotSupported, "CreateAcl")
   792  }
   793  
   794  // https://cloud.tencent.com/document/product/239/20010
   795  func (self *SElasticcache) CreateBackup(desc string) (cloudprovider.ICloudElasticcacheBackup, error) {
   796  	readmark := strings.Join([]string{desc, fmt.Sprintf("%d", time.Now().Unix())}, "@")
   797  	params := map[string]string{}
   798  	params["InstanceId"] = self.GetId()
   799  	params["Remark"] = readmark
   800  	resp, err := self.region.redisRequest("ManualBackupInstance", params)
   801  	if err != nil {
   802  		return nil, errors.Wrap(err, "ManualBackupInstance")
   803  	}
   804  
   805  	taskId := 0
   806  	err = resp.Unmarshal(&taskId, "TaskId")
   807  	if err != nil {
   808  		return nil, errors.Wrap(err, "Unmarshal")
   809  	}
   810  
   811  	var task *SElasticcacheTask
   812  	err = cloudprovider.Wait(5*time.Second, 900*time.Second, func() (bool, error) {
   813  		task, err = self.region.DescribeTaskInfo(fmt.Sprintf("%d", taskId))
   814  		if err != nil {
   815  			return false, nil
   816  		}
   817  
   818  		if task != nil {
   819  			if task.Status == "failed" || task.Status == "error" {
   820  				return false, fmt.Errorf("CreateBackup failed %#v", task)
   821  			}
   822  
   823  			if task.Status == "succeed" {
   824  				return true, nil
   825  			}
   826  		}
   827  
   828  		return false, nil
   829  	})
   830  	if err != nil {
   831  		return nil, errors.Wrap(err, "Wait.DescribeTaskInfo")
   832  	}
   833  
   834  	if task == nil {
   835  		return nil, fmt.Errorf("CreateBackup failed task is empty")
   836  	}
   837  
   838  	return self.GetICloudElasticcacheBackupByReadmark(readmark)
   839  }
   840  
   841  // https://cloud.tencent.com/document/product/239/20021
   842  func (self *SElasticcache) FlushInstance(input cloudprovider.SCloudElasticCacheFlushInstanceInput) error {
   843  	params := map[string]string{}
   844  	params["InstanceId"] = self.GetId()
   845  	if self.NoAuth != nil && *self.NoAuth == false {
   846  		if len(input.Password) > 0 {
   847  			params["Password"] = input.Password
   848  		} else {
   849  			return fmt.Errorf("password required on auth mode while flush elastich cache instance")
   850  		}
   851  	}
   852  	_, err := self.region.redisRequest("ClearInstance", params)
   853  	if err != nil {
   854  		return errors.Wrap(err, "ClearInstance")
   855  	}
   856  
   857  	return nil
   858  }
   859  
   860  // https://cloud.tencent.com/document/product/239/38923
   861  // https://cloud.tencent.com/document/product/239/20014
   862  // true表示将主账号切换为免密账号,这里只适用于主账号,子账号不可免密
   863  func (self *SElasticcache) UpdateAuthMode(noPasswordAccess bool, password string) error {
   864  	params := map[string]string{}
   865  	params["InstanceId"] = self.GetId()
   866  	if noPasswordAccess {
   867  		params["NoAuth"] = "true"
   868  	} else {
   869  		params["NoAuth"] = "false"
   870  		params["Password"] = password
   871  	}
   872  
   873  	_, err := self.region.redisRequest("ResetPassword", params)
   874  	if err != nil {
   875  		return errors.Wrap(err, "ResetPassword")
   876  	}
   877  
   878  	return nil
   879  }
   880  
   881  // https://cloud.tencent.com/document/product/239/34445
   882  // todo: finish me
   883  func (self *SElasticcache) UpdateInstanceParameters(config jsonutils.JSONObject) error {
   884  	params := map[string]string{}
   885  	params["InstanceId"] = self.GetId()
   886  	params["InstanceParams.0"] = config.String()
   887  	_, err := self.region.redisRequest("ModifyInstanceParams", params)
   888  	if err != nil {
   889  		return errors.Wrap(err, "ModifyInstanceParams")
   890  	}
   891  
   892  	return nil
   893  }
   894  
   895  func (self *SElasticcache) UpdateBackupPolicy(config cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) error {
   896  	panic("implement me")
   897  }
   898  
   899  func (self *SElasticcache) getCloudElasticcacheAccounts() ([]SElasticcacheAccount, error) {
   900  	if self.GetEngineVersion() == "2.8" || (self.NodeSet != nil && len(self.NodeSet) > 0) {
   901  		account := SElasticcacheAccount{}
   902  		account.cacheDB = self
   903  		account.AccountName = "root"
   904  		account.InstanceID = self.GetId()
   905  		account.Privilege = "rw"
   906  		account.Status = 2
   907  		account.IsEmulate = true
   908  
   909  		return []SElasticcacheAccount{account}, nil
   910  	}
   911  
   912  	accounts, err := self.region.GetCloudElasticcacheAccounts(self.GetId())
   913  	if err != nil {
   914  		return nil, errors.Wrap(err, "GetCloudElasticcacheAccounts")
   915  	}
   916  
   917  	for i := range accounts {
   918  		accounts[i].cacheDB = self
   919  	}
   920  
   921  	return accounts, nil
   922  }
   923  
   924  // https://cloud.tencent.com/document/api/239/38924
   925  func (self *SElasticcache) getCloudElasticcacheAccount(accountName string) (*SElasticcacheAccount, error) {
   926  	accounts, err := self.getCloudElasticcacheAccounts()
   927  	if err != nil {
   928  		return nil, errors.Wrap(err, "GetCloudElasticcacheAccounts")
   929  	}
   930  
   931  	for i := range accounts {
   932  		if accounts[i].AccountName == accountName {
   933  			accounts[i].cacheDB = self
   934  			return &accounts[i], nil
   935  		}
   936  	}
   937  
   938  	return nil, cloudprovider.ErrNotFound
   939  }
   940  
   941  // https://cloud.tencent.com/document/api/239/41256
   942  func (self *SElasticcache) UpdateSecurityGroups(secgroupIds []string) error {
   943  	params := map[string]string{}
   944  	params["InstanceId"] = self.GetId()
   945  	params["Product"] = "redis"
   946  	for i := range secgroupIds {
   947  		params[fmt.Sprintf("SecurityGroupIds.%d", i)] = secgroupIds[i]
   948  	}
   949  
   950  	_, err := self.region.redisRequest("ModifyDBInstanceSecurityGroups", params)
   951  	if err != nil {
   952  		return errors.Wrap(err, "ModifyDBInstanceSecurityGroups")
   953  	}
   954  
   955  	return nil
   956  }
   957  
   958  func (self *SElasticcache) Renew(bc billing.SBillingCycle) error {
   959  	month := bc.GetMonths()
   960  	if month <= 0 {
   961  		return errors.Wrap(fmt.Errorf("month should great than 0"), "GetMonths")
   962  	}
   963  
   964  	params := map[string]string{}
   965  	params["InstanceId"] = self.GetId()
   966  	params["Period"] = fmt.Sprintf("%d", month)
   967  	_, err := self.region.redisRequest("RenewInstance", params)
   968  	if err != nil {
   969  		return errors.Wrap(err, "RenewInstance")
   970  	}
   971  
   972  	return nil
   973  }
   974  
   975  // https://cloud.tencent.com/document/api/239/38924
   976  func (self *SRegion) GetCloudElasticcacheAccounts(instanceId string) ([]SElasticcacheAccount, error) {
   977  	params := map[string]string{}
   978  	params["Region"] = self.GetId()
   979  	params["InstanceId"] = instanceId
   980  	params["Limit"] = "20"
   981  	params["Offset"] = "0"
   982  
   983  	ret := []SElasticcacheAccount{}
   984  	offset := 0
   985  	for {
   986  		resp, err := self.client.redisRequest("DescribeInstanceAccount", params)
   987  		if err != nil {
   988  			return nil, errors.Wrap(err, "DescribeInstanceAccount")
   989  		}
   990  
   991  		_ret := []SElasticcacheAccount{}
   992  		err = resp.Unmarshal(&_ret, "Accounts")
   993  		if err != nil {
   994  			return nil, errors.Wrap(err, "Unmarshal")
   995  		} else {
   996  			ret = append(ret, _ret...)
   997  		}
   998  
   999  		if len(_ret) < 20 {
  1000  			break
  1001  		} else {
  1002  			offset += 20
  1003  			params["Offset"] = strconv.Itoa(offset)
  1004  		}
  1005  	}
  1006  
  1007  	return ret, nil
  1008  }
  1009  
  1010  // https://cloud.tencent.com/document/api/239/20011
  1011  func (self *SRegion) GetCloudElasticcacheBackups(instanceId string) ([]SElasticcacheBackup, error) {
  1012  	params := map[string]string{}
  1013  	params["Region"] = self.GetId()
  1014  	params["InstanceId"] = instanceId
  1015  	params["Limit"] = "20"
  1016  	params["Offset"] = "0"
  1017  
  1018  	ret := []SElasticcacheBackup{}
  1019  	offset := 0
  1020  	for {
  1021  		resp, err := self.client.redisRequest("DescribeInstanceBackups", params)
  1022  		if err != nil {
  1023  			return nil, errors.Wrap(err, "DescribeInstanceBackups")
  1024  		}
  1025  
  1026  		_ret := []SElasticcacheBackup{}
  1027  		err = resp.Unmarshal(&_ret, "BackupSet")
  1028  		if err != nil {
  1029  			return nil, errors.Wrap(err, "Unmarshal")
  1030  		} else {
  1031  			ret = append(ret, _ret...)
  1032  		}
  1033  
  1034  		if len(_ret) < 20 {
  1035  			break
  1036  		} else {
  1037  			offset += 20
  1038  			params["Offset"] = strconv.Itoa(offset)
  1039  		}
  1040  	}
  1041  
  1042  	return ret, nil
  1043  }
  1044  
  1045  // https://cloud.tencent.com/document/api/239/41259
  1046  func (self *SRegion) GetCloudElasticcacheSecurityGroups(instanceId string) ([]SElasticcacheSecgroup, error) {
  1047  	params := map[string]string{}
  1048  	params["Region"] = self.GetId()
  1049  	params["InstanceId"] = instanceId
  1050  	params["Product"] = "redis"
  1051  	resp, err := self.client.redisRequest("DescribeDBSecurityGroups", params)
  1052  	if err != nil {
  1053  		return nil, errors.Wrap(err, "DescribeDBSecurityGroups")
  1054  	}
  1055  
  1056  	ret := []SElasticcacheSecgroup{}
  1057  	err = resp.Unmarshal(&ret, "Groups")
  1058  	if err != nil {
  1059  		return nil, errors.Wrap(err, "Unmarshal")
  1060  	}
  1061  
  1062  	return ret, nil
  1063  }
  1064  
  1065  // https://cloud.tencent.com/document/api/239/34448
  1066  func (self *SRegion) GetCloudElasticcacheParameters(instanceId string) ([]SElasticcacheParameter, error) {
  1067  	params := map[string]string{}
  1068  	params["Region"] = self.GetId()
  1069  	params["InstanceId"] = instanceId
  1070  	resp, err := self.client.redisRequest("DescribeInstanceParams", params)
  1071  	if err != nil {
  1072  		return nil, errors.Wrap(err, "DescribeInstanceParams")
  1073  	}
  1074  
  1075  	ret1 := []SElasticcacheParameter{}
  1076  	err = resp.Unmarshal(&ret1, "InstanceEnumParam")
  1077  	if err != nil {
  1078  		return nil, errors.Wrap(err, "InstanceEnumParam")
  1079  	}
  1080  
  1081  	ret2 := []SElasticcacheParameter{}
  1082  	err = resp.Unmarshal(&ret2, "InstanceIntegerParam")
  1083  	if err != nil {
  1084  		return nil, errors.Wrap(err, "InstanceIntegerParam")
  1085  	}
  1086  
  1087  	ret3 := []SElasticcacheParameter{}
  1088  	err = resp.Unmarshal(&ret3, "InstanceMultiParam")
  1089  	if err != nil {
  1090  		return nil, errors.Wrap(err, "InstanceMultiParam")
  1091  	}
  1092  
  1093  	ret4 := []SElasticcacheParameter{}
  1094  	err = resp.Unmarshal(&ret4, "InstanceTextParam")
  1095  	if err != nil {
  1096  		return nil, errors.Wrap(err, "InstanceTextParam")
  1097  	}
  1098  
  1099  	ret := []SElasticcacheParameter{}
  1100  	ret = append(ret, ret1...)
  1101  	ret = append(ret, ret2...)
  1102  	ret = append(ret, ret3...)
  1103  	ret = append(ret, ret4...)
  1104  	return ret, nil
  1105  }
  1106  
  1107  // https://cloud.tencent.com/document/api/239/20018
  1108  func (self *SRegion) GetCloudElasticcaches(instanceId string) ([]SElasticcache, error) {
  1109  	params := map[string]string{}
  1110  	params["Region"] = self.GetId()
  1111  	params["Limit"] = "20"
  1112  	params["Offset"] = "0"
  1113  
  1114  	if len(instanceId) > 0 {
  1115  		params["InstanceId"] = instanceId
  1116  	}
  1117  
  1118  	ret := []SElasticcache{}
  1119  	offset := 0
  1120  	for {
  1121  		resp, err := self.client.redisRequest("DescribeInstances", params)
  1122  		if err != nil {
  1123  			return nil, errors.Wrap(err, "DescribeInstances")
  1124  		}
  1125  
  1126  		_ret := []SElasticcache{}
  1127  		err = resp.Unmarshal(&_ret, "InstanceSet")
  1128  		if err != nil {
  1129  			return nil, errors.Wrap(err, "Unmarshal")
  1130  		} else {
  1131  			ret = append(ret, _ret...)
  1132  		}
  1133  
  1134  		if len(_ret) < 20 {
  1135  			break
  1136  		} else {
  1137  			offset += 20
  1138  			params["Offset"] = strconv.Itoa(offset)
  1139  		}
  1140  	}
  1141  
  1142  	return ret, nil
  1143  }
  1144  
  1145  type elasticcachInstanceSpec struct {
  1146  	TypeId           string `json:"type_id"`
  1147  	MemSizeMB        int    `json:"mem_size_mb"`
  1148  	DiskSizeGB       int    `json:"disk_size_gb"`
  1149  	RedisShardNum    string `json:"redis_shard_num"`
  1150  	RedisReplicasNum string `json:"redis_replicas_num"`
  1151  }
  1152  
  1153  //  redis:master:s1:r5:m1g:v4.0
  1154  func parseLocalInstanceSpec(s string) (elasticcachInstanceSpec, error) {
  1155  	ret := elasticcachInstanceSpec{}
  1156  	segs := strings.Split(s, ":")
  1157  	if len(segs) != 6 {
  1158  		return ret, fmt.Errorf("invalid instance spec %s", s)
  1159  	}
  1160  	if segs[1] == "master" {
  1161  		switch segs[5] {
  1162  		case "v2.8":
  1163  			ret.TypeId = "2"
  1164  		case "v3.2":
  1165  			ret.TypeId = "3"
  1166  		case "v4.0":
  1167  			ret.TypeId = "6"
  1168  		case "v5.0":
  1169  			ret.TypeId = "8"
  1170  		default:
  1171  			return ret, fmt.Errorf("unknown master elastic cache version %s", segs[1])
  1172  		}
  1173  	} else if segs[1] == "cluster" {
  1174  		switch segs[5] {
  1175  		case "v3.0":
  1176  			ret.TypeId = "4"
  1177  		case "v4.0":
  1178  			ret.TypeId = "7"
  1179  		case "v5.0":
  1180  			if strings.Contains(segs[4], "-d") {
  1181  				ret.TypeId = "10"
  1182  			} else {
  1183  				ret.TypeId = "9"
  1184  			}
  1185  
  1186  		default:
  1187  			return ret, fmt.Errorf("unknown cluster elastic cache version %s", segs[1])
  1188  		}
  1189  	} else if segs[1] == "single" {
  1190  		switch segs[5] {
  1191  		case "v2.8":
  1192  			ret.TypeId = "5"
  1193  		default:
  1194  			return ret, fmt.Errorf("unknown single elastic cache version %s", segs[1])
  1195  		}
  1196  	} else {
  1197  		return ret, fmt.Errorf("unknown elastic cache type %s", segs[1])
  1198  	}
  1199  
  1200  	//
  1201  	sizes := strings.Split(segs[4], "-")
  1202  	if len(sizes) == 2 {
  1203  		ms, err := strconv.Atoi(strings.Trim(sizes[1], "dg"))
  1204  		if err != nil {
  1205  			return ret, errors.Wrap(err, "Atoi")
  1206  		}
  1207  
  1208  		ret.DiskSizeGB = ms
  1209  	}
  1210  
  1211  	sn, err := strconv.Atoi(strings.Trim(segs[2], "s"))
  1212  	if err != nil {
  1213  		return ret, errors.Wrap(err, "RedisShardNum")
  1214  	}
  1215  
  1216  	ms, err := strconv.Atoi(strings.Trim(sizes[0], "mdg"))
  1217  	if err != nil {
  1218  		return ret, errors.Wrap(err, "Atoi")
  1219  	}
  1220  	if strings.HasSuffix(sizes[0], "g") {
  1221  		ret.MemSizeMB = ms * 1024 / sn
  1222  	} else if strings.HasSuffix(sizes[0], "m") {
  1223  		ret.MemSizeMB = ms / sn
  1224  	}
  1225  
  1226  	ret.RedisShardNum = strings.Trim(segs[2], "s")
  1227  	ret.RedisReplicasNum = strings.Trim(segs[3], "r")
  1228  	return ret, nil
  1229  }