yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/loadbalancer.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  	"yunion.io/x/jsonutils"
    25  	"yunion.io/x/log"
    26  	"yunion.io/x/pkg/errors"
    27  	"yunion.io/x/pkg/utils"
    28  
    29  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    30  	"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
    31  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    32  	"yunion.io/x/cloudmux/pkg/multicloud"
    33  )
    34  
    35  const (
    36  	LB_ADDR_TYPE_INTERNAL = "INTERNAL"
    37  	LB_ADDR_TYPE_OPEN     = "OPEN"
    38  )
    39  
    40  type LB_TYPE int64
    41  
    42  const (
    43  	LB_TYPE_CLASSIC     = LB_TYPE(0)
    44  	LB_TYPE_APPLICATION = LB_TYPE(1)
    45  )
    46  
    47  /*
    48  todo:
    49  1.统一LB 证书fingerprint算法.另外缺少一个回写指纹算法到数据库的方法。
    50  2.需要同步腾讯云LB 所在的project
    51  */
    52  
    53  // https://cloud.tencent.com/document/api/214/30694#LoadBalancer
    54  type SLoadbalancer struct {
    55  	multicloud.SLoadbalancerBase
    56  	QcloudTags
    57  	region *SRegion
    58  
    59  	Status            int64     `json:"Status"` // 0:创建中,1:正常运行
    60  	Domain            string    `json:"Domain"`
    61  	VpcID             string    `json:"VpcId"`
    62  	Log               string    `json:"Log"`
    63  	ProjectID         int64     `json:"ProjectId"`
    64  	Snat              bool      `json:"Snat"`
    65  	LoadBalancerID    string    `json:"LoadBalancerId"`
    66  	LoadBalancerVips  []string  `json:"LoadBalancerVips"`
    67  	LoadBalancerType  string    `json:"LoadBalancerType"` // 负载均衡实例的网络类型: OPEN:公网属性, INTERNAL:内网属性。
    68  	LoadBalancerName  string    `json:"LoadBalancerName"`
    69  	Forward           LB_TYPE   `json:"Forward"` // 应用型负载均衡标识,1:应用型负载均衡,0:传统型的负载均衡。
    70  	StatusTime        time.Time `json:"StatusTime"`
    71  	OpenBGP           int64     `json:"OpenBgp"` // 高防 LB 的标识,1:高防负载均衡 0:非高防负载均衡。
    72  	CreateTime        time.Time `json:"CreateTime"`
    73  	Isolation         int64     `json:"Isolation"` // 0:表示未被隔离,1:表示被隔离。
    74  	SubnetId          string    `json:"SubnetId"`
    75  	BackupZoneSet     []ZoneSet `json:"BackupZoneSet"`
    76  	MasterZone        ZoneSet   `json:"MasterZone"`
    77  	NetworkAttributes struct {
    78  		InternetChargeType      string
    79  		InternetMaxBandwidthOut int
    80  	}
    81  }
    82  
    83  type ZoneSet struct {
    84  	Zone     string `json:"Zone"`
    85  	ZoneID   int64  `json:"ZoneId"`
    86  	ZoneName string `json:"ZoneName"`
    87  }
    88  
    89  func (self *SLoadbalancer) GetLoadbalancerSpec() string {
    90  	return ""
    91  }
    92  
    93  func (self *SLoadbalancer) GetChargeType() string {
    94  	if len(self.NetworkAttributes.InternetChargeType) > 0 && self.NetworkAttributes.InternetChargeType != "TRAFFIC_POSTPAID_BY_HOUR" {
    95  		return api.LB_CHARGE_TYPE_BY_BANDWIDTH
    96  	}
    97  	return api.LB_CHARGE_TYPE_BY_TRAFFIC
    98  }
    99  
   100  func (self *SLoadbalancer) GetEgressMbps() int {
   101  	return self.NetworkAttributes.InternetMaxBandwidthOut
   102  }
   103  
   104  // https://cloud.tencent.com/document/product/214/30689
   105  func (self *SLoadbalancer) Delete(ctx context.Context) error {
   106  	lockman.LockRawObject(ctx, "qcloud.SLoadbalancer.Delete", self.region.client.ownerId)
   107  	defer lockman.ReleaseRawObject(ctx, "qcloud.SLoadbalancer.Delete", self.region.client.ownerId)
   108  
   109  	if self.Forward == LB_TYPE_APPLICATION {
   110  		_, err := self.region.DeleteLoadbalancer(self.GetId())
   111  		if err != nil {
   112  			return err
   113  		}
   114  	} else {
   115  		_, err := self.region.DeleteClassicLoadbalancer(self.GetId())
   116  		if err != nil {
   117  			return err
   118  		}
   119  	}
   120  
   121  	return cloudprovider.WaitDeleted(self, 5*time.Second, 60*time.Second)
   122  }
   123  
   124  // 腾讯云loadbalance不支持启用/禁用
   125  func (self *SLoadbalancer) Start() error {
   126  	return nil
   127  }
   128  
   129  func (self *SLoadbalancer) Stop() error {
   130  	return cloudprovider.ErrNotSupported
   131  }
   132  
   133  // 腾讯云无后端服务器组
   134  // todo: 是否返回一个fake的后端服务器组
   135  func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
   136  	return nil, cloudprovider.ErrNotSupported
   137  }
   138  
   139  func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
   140  	groups, err := self.GetILoadBalancerBackendGroups()
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	for _, group := range groups {
   146  		if group.GetId() == groupId {
   147  			return group, nil
   148  		}
   149  	}
   150  
   151  	return nil, cloudprovider.ErrNotFound
   152  }
   153  
   154  func onecloudHealthCodeToQcloud(codes string) int {
   155  	qcode := 0
   156  	for i, code := range HTTP_CODES {
   157  		if strings.Contains(codes, code) {
   158  			// 按位或然后再赋值qcode
   159  			qcode |= 1 << uint(i)
   160  		}
   161  	}
   162  
   163  	return qcode
   164  }
   165  
   166  // https://cloud.tencent.com/document/product/214/30693
   167  // todo:  1.限制比较多必须加参数校验 2.Onecloud 不支持双向证书可能存在兼容性问题
   168  // 应用型负载均衡 传统型不支持设置SNI
   169  func (self *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) {
   170  	lockman.LockRawObject(ctx, "qcloud.SLoadbalancer.CreateILoadBalancerListener", self.region.client.ownerId)
   171  	defer lockman.ReleaseRawObject(ctx, "qcloud.SLoadbalancer.CreateILoadBalancerListener", self.region.client.ownerId)
   172  
   173  	sniSwitch := 0
   174  	hc := getHealthCheck(listener)
   175  	cert := getCertificate(listener)
   176  
   177  	var listenId string
   178  	var err error
   179  	if self.Forward == LB_TYPE_APPLICATION {
   180  		listenId, err = self.region.CreateLoadbalancerListener(self.GetId(),
   181  			listener.Name,
   182  			getProtocol(listener),
   183  			listener.ListenerPort,
   184  			getScheduler(listener),
   185  			&listener.StickySessionCookieTimeout,
   186  			&sniSwitch,
   187  			hc,
   188  			cert)
   189  	} else {
   190  		// 传统型内网属性负载均衡不支持指定scheduler
   191  		var scheduler *string
   192  		if self.LoadBalancerType == "OPEN" {
   193  			scheduler = getScheduler(listener)
   194  		}
   195  
   196  		listenId, err = self.region.CreateClassicLoadbalancerListener(self.GetId(),
   197  			listener.Name,
   198  			getClassicLBProtocol(listener),
   199  			listener.ListenerPort,
   200  			listener.BackendServerPort,
   201  			scheduler,
   202  			&listener.StickySessionCookieTimeout,
   203  			&sniSwitch,
   204  			hc,
   205  			cert)
   206  	}
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	var lblis cloudprovider.ICloudLoadbalancerListener
   212  	err = cloudprovider.Wait(3*time.Second, 30*time.Second, func() (bool, error) {
   213  		lblis, err = self.GetILoadBalancerListenerById(listenId)
   214  		if err != nil {
   215  			if errors.Cause(err) != cloudprovider.ErrNotFound {
   216  				return false, err
   217  			} else {
   218  				return false, nil
   219  			}
   220  		}
   221  
   222  		return true, nil
   223  	})
   224  	if err != nil {
   225  		return nil, errors.Wrap(err, "GetILoadBalancerListenerById.Wait")
   226  	}
   227  
   228  	return lblis, nil
   229  }
   230  
   231  func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) {
   232  	listeners, err := self.GetLoadbalancerListeners("")
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  
   237  	for _, listener := range listeners {
   238  		if listener.GetId() == listenerId {
   239  			return &listener, nil
   240  		}
   241  	}
   242  
   243  	return nil, cloudprovider.ErrNotFound
   244  }
   245  
   246  func (self *SLoadbalancer) GetId() string {
   247  	return self.LoadBalancerID
   248  }
   249  
   250  func (self *SLoadbalancer) GetName() string {
   251  	return self.LoadBalancerName
   252  }
   253  
   254  // add region?
   255  func (self *SLoadbalancer) GetGlobalId() string {
   256  	return self.LoadBalancerID
   257  }
   258  
   259  func (self *SLoadbalancer) GetStatus() string {
   260  	switch self.Status {
   261  	case 0:
   262  		return api.LB_STATUS_INIT
   263  	case 1:
   264  		return api.LB_STATUS_ENABLED
   265  	default:
   266  		return api.LB_STATUS_UNKNOWN
   267  	}
   268  }
   269  
   270  func (self *SLoadbalancer) Refresh() error {
   271  	lb, err := self.region.GetLoadbalancer(self.GetId())
   272  	if err != nil {
   273  		return err
   274  	}
   275  
   276  	return jsonutils.Update(self, lb)
   277  }
   278  
   279  func (self *SLoadbalancer) IsEmulated() bool {
   280  	return false
   281  }
   282  
   283  func (self *SLoadbalancer) GetSysTags() map[string]string {
   284  	meta := map[string]string{}
   285  	meta["Forward"] = strconv.FormatInt(int64(self.Forward), 10)
   286  	meta["OpenBGP"] = strconv.FormatInt(self.OpenBGP, 10)
   287  	meta["Domain"] = self.Domain
   288  	meta["ProjectID"] = strconv.FormatInt(self.ProjectID, 10)
   289  	return meta
   290  }
   291  
   292  // 腾讯云当前不支持一个LB绑定多个ip,每个LB只支持绑定一个ip
   293  func (self *SLoadbalancer) GetAddress() string {
   294  	return self.LoadBalancerVips[0]
   295  }
   296  
   297  func (self *SLoadbalancer) GetAddressType() string {
   298  	switch self.LoadBalancerType {
   299  	case LB_ADDR_TYPE_INTERNAL:
   300  		return api.LB_ADDR_TYPE_INTRANET
   301  	case LB_ADDR_TYPE_OPEN:
   302  		return api.LB_ADDR_TYPE_INTERNET
   303  	default:
   304  		return ""
   305  	}
   306  }
   307  
   308  func (self *SLoadbalancer) GetNetworkType() string {
   309  	if len(self.VpcID) > 0 {
   310  		return api.LB_NETWORK_TYPE_VPC
   311  	} else {
   312  		return api.LB_NETWORK_TYPE_CLASSIC
   313  	}
   314  }
   315  
   316  func (self *SLoadbalancer) GetNetworkIds() []string {
   317  	if len(self.SubnetId) == 0 {
   318  		return []string{}
   319  	}
   320  
   321  	return []string{self.SubnetId}
   322  }
   323  
   324  func (self *SLoadbalancer) GetVpcId() string {
   325  	return self.VpcID
   326  }
   327  
   328  func (self *SLoadbalancer) GetZoneId() string {
   329  	zoneId := ""
   330  	if len(self.MasterZone.Zone) > 0 {
   331  		zoneId = self.MasterZone.Zone
   332  	} else if len(self.SubnetId) > 0 {
   333  		net, err := self.region.GetNetwork(self.SubnetId)
   334  		if err != nil {
   335  			log.Warningf("GetNetwork %s %s", self.SubnetId, err)
   336  			return ""
   337  		}
   338  
   339  		zoneId = net.Zone
   340  	}
   341  
   342  	if len(zoneId) > 0 {
   343  		z, err := self.region.getZoneById(zoneId)
   344  		if err != nil {
   345  			log.Warningf("getZoneById %s %s", zoneId, err)
   346  			return ""
   347  		}
   348  
   349  		return z.GetGlobalId()
   350  	}
   351  
   352  	return ""
   353  }
   354  
   355  func (self *SLoadbalancer) GetZone1Id() string {
   356  	if self.BackupZoneSet == nil {
   357  		return ""
   358  	}
   359  
   360  	if len(self.BackupZoneSet) > 0 {
   361  		z, err := self.region.getZoneById(self.BackupZoneSet[0].Zone)
   362  		if err != nil {
   363  			log.Warningf("getZoneById %s %s", self.BackupZoneSet[0].Zone, err)
   364  			return ""
   365  		}
   366  
   367  		return z.GetGlobalId()
   368  	}
   369  
   370  	return ""
   371  }
   372  
   373  func (self *SLoadbalancer) GetLoadbalancerListeners(protocal string) ([]SLBListener, error) {
   374  	listeners, err := self.region.GetLoadbalancerListeners(self.GetId(), self.Forward, protocal)
   375  	if err != nil {
   376  		return nil, err
   377  	}
   378  
   379  	for i := range listeners {
   380  		listeners[i].lb = self
   381  	}
   382  
   383  	return listeners, nil
   384  }
   385  
   386  func (self *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) {
   387  	listeners, err := self.GetLoadbalancerListeners("")
   388  	if err != nil {
   389  		return nil, err
   390  	}
   391  
   392  	ilisteners := make([]cloudprovider.ICloudLoadbalancerListener, len(listeners))
   393  	for i := range listeners {
   394  		l := listeners[i]
   395  		ilisteners[i] = &l
   396  	}
   397  
   398  	return ilisteners, nil
   399  }
   400  
   401  func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) {
   402  	if self.Forward == LB_TYPE_CLASSIC {
   403  		bg := SLBBackendGroup{lb: self}
   404  		return []cloudprovider.ICloudLoadbalancerBackendGroup{&bg}, nil
   405  	}
   406  
   407  	listeners, err := self.GetLoadbalancerListeners("")
   408  	if err != nil {
   409  		return nil, err
   410  	}
   411  
   412  	bgs := []cloudprovider.ICloudLoadbalancerBackendGroup{}
   413  	for i := range listeners {
   414  		listener := listeners[i]
   415  		t := listener.GetListenerType()
   416  		if t == api.LB_LISTENER_TYPE_HTTP || t == api.LB_LISTENER_TYPE_HTTPS {
   417  			rules := listener.Rules
   418  			for i := range rules {
   419  				rule := rules[i]
   420  				rule.listener = &listener
   421  				bg := rule.GetBackendGroup()
   422  				bgs = append(bgs, bg)
   423  			}
   424  		} else {
   425  			bg := listener.GetBackendGroup()
   426  			bgs = append(bgs, bg)
   427  		}
   428  	}
   429  
   430  	ibgs := make([]cloudprovider.ICloudLoadbalancerBackendGroup, len(bgs))
   431  	for i := range bgs {
   432  		ibgs[i] = bgs[i]
   433  	}
   434  
   435  	return ibgs, nil
   436  }
   437  
   438  func (self *SLoadbalancer) GetIEIP() (cloudprovider.ICloudEIP, error) {
   439  	if self.LoadBalancerType == "OPEN" && len(self.LoadBalancerVips) > 0 {
   440  		return &SEipAddress{
   441  			region:      self.region,
   442  			AddressId:   self.LoadBalancerID,
   443  			AddressIp:   self.LoadBalancerVips[0],
   444  			AddressType: EIP_STATUS_BIND,
   445  			InstanceId:  self.LoadBalancerID,
   446  			CreatedTime: self.CreateTime,
   447  		}, nil
   448  	}
   449  	return nil, nil
   450  }
   451  
   452  func (self *SRegion) GetLoadbalancers(ids []string) ([]SLoadbalancer, error) {
   453  	params := map[string]string{}
   454  	for i, id := range ids {
   455  		params[fmt.Sprintf("LoadBalancerIds.%d", i)] = id
   456  	}
   457  
   458  	offset := 0
   459  	limit := 100
   460  	lbs := make([]SLoadbalancer, 0)
   461  	for {
   462  		params["Limit"] = strconv.Itoa(limit)
   463  		params["Offset"] = strconv.Itoa(offset)
   464  
   465  		resp, err := self.clbRequest("DescribeLoadBalancers", params)
   466  		if err != nil {
   467  			return nil, err
   468  		}
   469  
   470  		parts := make([]SLoadbalancer, 0)
   471  		err = resp.Unmarshal(&parts, "LoadBalancerSet")
   472  		if err != nil {
   473  			return nil, err
   474  		}
   475  
   476  		_total, err := resp.Float("TotalCount")
   477  		if err != nil {
   478  			return nil, err
   479  		}
   480  
   481  		total := int(_total)
   482  		if err != nil {
   483  			return nil, err
   484  		}
   485  
   486  		lbs = append(lbs, parts...)
   487  		offset += len(parts)
   488  		if offset >= total {
   489  			for i := range lbs {
   490  				lbs[i].region = self
   491  			}
   492  
   493  			return lbs, err
   494  		}
   495  	}
   496  }
   497  
   498  func (self *SRegion) GetLoadbalancer(id string) (*SLoadbalancer, error) {
   499  	if len(id) == 0 {
   500  		return nil, fmt.Errorf("GetILoadbalancer id should not empty")
   501  	}
   502  
   503  	lbs, err := self.GetLoadbalancers([]string{id})
   504  	if err != nil {
   505  		return nil, err
   506  	}
   507  
   508  	switch len(lbs) {
   509  	case 0:
   510  		return nil, cloudprovider.ErrNotFound
   511  	case 1:
   512  		return &lbs[0], nil
   513  	default:
   514  		return nil, fmt.Errorf("GetILoadbalancer %s found %d", id, len(lbs))
   515  	}
   516  }
   517  
   518  /*
   519  返回requstid 用于异步任务查询
   520  https://cloud.tencent.com/document/product/214/30689
   521  */
   522  func (self *SRegion) DeleteLoadbalancer(lbid string) (string, error) {
   523  	if len(lbid) == 0 {
   524  		return "", fmt.Errorf("loadbalancer id should not be empty")
   525  	}
   526  
   527  	params := map[string]string{"LoadBalancerIds.0": lbid}
   528  	resp, err := self.clbRequest("DeleteLoadBalancer", params)
   529  	if err != nil {
   530  		return "", err
   531  	}
   532  
   533  	return resp.GetString("RequestId")
   534  }
   535  
   536  /*
   537  返回requstid 用于异步任务查询
   538  https://cloud.tencent.com/document/product/214/30689
   539  */
   540  func (self *SRegion) DeleteClassicLoadbalancer(lbid string) (string, error) {
   541  	if len(lbid) == 0 {
   542  		return "", fmt.Errorf("loadbalancer id should not be empty")
   543  	}
   544  
   545  	params := map[string]string{"loadBalancerIds.n": lbid}
   546  	resp, err := self.lbRequest("DeleteLoadBalancers", params)
   547  	if err != nil {
   548  		return "", err
   549  	}
   550  
   551  	return resp.GetString("requestId")
   552  }
   553  
   554  /*
   555  https://cloud.tencent.com/document/product/214/30693
   556  SNI 特性是什么??
   557  */
   558  func (self *SRegion) CreateLoadbalancerListener(lbid, name, protocol string, port int, scheduler *string, sessionExpireTime, sniSwitch *int, healthCheck *healthCheck, cert *certificate) (string, error) {
   559  	if len(lbid) == 0 {
   560  		return "", fmt.Errorf("loadbalancer id should not be empty")
   561  	}
   562  
   563  	params := map[string]string{
   564  		"LoadBalancerId": lbid,
   565  		"Ports.0":        strconv.Itoa(port),
   566  		"Protocol":       protocol,
   567  	}
   568  
   569  	if len(name) > 0 {
   570  		params["ListenerNames.0"] = name
   571  	}
   572  
   573  	if utils.IsInStringArray(protocol, []string{PROTOCOL_TCP, PROTOCOL_UDP, PROTOCOL_TCP_SSL}) {
   574  		params = healthCheckParams(LB_TYPE_APPLICATION, params, healthCheck, "HealthCheck.")
   575  
   576  		if scheduler != nil && len(*scheduler) > 0 {
   577  			params["Scheduler"] = *scheduler
   578  		}
   579  
   580  		if sessionExpireTime != nil {
   581  			params["SessionExpireTime"] = strconv.Itoa(*sessionExpireTime)
   582  		}
   583  	} else {
   584  		if protocol == PROTOCOL_HTTPS && sniSwitch != nil {
   585  			params["SniSwitch"] = strconv.Itoa(*sniSwitch)
   586  		}
   587  	}
   588  
   589  	params = certificateParams(LB_TYPE_APPLICATION, params, cert, "Certificate.")
   590  
   591  	resp, err := self.clbRequest("CreateListener", params)
   592  	if err != nil {
   593  		return "", err
   594  	}
   595  
   596  	listeners, err := resp.GetArray("ListenerIds")
   597  	if err != nil {
   598  		return "", err
   599  	}
   600  
   601  	if len(listeners) == 0 {
   602  		return "", fmt.Errorf("CreateLoadbalancerListener no listener id returned: %s", resp.String())
   603  	} else if len(listeners) == 1 {
   604  		return listeners[0].GetString()
   605  	} else {
   606  		return "", fmt.Errorf("CreateLoadbalancerListener mutliple listener id returned: %s", resp.String())
   607  	}
   608  }
   609  
   610  // https://cloud.tencent.com/document/api/214/1255
   611  // 不支持sniSwitch
   612  // todo: 待测试
   613  func (self *SRegion) CreateClassicLoadbalancerListener(lbid, name string, protocol, port, backendServerPort int, scheduler *string, sessionExpireTime, sniSwitch *int, healthCheck *healthCheck, cert *certificate) (string, error) {
   614  	if len(lbid) == 0 {
   615  		return "", fmt.Errorf("loadbalancer id should not be empty")
   616  	}
   617  
   618  	// 负载均衡实例监听器协议类型 1:HTTP,2:TCP,3:UDP,4:HTTPS。
   619  	// todo: 待测试 。 这里没有判断是否为公网负载均衡,可能存在问题.内网传统型负载均衡监听协议只支持TCP、UDP,并且不能指定调度算法
   620  	params := map[string]string{
   621  		"loadBalancerId":               lbid,
   622  		"listeners.0.loadBalancerPort": strconv.Itoa(port),
   623  		"listeners.0.instancePort":     strconv.Itoa(backendServerPort),
   624  		"listeners.0.protocol":         strconv.Itoa(protocol),
   625  	}
   626  
   627  	if len(name) > 0 {
   628  		params["listeners.0.listenerName"] = name
   629  	}
   630  
   631  	if sessionExpireTime != nil {
   632  		params["listeners.0.sessionExpire"] = strconv.Itoa(*sessionExpireTime)
   633  	}
   634  
   635  	if scheduler != nil && len(*scheduler) > 0 && (protocol == 2 || protocol == 3) {
   636  		params["listeners.0.scheduler"] = strings.ToLower(*scheduler)
   637  	}
   638  
   639  	if scheduler != nil && len(*scheduler) > 0 && (protocol == 1 || protocol == 4) {
   640  		params["listeners.0.httpHash"] = strings.ToLower(*scheduler)
   641  	}
   642  
   643  	params = healthCheckParams(LB_TYPE_CLASSIC, params, healthCheck, "listeners.0.")
   644  	params = certificateParams(LB_TYPE_CLASSIC, params, cert, "listeners.0.")
   645  
   646  	resp, err := self.lbRequest("CreateLoadBalancerListeners", params)
   647  	if err != nil {
   648  		return "", err
   649  	}
   650  
   651  	listeners, err := resp.GetArray("listenerIds")
   652  	if err != nil {
   653  		return "", err
   654  	}
   655  
   656  	if len(listeners) == 0 {
   657  		return "", fmt.Errorf("CreateLoadbalancerListener no listener id returned: %s", resp.String())
   658  	} else if len(listeners) == 1 {
   659  		return listeners[0].GetString()
   660  	} else {
   661  		return "", fmt.Errorf("CreateLoadbalancerListener mutliple listener id returned: %s", resp.String())
   662  	}
   663  }
   664  
   665  // https://cloud.tencent.com/document/product/214/30683
   666  // 任务的当前状态。 0:成功,1:失败,2:进行中
   667  func (self *SRegion) GetLBTaskStatus(requestId string) (string, error) {
   668  	if len(requestId) == 0 {
   669  		return "", fmt.Errorf("WaitTaskSuccess requestId should not be emtpy")
   670  	}
   671  
   672  	params := map[string]string{"TaskId": requestId}
   673  	resp, err := self.clbRequest("DescribeTaskStatus", params)
   674  	if err != nil {
   675  		return "", err
   676  	}
   677  
   678  	status, err := resp.Get("Status")
   679  	if err != nil {
   680  		log.Debugf("WaitTaskSuccess failed %s: %s", err, resp.String())
   681  		return "", err
   682  	}
   683  
   684  	_status, err := status.Float()
   685  	return fmt.Sprintf("%1.f", _status), err
   686  }
   687  
   688  func (self *SRegion) WaitLBTaskSuccess(requestId string, interval time.Duration, timeout time.Duration) error {
   689  	startTime := time.Now()
   690  	for time.Now().Sub(startTime) < timeout {
   691  		status, err := self.GetLBTaskStatus(requestId)
   692  		if err != nil {
   693  			return err
   694  		}
   695  		if status == "0" {
   696  			return nil
   697  		}
   698  
   699  		if status == "1" {
   700  			return fmt.Errorf("Task %s failed.", requestId)
   701  		}
   702  
   703  		time.Sleep(interval)
   704  	}
   705  
   706  	return cloudprovider.ErrTimeout
   707  }
   708  
   709  func (self *SLoadbalancer) GetProjectId() string {
   710  	return strconv.Itoa(int(self.ProjectID))
   711  }
   712  
   713  func (self *SLoadbalancer) SetTags(tags map[string]string, replace bool) error {
   714  	return self.region.SetResourceTags("clb", "clb", []string{self.LoadBalancerID}, tags, replace)
   715  }