yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/region.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  	"context"
    19  	"fmt"
    20  	"strconv"
    21  	"strings"
    22  	"time"
    23  
    24  	sdkerrors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
    25  	"github.com/tencentyun/cos-go-sdk-v5"
    26  
    27  	"yunion.io/x/jsonutils"
    28  	"yunion.io/x/log"
    29  	"yunion.io/x/pkg/errors"
    30  	"yunion.io/x/pkg/utils"
    31  
    32  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    33  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    34  	"yunion.io/x/cloudmux/pkg/multicloud"
    35  )
    36  
    37  type SRegion struct {
    38  	multicloud.SRegion
    39  
    40  	client *SQcloudClient
    41  
    42  	izones []cloudprovider.ICloudZone
    43  	ivpcs  []cloudprovider.ICloudVpc
    44  
    45  	storageCache *SStoragecache
    46  
    47  	instanceTypes []SInstanceType
    48  
    49  	Region      string
    50  	RegionName  string
    51  	RegionState string
    52  
    53  	Latitude      float64
    54  	Longitude     float64
    55  	fetchLocation bool
    56  }
    57  
    58  func (self *SRegion) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) {
    59  	return nil, cloudprovider.ErrNotImplemented
    60  }
    61  
    62  func (self *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
    63  	return nil, cloudprovider.ErrNotImplemented
    64  }
    65  
    66  func (self *SRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) {
    67  	lbs, err := self.GetLoadbalancers(nil)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	ilbs := make([]cloudprovider.ICloudLoadbalancer, len(lbs))
    73  	for i := range lbs {
    74  		lbs[i].region = self
    75  		ilbs[i] = &lbs[i]
    76  	}
    77  
    78  	return ilbs, nil
    79  }
    80  
    81  // 腾讯云不支持acl
    82  func (self *SRegion) GetILoadBalancerAcls() ([]cloudprovider.ICloudLoadbalancerAcl, error) {
    83  	return []cloudprovider.ICloudLoadbalancerAcl{}, nil
    84  }
    85  
    86  func (self *SRegion) GetILoadBalancerCertificates() ([]cloudprovider.ICloudLoadbalancerCertificate, error) {
    87  	certs, err := self.GetCertificates("", "", "")
    88  	if err != nil {
    89  		return nil, errors.Wrap(err, "GetCertificates")
    90  	}
    91  
    92  	icerts := make([]cloudprovider.ICloudLoadbalancerCertificate, len(certs))
    93  	for i := range certs {
    94  		icerts[i] = &certs[i]
    95  	}
    96  
    97  	return icerts, nil
    98  }
    99  
   100  func (self *SRegion) GetILoadBalancerCertificateById(certId string) (cloudprovider.ICloudLoadbalancerCertificate, error) {
   101  	cert, err := self.GetCertificate(certId)
   102  	if err != nil {
   103  		return nil, errors.Wrap(err, "GetCertificate")
   104  	}
   105  
   106  	return cert, nil
   107  }
   108  
   109  func (self *SRegion) GetILoadBalancerById(loadbalancerId string) (cloudprovider.ICloudLoadbalancer, error) {
   110  	lbs, err := self.GetLoadbalancers([]string{loadbalancerId})
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	if len(lbs) == 1 {
   116  		lbs[0].region = self
   117  		return &lbs[0], nil
   118  	} else if len(lbs) == 0 {
   119  		return nil, cloudprovider.ErrNotFound
   120  	} else {
   121  		log.Debugf("GetILoadBalancerById %s %d loadbalancer found", loadbalancerId, len(lbs))
   122  		return nil, cloudprovider.ErrNotFound
   123  	}
   124  }
   125  
   126  func (self *SRegion) GetILoadBalancerAclById(aclId string) (cloudprovider.ICloudLoadbalancerAcl, error) {
   127  	return nil, nil
   128  }
   129  
   130  // https://cloud.tencent.com/document/api/214/30692
   131  // todo: 1. 支持跨地域绑定负载均衡 及 https://cloud.tencent.com/document/product/214/12014
   132  // todo: 2. 支持指定Project。 ProjectId可以通过 DescribeProject 接口获取。不填则属于默认项目。
   133  func (self *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (cloudprovider.ICloudLoadbalancer, error) {
   134  	params := map[string]string{
   135  		"LoadBalancerName": loadbalancer.Name,
   136  		"VpcId":            loadbalancer.VpcID,
   137  	}
   138  
   139  	LoadBalancerType := "INTERNAL"
   140  	if loadbalancer.AddressType == api.LB_ADDR_TYPE_INTERNET {
   141  		LoadBalancerType = "OPEN"
   142  		switch loadbalancer.ChargeType {
   143  		case api.LB_CHARGE_TYPE_BY_BANDWIDTH:
   144  			pkgs, _, err := self.GetBandwidthPackages([]string{}, 0, 50)
   145  			if err != nil {
   146  				return nil, errors.Wrapf(err, "GetBandwidthPackages")
   147  			}
   148  			bps := loadbalancer.EgressMbps
   149  			if bps == 0 {
   150  				bps = 200
   151  			}
   152  			if len(pkgs) > 0 {
   153  				pkgId := pkgs[0].BandwidthPackageId
   154  				for _, pkg := range pkgs {
   155  					if len(pkg.ResourceSet) < 100 {
   156  						pkgId = pkg.BandwidthPackageId
   157  						break
   158  					}
   159  				}
   160  				params["BandwidthPackageId"] = pkgId
   161  				params["InternetAccessible.InternetChargeType"] = "BANDWIDTH_PACKAGE"
   162  				params["InternetAccessible.InternetMaxBandwidthOut"] = fmt.Sprintf("%d", bps)
   163  			} else {
   164  				params["InternetAccessible.InternetChargeType"] = "BANDWIDTH_POSTPAID_BY_HOUR"
   165  				params["InternetAccessible.InternetMaxBandwidthOut"] = fmt.Sprintf("%d", bps)
   166  			}
   167  		default:
   168  			bps := loadbalancer.EgressMbps
   169  			if bps == 0 {
   170  				bps = 200
   171  			}
   172  			params["InternetAccessible.InternetChargeType"] = "TRAFFIC_POSTPAID_BY_HOUR"
   173  			params["InternetAccessible.InternetMaxBandwidthOut"] = fmt.Sprintf("%d", bps)
   174  
   175  		}
   176  	}
   177  
   178  	params["LoadBalancerType"] = LoadBalancerType
   179  
   180  	if len(loadbalancer.ProjectId) > 0 {
   181  		params["ProjectId"] = loadbalancer.ProjectId
   182  	}
   183  
   184  	if loadbalancer.AddressType != api.LB_ADDR_TYPE_INTERNET {
   185  		params["SubnetId"] = loadbalancer.NetworkIDs[0]
   186  	} else {
   187  		// 公网类型ELB可支持多可用区
   188  		if len(loadbalancer.ZoneID) > 0 {
   189  			if len(loadbalancer.SlaveZoneID) > 0 {
   190  				params["MasterZoneId"] = loadbalancer.ZoneID
   191  			} else {
   192  				params["ZoneId"] = loadbalancer.ZoneID
   193  			}
   194  		}
   195  	}
   196  	i := 0
   197  	for k, v := range loadbalancer.Tags {
   198  		params[fmt.Sprintf("Tags.%d.TagKey", i)] = k
   199  		params[fmt.Sprintf("Tags.%d.TagValue", i)] = v
   200  		i++
   201  	}
   202  
   203  	resp, err := func() (jsonutils.JSONObject, error) {
   204  		_resp, err := self.clbRequest("CreateLoadBalancer", params)
   205  		if err != nil {
   206  			// 兼容不支持指定zone的账号
   207  			if e, ok := err.(*sdkerrors.TencentCloudSDKError); ok && e.Code == "InvalidParameterValue" {
   208  				delete(params, "ZoneId")
   209  				delete(params, "MasterZoneId")
   210  				return self.clbRequest("CreateLoadBalancer", params)
   211  			}
   212  		}
   213  		return _resp, err
   214  	}()
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	requestId, err := resp.GetString("RequestId")
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	lbs, err := resp.GetArray("LoadBalancerIds")
   225  	if err != nil || len(lbs) != 1 {
   226  		log.Debugf("CreateILoadBalancer %s", resp.String())
   227  		return nil, err
   228  	}
   229  
   230  	err = self.WaitLBTaskSuccess(requestId, 5*time.Second, 60*time.Second)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  
   235  	lbId, err := lbs[0].GetString()
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  
   240  	return self.GetLoadbalancer(lbId)
   241  }
   242  
   243  func (self *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) {
   244  	return nil, cloudprovider.ErrNotSupported
   245  }
   246  
   247  // todo:目前onecloud端只能指定服务器端证书。需要兼容客户端证书?
   248  // todo:支持指定Project。
   249  // todo: 已过期的证书不能上传也不能关联资源
   250  func (self *SRegion) CreateILoadBalancerCertificate(input *cloudprovider.SLoadbalancerCertificate) (cloudprovider.ICloudLoadbalancerCertificate, error) {
   251  	certId, err := self.CreateCertificate("", input.Certificate, input.PrivateKey, "SVR", input.Name)
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  
   256  	cert, err := self.GetCertificate(certId)
   257  	if err != nil {
   258  		log.Debugf("GetCertificate %s failed", certId)
   259  		return nil, err
   260  	}
   261  
   262  	return cert, nil
   263  }
   264  
   265  func (self *SRegion) GetId() string {
   266  	return self.Region
   267  }
   268  
   269  func (self *SRegion) GetName() string {
   270  	return self.RegionName
   271  }
   272  
   273  func (self *SRegion) GetI18n() cloudprovider.SModelI18nTable {
   274  	en := self.RegionName
   275  	table := cloudprovider.SModelI18nTable{}
   276  	table["name"] = cloudprovider.NewSModelI18nEntry(self.GetName()).CN(self.GetName()).EN(en)
   277  	return table
   278  }
   279  
   280  func (self *SRegion) GetGlobalId() string {
   281  	return fmt.Sprintf("%s/%s", CLOUD_PROVIDER_QCLOUD, self.Region)
   282  }
   283  
   284  func (self *SRegion) IsEmulated() bool {
   285  	return false
   286  }
   287  
   288  func (self *SRegion) GetProvider() string {
   289  	return CLOUD_PROVIDER_QCLOUD
   290  }
   291  
   292  func (self *SRegion) GetCloudEnv() string {
   293  	return ""
   294  }
   295  
   296  func (self *SRegion) CreateIVpc(opts *cloudprovider.VpcCreateOptions) (cloudprovider.ICloudVpc, error) {
   297  	params := make(map[string]string)
   298  	if len(opts.CIDR) > 0 {
   299  		params["CidrBlock"] = opts.CIDR
   300  	}
   301  	if len(opts.NAME) > 0 {
   302  		params["VpcName"] = opts.NAME
   303  	}
   304  	body, err := self.vpcRequest("CreateVpc", params)
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  	vpcId, err := body.GetString("Vpc", "VpcId")
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  	err = self.fetchInfrastructure()
   313  	if err != nil {
   314  		return nil, err
   315  	}
   316  	return self.GetIVpcById(vpcId)
   317  }
   318  
   319  func (self *SRegion) GetCosClient(bucket *SBucket) (*cos.Client, error) {
   320  	return self.client.getCosClient(bucket)
   321  }
   322  
   323  func (self *SRegion) GetClient() *SQcloudClient {
   324  	return self.client
   325  }
   326  
   327  func (self *SRegion) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
   328  	return self.GetInstance(id)
   329  }
   330  
   331  func (self *SRegion) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) {
   332  	return self.GetDisk(id)
   333  }
   334  
   335  func (self *SRegion) GetIEipById(eipId string) (cloudprovider.ICloudEIP, error) {
   336  	if len(eipId) == 0 {
   337  		return nil, cloudprovider.ErrNotFound
   338  	}
   339  	eips, total, err := self.GetEips(eipId, "", 0, 1)
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  	if total == 0 {
   344  		return nil, cloudprovider.ErrNotFound
   345  	}
   346  	if total > 1 {
   347  		return nil, cloudprovider.ErrDuplicateId
   348  	}
   349  	return &eips[0], nil
   350  }
   351  
   352  func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) {
   353  	eips, total, err := self.GetEips("", "", 0, 50)
   354  	if err != nil {
   355  		return nil, err
   356  	}
   357  	for len(eips) < total {
   358  		var parts []SEipAddress
   359  		parts, total, err = self.GetEips("", "", len(eips), 50)
   360  		if err != nil {
   361  			return nil, err
   362  		}
   363  		eips = append(eips, parts...)
   364  	}
   365  	ret := make([]cloudprovider.ICloudEIP, len(eips))
   366  	for i := 0; i < len(eips); i++ {
   367  		ret[i] = &eips[i]
   368  	}
   369  	return ret, nil
   370  }
   371  
   372  func (self *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
   373  	izones, err := self.GetIZones()
   374  	if err != nil {
   375  		return nil, err
   376  	}
   377  	for i := 0; i < len(izones); i += 1 {
   378  		ihost, err := izones[i].GetIHostById(id)
   379  		if err == nil {
   380  			return ihost, nil
   381  		} else if errors.Cause(err) != cloudprovider.ErrNotFound {
   382  			return nil, err
   383  		}
   384  	}
   385  	return nil, cloudprovider.ErrNotFound
   386  }
   387  
   388  func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
   389  	izones, err := self.GetIZones()
   390  	if err != nil {
   391  		return nil, err
   392  	}
   393  	for i := 0; i < len(izones); i += 1 {
   394  		istore, err := izones[i].GetIStorageById(id)
   395  		if err == nil {
   396  			return istore, nil
   397  		} else if errors.Cause(err) != cloudprovider.ErrNotFound {
   398  			return nil, err
   399  		}
   400  	}
   401  	return nil, cloudprovider.ErrNotFound
   402  }
   403  
   404  func (self *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) {
   405  	iHosts := make([]cloudprovider.ICloudHost, 0)
   406  
   407  	izones, err := self.GetIZones()
   408  	if err != nil {
   409  		return nil, err
   410  	}
   411  	for i := 0; i < len(izones); i += 1 {
   412  		iZoneHost, err := izones[i].GetIHosts()
   413  		if err != nil {
   414  			return nil, err
   415  		}
   416  		iHosts = append(iHosts, iZoneHost...)
   417  	}
   418  	return iHosts, nil
   419  }
   420  
   421  func (self *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
   422  	iStores := make([]cloudprovider.ICloudStorage, 0)
   423  
   424  	izones, err := self.GetIZones()
   425  	if err != nil {
   426  		return nil, err
   427  	}
   428  	for i := 0; i < len(izones); i += 1 {
   429  		iZoneStores, err := izones[i].GetIStorages()
   430  		if err != nil {
   431  			return nil, err
   432  		}
   433  		iStores = append(iStores, iZoneStores...)
   434  	}
   435  	return iStores, nil
   436  }
   437  
   438  func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) {
   439  	storageCache := self.getStoragecache()
   440  	if storageCache.GetGlobalId() == id {
   441  		return self.storageCache, nil
   442  	}
   443  	return nil, cloudprovider.ErrNotFound
   444  }
   445  
   446  func (self *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) {
   447  	storageCache := self.getStoragecache()
   448  	return []cloudprovider.ICloudStoragecache{storageCache}, nil
   449  }
   450  
   451  func (self *SRegion) updateInstance(instId string, name, desc, passwd, hostname string) error {
   452  	params := make(map[string]string)
   453  	params["InstanceId"] = instId
   454  	if len(name) > 0 {
   455  		params["InstanceName"] = name
   456  	}
   457  	if len(desc) > 0 {
   458  		params["Description"] = desc
   459  	}
   460  	if len(passwd) > 0 {
   461  		params["Password"] = passwd
   462  	}
   463  	if len(hostname) > 0 {
   464  		params["HostName"] = hostname
   465  	}
   466  	_, err := self.cvmRequest("ModifyInstanceAttribute", params, true)
   467  	return err
   468  }
   469  
   470  func (self *SRegion) UpdateInstancePassword(instId string, passwd string) error {
   471  	return self.updateInstance(instId, "", "", passwd, "")
   472  }
   473  
   474  func (self *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
   475  	ivpcs, err := self.GetIVpcs()
   476  	if err != nil {
   477  		return nil, err
   478  	}
   479  	for i := 0; i < len(ivpcs); i++ {
   480  		if ivpcs[i].GetGlobalId() == id {
   481  			return ivpcs[i], nil
   482  		}
   483  	}
   484  	return nil, cloudprovider.ErrNotFound
   485  }
   486  
   487  func (self *SRegion) getZoneById(id string) (*SZone, error) {
   488  	izones, err := self.GetIZones()
   489  	if err != nil {
   490  		return nil, err
   491  	}
   492  	for i := 0; i < len(izones); i += 1 {
   493  		zone := izones[i].(*SZone)
   494  		if zone.Zone == id {
   495  			return zone, nil
   496  		}
   497  	}
   498  	return nil, fmt.Errorf("no such zone %s", id)
   499  }
   500  
   501  func (self *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) {
   502  	if self.ivpcs == nil {
   503  		err := self.fetchInfrastructure()
   504  		if err != nil {
   505  			return nil, err
   506  		}
   507  	}
   508  	return self.ivpcs, nil
   509  }
   510  
   511  func (self *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) {
   512  	izones, err := self.GetIZones()
   513  	if err != nil {
   514  		return nil, err
   515  	}
   516  	for i := 0; i < len(izones); i += 1 {
   517  		if izones[i].GetGlobalId() == id {
   518  			return izones[i], nil
   519  		}
   520  	}
   521  	return nil, cloudprovider.ErrNotFound
   522  }
   523  
   524  func (self *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) {
   525  	if self.izones == nil {
   526  		var err error
   527  		err = self.fetchInfrastructure()
   528  		if err != nil {
   529  			return nil, err
   530  		}
   531  	}
   532  	return self.izones, nil
   533  }
   534  
   535  func (self *SRegion) _fetchZones() error {
   536  	params := make(map[string]string)
   537  	zones := make([]SZone, 0)
   538  	body, err := self.cvmRequest("DescribeZones", params, true)
   539  	if err != nil {
   540  		return err
   541  	}
   542  	err = body.Unmarshal(&zones, "ZoneSet")
   543  	if err != nil {
   544  		return err
   545  	}
   546  	self.izones = make([]cloudprovider.ICloudZone, len(zones))
   547  	for i := 0; i < len(zones); i++ {
   548  		zones[i].region = self
   549  		self.izones[i] = &zones[i]
   550  	}
   551  	return nil
   552  }
   553  
   554  func (self *SRegion) fetchInfrastructure() error {
   555  	err := self._fetchZones()
   556  	if err != nil {
   557  		return err
   558  	}
   559  	err = self.fetchIVpcs()
   560  	if err != nil {
   561  		return err
   562  	}
   563  	for i := 0; i < len(self.ivpcs); i += 1 {
   564  		for j := 0; j < len(self.izones); j += 1 {
   565  			zone := self.izones[j].(*SZone)
   566  			vpc := self.ivpcs[i].(*SVpc)
   567  			wire := SWire{zone: zone, vpc: vpc}
   568  			zone.addWire(&wire)
   569  			vpc.addWire(&wire)
   570  		}
   571  	}
   572  	return nil
   573  }
   574  
   575  func (self *SRegion) DeleteVpc(vpcId string) error {
   576  	params := make(map[string]string)
   577  	params["VpcId"] = vpcId
   578  
   579  	_, err := self.vpcRequest("DeleteVpc", params)
   580  	return err
   581  }
   582  
   583  func (self *SRegion) getVpc(vpcId string) (*SVpc, error) {
   584  	vpcs, total, err := self.GetVpcs([]string{vpcId}, 0, 1)
   585  	if err != nil {
   586  		return nil, err
   587  	}
   588  	if total > 1 {
   589  		return nil, cloudprovider.ErrDuplicateId
   590  	}
   591  	if total == 0 {
   592  		return nil, cloudprovider.ErrNotFound
   593  	}
   594  	vpcs[0].region = self
   595  	return &vpcs[0], nil
   596  }
   597  
   598  func (self *SRegion) fetchIVpcs() error {
   599  	vpcs := make([]SVpc, 0)
   600  	for {
   601  		part, total, err := self.GetVpcs(nil, len(vpcs), 50)
   602  		if err != nil {
   603  			return err
   604  		}
   605  		vpcs = append(vpcs, part...)
   606  		if len(vpcs) >= total {
   607  			break
   608  		}
   609  	}
   610  	self.ivpcs = make([]cloudprovider.ICloudVpc, len(vpcs))
   611  	for i := 0; i < len(vpcs); i += 1 {
   612  		vpcs[i].region = self
   613  		self.ivpcs[i] = &vpcs[i]
   614  	}
   615  	return nil
   616  }
   617  
   618  func (self *SRegion) GetVpcs(vpcIds []string, offset int, limit int) ([]SVpc, int, error) {
   619  	if limit > 50 || limit <= 0 {
   620  		limit = 50
   621  	}
   622  	params := make(map[string]string)
   623  	params["Limit"] = fmt.Sprintf("%d", limit)
   624  	params["Offset"] = fmt.Sprintf("%d", offset)
   625  	if vpcIds != nil && len(vpcIds) > 0 {
   626  		for index, vpcId := range vpcIds {
   627  			params[fmt.Sprintf("VpcIds.%d", index)] = vpcId
   628  		}
   629  	}
   630  	body, err := self.vpcRequest("DescribeVpcs", params)
   631  	if err != nil {
   632  		return nil, 0, err
   633  	}
   634  	vpcs := make([]SVpc, 0)
   635  	err = body.Unmarshal(&vpcs, "VpcSet")
   636  	if err != nil {
   637  		log.Errorf("Unmarshal vpc fail %s", err)
   638  		return nil, 0, err
   639  	}
   640  	total, _ := body.Float("TotalCount")
   641  	return vpcs, int(total), nil
   642  }
   643  
   644  func (self *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo {
   645  	if info, ok := LatitudeAndLongitude[self.Region]; ok {
   646  		return info
   647  	}
   648  	return cloudprovider.SGeographicInfo{}
   649  }
   650  
   651  func (self *SRegion) GetStatus() string {
   652  	if self.RegionState == "AVAILABLE" {
   653  		return api.CLOUD_REGION_STATUS_INSERVER
   654  	}
   655  	return api.CLOUD_REGION_STATUS_OUTOFSERVICE
   656  }
   657  
   658  func (self *SRegion) Refresh() error {
   659  	// do nothing
   660  	return nil
   661  }
   662  
   663  // 容器
   664  func (self *SRegion) tkeRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   665  	params["Region"] = self.Region
   666  	return self.client.tkeRequest(apiName, params)
   667  }
   668  
   669  func (self *SRegion) vpcRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   670  	params["Region"] = self.Region
   671  	return self.client.vpcRequest(apiName, params)
   672  }
   673  
   674  func (self *SRegion) auditRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   675  	params["Region"] = self.Region
   676  	return self.client.auditRequest(apiName, params)
   677  }
   678  
   679  func (self *SRegion) vpc2017Request(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   680  	params["Region"] = self.Region
   681  	return self.client.vpc2017Request(apiName, params)
   682  }
   683  
   684  func (self *SRegion) cvmRequest(apiName string, params map[string]string, retry bool) (jsonutils.JSONObject, error) {
   685  	params["Region"] = self.Region
   686  	return self.client.jsonRequest(apiName, params, retry)
   687  }
   688  
   689  func (self *SRegion) accountRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   690  	return self.client.accountRequestRequest(apiName, params)
   691  }
   692  
   693  func (self *SRegion) cbsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   694  	params["Region"] = self.Region
   695  	return self.client.cbsRequest(apiName, params)
   696  }
   697  
   698  func (self *SRegion) clbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   699  	params["Region"] = self.Region
   700  	return self.client.clbRequest(apiName, params)
   701  }
   702  
   703  func (self *SRegion) lbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   704  	params["Region"] = self.Region
   705  	return self.client.lbRequest(apiName, params)
   706  }
   707  
   708  func (self *SRegion) cdbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   709  	params["Region"] = self.Region
   710  	return self.client.cdbRequest(apiName, params)
   711  }
   712  
   713  func (self *SRegion) mariadbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   714  	params["Region"] = self.Region
   715  	return self.client.mariadbRequest(apiName, params)
   716  }
   717  
   718  func (self *SRegion) postgresRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   719  	params["Region"] = self.Region
   720  	return self.client.postgresRequest(apiName, params)
   721  }
   722  
   723  func (self *SRegion) sqlserverRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   724  	params["Region"] = self.Region
   725  	return self.client.sqlserverRequest(apiName, params)
   726  }
   727  
   728  // deprecated
   729  func (self *SRegion) wssRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   730  	return self.client.wssRequest(apiName, params)
   731  }
   732  
   733  func (self *SRegion) sslRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   734  	return self.client.sslRequest(apiName, params)
   735  }
   736  
   737  func (self *SRegion) kafkaRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   738  	params["Region"] = self.Region
   739  	return self.client.kafkaRequest(apiName, params)
   740  }
   741  
   742  func (self *SRegion) redisRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   743  	params["Region"] = self.Region
   744  	return self.client.redisRequest(apiName, params)
   745  }
   746  
   747  func (self *SRegion) dcdbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   748  	params["Region"] = self.Region
   749  	return self.client.dcdbRequest(apiName, params)
   750  }
   751  
   752  func (self *SRegion) mongodbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   753  	params["Region"] = self.Region
   754  	return self.client.mongodbRequest(apiName, params)
   755  }
   756  
   757  // Elasticsearch
   758  func (self *SRegion) esRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   759  	params["Region"] = self.Region
   760  	return self.client.esRequest(apiName, params)
   761  }
   762  
   763  func (self *SRegion) memcachedRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
   764  	params["Region"] = self.Region
   765  	return self.client.memcachedRequest(apiName, params)
   766  }
   767  
   768  func (self *SRegion) GetNetworks(ids []string, vpcId string, offset int, limit int) ([]SNetwork, int, error) {
   769  	if limit > 50 || limit <= 0 {
   770  		limit = 50
   771  	}
   772  	params := make(map[string]string)
   773  	params["Limit"] = fmt.Sprintf("%d", limit)
   774  	params["Offset"] = fmt.Sprintf("%d", offset)
   775  	base := 0
   776  	if ids != nil && len(ids) > 0 {
   777  		for index, networkId := range ids {
   778  			params[fmt.Sprintf("SubnetIds.%d", index)] = networkId
   779  		}
   780  		base += len(ids)
   781  	}
   782  	if len(vpcId) > 0 {
   783  		params["Filters.0.Name"] = "vpc-id"
   784  		params["Filters.0.Values.0"] = vpcId
   785  	}
   786  
   787  	body, err := self.vpcRequest("DescribeSubnets", params)
   788  	if err != nil {
   789  		log.Errorf("DescribeSubnets fail %s", err)
   790  		return nil, 0, err
   791  	}
   792  
   793  	networks := make([]SNetwork, 0)
   794  	err = body.Unmarshal(&networks, "SubnetSet")
   795  	if err != nil {
   796  		log.Errorf("Unmarshal network fail %s", err)
   797  		return nil, 0, err
   798  	}
   799  	total, _ := body.Float("TotalCount")
   800  	return networks, int(total), nil
   801  }
   802  
   803  func (self *SRegion) GetNetwork(networkId string) (*SNetwork, error) {
   804  	networks, total, err := self.GetNetworks([]string{networkId}, "", 0, 1)
   805  	if err != nil {
   806  		return nil, err
   807  	}
   808  	if total > 1 {
   809  		return nil, cloudprovider.ErrDuplicateId
   810  	}
   811  	if total == 0 {
   812  		return nil, cloudprovider.ErrNotFound
   813  	}
   814  	return &networks[0], nil
   815  }
   816  
   817  func (self *SRegion) getStoragecache() *SStoragecache {
   818  	if self.storageCache == nil {
   819  		self.storageCache = &SStoragecache{region: self}
   820  	}
   821  	return self.storageCache
   822  }
   823  
   824  func (self *SRegion) GetMatchInstanceTypes(cpu int, memMB int, gpu int, zoneId string) ([]SInstanceType, error) {
   825  	if self.instanceTypes == nil {
   826  		types, err := self.GetInstanceTypes()
   827  		if err != nil {
   828  			log.Errorf("GetInstanceTypes %s", err)
   829  			return nil, err
   830  		}
   831  		self.instanceTypes = types
   832  	}
   833  
   834  	var available []string
   835  	if len(zoneId) > 0 {
   836  		zone, err := self.getZoneById(zoneId)
   837  		if err != nil {
   838  			return nil, err
   839  		}
   840  		available = zone.getAvaliableInstanceTypes()
   841  	}
   842  	ret := make([]SInstanceType, 0)
   843  	for _, t := range self.instanceTypes {
   844  		if t.CPU == cpu && memMB == t.memoryMB() && gpu == t.GPU {
   845  			if available == nil || utils.IsInStringArray(t.InstanceType, available) {
   846  				ret = append(ret, t)
   847  			}
   848  		}
   849  	}
   850  	return ret, nil
   851  }
   852  
   853  func (self *SRegion) CreateInstanceSimple(name string, imgId string, cpu int, memGB int, storageType string, dataDiskSizesGB []int, networkId string, passwd string, publicKey string, secgroup string, tags map[string]string) (*SInstance, error) {
   854  	izones, err := self.GetIZones()
   855  	if err != nil {
   856  		return nil, err
   857  	}
   858  	for i := 0; i < len(izones); i += 1 {
   859  		z := izones[i].(*SZone)
   860  		log.Debugf("Search in zone %s", z.Zone)
   861  		net := z.getNetworkById(networkId)
   862  		if net != nil {
   863  			desc := &cloudprovider.SManagedVMCreateConfig{
   864  				Name:              name,
   865  				ExternalImageId:   imgId,
   866  				SysDisk:           cloudprovider.SDiskInfo{SizeGB: 0, StorageType: storageType},
   867  				Cpu:               cpu,
   868  				MemoryMB:          memGB * 1024,
   869  				ExternalNetworkId: networkId,
   870  				Password:          passwd,
   871  				DataDisks:         []cloudprovider.SDiskInfo{},
   872  				PublicKey:         publicKey,
   873  
   874  				Tags: tags,
   875  
   876  				ExternalSecgroupId: secgroup,
   877  			}
   878  			for _, sizeGB := range dataDiskSizesGB {
   879  				desc.DataDisks = append(desc.DataDisks, cloudprovider.SDiskInfo{SizeGB: sizeGB, StorageType: storageType})
   880  			}
   881  			inst, err := z.getHost().CreateVM(desc)
   882  			if err != nil {
   883  				return nil, err
   884  			}
   885  			return inst.(*SInstance), nil
   886  		}
   887  	}
   888  	return nil, fmt.Errorf("cannot find network %s", networkId)
   889  }
   890  
   891  func (self *SRegion) instanceOperation(instanceId string, opname string, extra map[string]string, retry bool) error {
   892  	params := make(map[string]string)
   893  	params["InstanceIds.0"] = instanceId
   894  	if extra != nil && len(extra) > 0 {
   895  		for k, v := range extra {
   896  			params[k] = v
   897  		}
   898  	}
   899  	_, err := self.cvmRequest(opname, params, retry)
   900  	return err
   901  }
   902  
   903  func (self *SRegion) GetInstanceVNCUrl(instanceId string) (string, error) {
   904  	params := make(map[string]string)
   905  	params["InstanceId"] = instanceId
   906  	body, err := self.cvmRequest("DescribeInstanceVncUrl", params, true)
   907  	if err != nil {
   908  		return "", err
   909  	}
   910  	return body.GetString("InstanceVncUrl")
   911  }
   912  
   913  func (self *SRegion) GetInstanceStatus(instanceId string) (string, error) {
   914  	instance, err := self.GetInstance(instanceId)
   915  	if err != nil {
   916  		return "", err
   917  	}
   918  	return instance.InstanceState, nil
   919  }
   920  
   921  func (self *SRegion) QueryAccountBalance() (*SAccountBalance, error) {
   922  	return self.client.QueryAccountBalance()
   923  }
   924  
   925  func (self *SRegion) getCosEndpoint() string {
   926  	return fmt.Sprintf("cos.%s.myqcloud.com", self.GetId())
   927  }
   928  
   929  func (self *SRegion) getCosWebsiteEndpoint() string {
   930  	return fmt.Sprintf("cos-website.%s.myqcloud.com", self.GetId())
   931  }
   932  
   933  func (region *SRegion) GetIBuckets() ([]cloudprovider.ICloudBucket, error) {
   934  	iBuckets, err := region.client.getIBuckets()
   935  	if err != nil {
   936  		return nil, err
   937  	}
   938  	ret := make([]cloudprovider.ICloudBucket, 0)
   939  	for i := range iBuckets {
   940  		bucket := iBuckets[i].(*SBucket)
   941  		if bucket.region.GetId() != region.GetId() {
   942  			continue
   943  		}
   944  		ret = append(ret, iBuckets[i])
   945  	}
   946  	return ret, nil
   947  }
   948  
   949  func (region *SRegion) CreateIBucket(name string, storageClassStr string, aclStr string) error {
   950  	bucket := &SBucket{
   951  		region: region,
   952  		Name:   name,
   953  	}
   954  	coscli, err := region.GetCosClient(bucket)
   955  	if err != nil {
   956  		return errors.Wrap(err, "GetCosClient")
   957  	}
   958  	opts := &cos.BucketPutOptions{}
   959  	if len(aclStr) > 0 {
   960  		if utils.IsInStringArray(aclStr, []string{
   961  			"private", "public-read", "public-read-write", "authenticated-read",
   962  		}) {
   963  			opts.XCosACL = aclStr
   964  		} else {
   965  			return errors.Error("invalid acl")
   966  		}
   967  	}
   968  	_, err = coscli.Bucket.Put(context.Background(), opts)
   969  	if err != nil {
   970  		return errors.Wrap(err, "coscli.Bucket.Put")
   971  	}
   972  	region.client.invalidateIBuckets()
   973  	return nil
   974  }
   975  
   976  func cosHttpCode(err error) int {
   977  	if httpErr, ok := err.(*cos.ErrorResponse); ok {
   978  		return httpErr.Response.StatusCode
   979  	}
   980  	return -1
   981  }
   982  
   983  func (region *SRegion) DeleteIBucket(name string) error {
   984  	bucket := &SBucket{
   985  		region: region,
   986  		Name:   name,
   987  	}
   988  	coscli, err := region.GetCosClient(bucket)
   989  	if err != nil {
   990  		return errors.Wrap(err, "GetCosClient")
   991  	}
   992  	_, err = coscli.Bucket.Delete(context.Background())
   993  	if err != nil {
   994  		if cosHttpCode(err) == 404 {
   995  			return nil
   996  		}
   997  		return errors.Wrap(err, "DeleteBucket")
   998  	}
   999  	return nil
  1000  }
  1001  
  1002  func (region *SRegion) IBucketExist(name string) (bool, error) {
  1003  	bucket := &SBucket{
  1004  		region: region,
  1005  		Name:   name,
  1006  	}
  1007  	coscli, err := region.GetCosClient(bucket)
  1008  	if err != nil {
  1009  		return false, errors.Wrap(err, "GetCosClient")
  1010  	}
  1011  	_, err = coscli.Bucket.Head(context.Background())
  1012  	if err != nil {
  1013  		if cosHttpCode(err) == 404 {
  1014  			return false, nil
  1015  		}
  1016  		return false, errors.Wrap(err, "BucketExists")
  1017  	}
  1018  	return true, nil
  1019  }
  1020  
  1021  func (region *SRegion) GetIBucketById(name string) (cloudprovider.ICloudBucket, error) {
  1022  	return cloudprovider.GetIBucketById(region, name)
  1023  }
  1024  
  1025  func (region *SRegion) GetIBucketByName(name string) (cloudprovider.ICloudBucket, error) {
  1026  	return region.GetIBucketById(name)
  1027  }
  1028  
  1029  func (self *SRegion) GetISecurityGroupById(secgroupId string) (cloudprovider.ICloudSecurityGroup, error) {
  1030  	secgroups, total, err := self.GetSecurityGroups([]string{secgroupId}, "", "", 0, 1)
  1031  	if err != nil {
  1032  		return nil, errors.Wrapf(err, "GetSecurityGroups(%s)", secgroupId)
  1033  	}
  1034  	if total < 1 {
  1035  		return nil, cloudprovider.ErrNotFound
  1036  	}
  1037  	secgroups[0].region = self
  1038  	return &secgroups[0], nil
  1039  }
  1040  
  1041  func (self *SRegion) GetISecurityGroupByName(opts *cloudprovider.SecurityGroupFilterOptions) (cloudprovider.ICloudSecurityGroup, error) {
  1042  	secgroups, total, err := self.GetSecurityGroups([]string{}, opts.VpcId, opts.Name, 0, 0)
  1043  	if err != nil {
  1044  		return nil, err
  1045  	}
  1046  	if total == 0 {
  1047  		return nil, cloudprovider.ErrNotFound
  1048  	}
  1049  	if total > 1 {
  1050  		return nil, cloudprovider.ErrDuplicateId
  1051  	}
  1052  	return &secgroups[0], nil
  1053  }
  1054  
  1055  func (self *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
  1056  	return self.CreateSecurityGroup(conf.Name, conf.ProjectId, conf.Desc)
  1057  }
  1058  
  1059  func (region *SRegion) GetCapabilities() []string {
  1060  	return region.client.GetCapabilities()
  1061  }
  1062  
  1063  func (region *SRegion) GetIElasticcaches() ([]cloudprovider.ICloudElasticcache, error) {
  1064  	caches, err := region.GetCloudElasticcaches("")
  1065  	if err != nil {
  1066  		return nil, errors.Wrap(err, "GetCloudElasticcaches")
  1067  	}
  1068  
  1069  	ret := []cloudprovider.ICloudElasticcache{}
  1070  	for i := range caches {
  1071  		cache := caches[i]
  1072  		cache.region = region
  1073  		ret = append(ret, &cache)
  1074  	}
  1075  
  1076  	mems := []SMemcached{}
  1077  	offset := 0
  1078  	for {
  1079  		part, total, err := region.GetMemcaches(nil, 100, offset)
  1080  		if err != nil {
  1081  			return nil, errors.Wrapf(err, "GetMemcaches")
  1082  		}
  1083  		mems = append(mems, part...)
  1084  		if len(mems) >= total {
  1085  			break
  1086  		}
  1087  		offset += len(part)
  1088  	}
  1089  	for i := range mems {
  1090  		mems[i].region = region
  1091  		ret = append(ret, &mems[i])
  1092  	}
  1093  
  1094  	return ret, nil
  1095  }
  1096  
  1097  func (region *SRegion) GetIElasticcacheById(id string) (cloudprovider.ICloudElasticcache, error) {
  1098  	if strings.HasPrefix(id, "cmem-") {
  1099  		memcacheds, _, err := region.GetMemcaches([]string{id}, 1, 0)
  1100  		if err != nil {
  1101  			return nil, errors.Wrapf(err, "GetMemcaches")
  1102  		}
  1103  		for i := range memcacheds {
  1104  			if memcacheds[i].GetGlobalId() == id {
  1105  				memcacheds[i].region = region
  1106  				return &memcacheds[i], nil
  1107  			}
  1108  		}
  1109  		return nil, errors.Wrapf(cloudprovider.ErrNotFound, id)
  1110  	}
  1111  	caches, err := region.GetCloudElasticcaches(id)
  1112  	if err != nil {
  1113  		return nil, errors.Wrap(err, "GetCloudElasticcaches")
  1114  	}
  1115  
  1116  	for i := range caches {
  1117  		if caches[i].GetGlobalId() == id {
  1118  			caches[i].region = region
  1119  			return &caches[i], nil
  1120  		}
  1121  	}
  1122  
  1123  	return nil, cloudprovider.ErrNotFound
  1124  }
  1125  
  1126  // DescribeProductInfo 可以查询在售可用区信息
  1127  // https://cloud.tencent.com/document/product/239/20026
  1128  func (r *SRegion) CreateIElasticcaches(ec *cloudprovider.SCloudElasticCacheInput) (cloudprovider.ICloudElasticcache, error) {
  1129  	params := map[string]string{}
  1130  	if len(ec.ZoneIds) == 0 {
  1131  		return nil, fmt.Errorf("CreateIElasticcaches zone id should not be empty.")
  1132  	}
  1133  
  1134  	zoneId, ok := zoneIdMaps[ec.ZoneIds[0]]
  1135  	if !ok {
  1136  		return nil, fmt.Errorf("can't convert zone %s to integer id", ec.ZoneIds[0])
  1137  	}
  1138  
  1139  	if len(ec.ZoneIds) > 1 {
  1140  		for i := range ec.ZoneIds {
  1141  			if i == 0 {
  1142  				params[fmt.Sprintf("NodeSet.%d.NodeType", i)] = "0"
  1143  				params[fmt.Sprintf("NodeSet.%d.ZoneId", i)] = fmt.Sprintf("%d", zoneId)
  1144  			} else {
  1145  				_z, ok := zoneIdMaps[ec.ZoneIds[i]]
  1146  				if !ok {
  1147  					return nil, fmt.Errorf("can't convert zone %s to integer id", ec.ZoneIds[i])
  1148  				}
  1149  				params[fmt.Sprintf("NodeSet.%d.NodeType", i)] = "1"
  1150  				params[fmt.Sprintf("NodeSet.%d.ZoneId", i)] = fmt.Sprintf("%d", _z)
  1151  			}
  1152  		}
  1153  	}
  1154  
  1155  	spec, err := parseLocalInstanceSpec(ec.InstanceType)
  1156  	if err != nil {
  1157  		return nil, errors.Wrap(err, "parseLocalInstanceSpec")
  1158  	}
  1159  
  1160  	params["InstanceName"] = ec.InstanceName
  1161  	if len(ec.ProjectId) > 0 {
  1162  		params["ProjectId"] = ec.ProjectId
  1163  	}
  1164  	params["ZoneId"] = fmt.Sprintf("%d", zoneId)
  1165  	params["TypeId"] = spec.TypeId
  1166  	params["MemSize"] = strconv.Itoa(spec.MemSizeMB)
  1167  	params["RedisShardNum"] = spec.RedisShardNum
  1168  	params["RedisReplicasNum"] = spec.RedisReplicasNum
  1169  	params["GoodsNum"] = "1"
  1170  	if ec.NetworkType == api.LB_NETWORK_TYPE_VPC {
  1171  		params["VpcId"] = ec.VpcId
  1172  		params["SubnetId"] = ec.NetworkId
  1173  
  1174  		for i := range ec.SecurityGroupIds {
  1175  			params[fmt.Sprintf("SecurityGroupIdList.%d", i)] = ec.SecurityGroupIds[i]
  1176  		}
  1177  	}
  1178  	params["Period"] = "1"
  1179  	params["BillingMode"] = "0"
  1180  	if ec.BillingCycle != nil && ec.BillingCycle.GetMonths() >= 1 {
  1181  		params["Period"] = strconv.Itoa(ec.BillingCycle.GetMonths())
  1182  		params["BillingMode"] = "1"
  1183  		// 自动续费
  1184  		if ec.BillingCycle.AutoRenew {
  1185  			params["AutoRenew"] = "1"
  1186  		}
  1187  	}
  1188  
  1189  	if len(ec.Password) > 0 {
  1190  		params["NoAuth"] = "false"
  1191  		params["Password"] = ec.Password
  1192  	} else {
  1193  		params["NoAuth"] = "true"
  1194  	}
  1195  
  1196  	resp, err := r.redisRequest("CreateInstances", params)
  1197  	if err != nil {
  1198  		return nil, errors.Wrap(err, "CreateInstances")
  1199  	}
  1200  
  1201  	instanceId := ""
  1202  	if resp.Contains("InstanceIds") {
  1203  		ids := []string{}
  1204  		if err := resp.Unmarshal(&ids, "InstanceIds"); err != nil {
  1205  			log.Debugf("Unmarshal.InstanceIds %s", resp)
  1206  		} else {
  1207  			if len(ids) > 0 {
  1208  				instanceId = ids[0]
  1209  			}
  1210  		}
  1211  	}
  1212  
  1213  	// try to fetch instance id from deal id
  1214  	if len(instanceId) == 0 {
  1215  		dealId, err := resp.GetString("DealId")
  1216  		if err != nil {
  1217  			return nil, errors.Wrap(err, "dealId")
  1218  		}
  1219  
  1220  		// maybe is a dealId not a instance ID.
  1221  		if strings.HasPrefix(dealId, "crs-") {
  1222  			instanceId = dealId
  1223  		} else {
  1224  			err = cloudprovider.Wait(5*time.Second, 900*time.Second, func() (bool, error) {
  1225  				_realInstanceId, err := r.GetElasticcacheIdByDeal(dealId)
  1226  				if err != nil {
  1227  					return false, nil
  1228  				}
  1229  
  1230  				instanceId = _realInstanceId
  1231  				return true, nil
  1232  			})
  1233  			if err != nil {
  1234  				return nil, errors.Wrap(err, "Wait.GetElasticcacheIdByDeal")
  1235  			}
  1236  		}
  1237  	}
  1238  
  1239  	err = r.SetResourceTags("redis", "instance", []string{instanceId}, ec.Tags, false)
  1240  	if err != nil {
  1241  		log.Errorf("SetResourceTags(redis:%s,error:%s)", instanceId, err)
  1242  	}
  1243  	return r.GetIElasticcacheById(instanceId)
  1244  }