yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/openstack/loadbalabcerpool.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 openstack
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"net/url"
    21  	"strings"
    22  	"time"
    23  
    24  	"yunion.io/x/jsonutils"
    25  	"yunion.io/x/pkg/errors"
    26  
    27  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    28  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    29  	"yunion.io/x/cloudmux/pkg/multicloud"
    30  )
    31  
    32  type SLoadbalancerPoolCreateParams struct {
    33  	LbAlgorithm        string               `json:"lb_algorithm,omitempty"`
    34  	Protocol           string               `json:"protocol,omitempty"`
    35  	Description        string               `json:"description,omitempty"`
    36  	AdminStateUp       bool                 `json:"admin_state_up,omitempty"`
    37  	SessionPersistence *SSessionPersistence `json:"session_persistence"`
    38  	LoadbalancerID     string               `json:"loadbalancer_id,omitempty"`
    39  	ListenerID         string               `json:"listener_id,omitempty"`
    40  	Name               string               `json:"name,omitempty"`
    41  	Tags               []string             `json:"tags,omitempty"`
    42  	TLSContainerRef    string               `json:"tls_container_ref,omitempty"`
    43  	CaTLSContainerRef  string               `json:"ca_tls_container_ref,omitempty"`
    44  	CrlContainerRef    string               `json:"crl_container_ref,omitempty"`
    45  	TLSEnabled         *bool                `json:"tls_enabled,omitempty"`
    46  	TLSCiphers         string               `json:"tls_ciphers,omitempty"`
    47  	TLSVersions        []string             `json:"tls_versions,omitempty"`
    48  }
    49  
    50  type SLoadbalancerPoolUpdateParams struct {
    51  	LbAlgorithm        string               `json:"lb_algorithm,omitempty"`
    52  	Description        string               `json:"description,omitempty"`
    53  	AdminStateUp       bool                 `json:"admin_state_up,omitempty"`
    54  	SessionPersistence *SSessionPersistence `json:"session_persistence"`
    55  	Name               string               `json:"name,omitempty"`
    56  	Tags               []string             `json:"tags,omitempty"`
    57  	TLSContainerRef    string               `json:"tls_container_ref,omitempty"`
    58  	CaTLSContainerRef  string               `json:"ca_tls_container_ref,omitempty"`
    59  	CrlContainerRef    string               `json:"crl_container_ref,omitempty"`
    60  	TLSEnabled         *bool                `json:"tls_enabled,omitempty"`
    61  	TLSCiphers         string               `json:"tls_ciphers,omitempty"`
    62  	TLSVersions        []string             `json:"tls_versions,omitempty"`
    63  }
    64  
    65  type SSessionPersistence struct {
    66  	CookieName string `json:"cookie_name,omitempty"`
    67  	Type       string `json:"type,omitempty"`
    68  }
    69  
    70  type SLoadbalancerPool struct {
    71  	multicloud.SResourceBase
    72  	OpenStackTags
    73  	region             *SRegion
    74  	members            []SLoadbalancerMember
    75  	healthmonitor      *SLoadbalancerHealthmonitor
    76  	LbAlgorithm        string              `json:"lb_algorithm"`
    77  	Protocol           string              `json:"protocol"`
    78  	Description        string              `json:"description"`
    79  	AdminStateUp       bool                `json:"admin_state_up"`
    80  	LoadbalancerIds    []SLoadbalancerID   `json:"loadbalancers"`
    81  	CreatedAt          string              `json:"created_at"`
    82  	ProvisioningStatus string              `json:"provisioning_status"`
    83  	UpdatedAt          string              `json:"updated_at"`
    84  	SessionPersistence SSessionPersistence `json:"session_persistence"`
    85  	ListenerIds        []SListenerID       `json:"listeners"`
    86  	MemberIds          []SMemberID         `json:"members"`
    87  	HealthmonitorID    string              `json:"healthmonitor_id"`
    88  	ProjectID          string              `json:"project_id"`
    89  	ID                 string              `json:"id"`
    90  	OperatingStatus    string              `json:"operating_status"`
    91  	Name               string              `json:"name"`
    92  	Tags               []string            `json:"tags"`
    93  	TLSContainerRef    string              `json:"tls_container_ref"`
    94  	CaTLSContainerRef  string              `json:"ca_tls_container_ref"`
    95  	CrlContainerRef    string              `json:"crl_container_ref"`
    96  	TLSEnabled         bool                `json:"tls_enabled"`
    97  	TLSCiphers         string              `json:"tls_ciphers"`
    98  	TLSVersions        []string            `json:"tls_versions"`
    99  }
   100  
   101  func ToOpenstackHealthCheckHttpCode(c string) string {
   102  	c = strings.TrimSpace(c)
   103  	segs := strings.Split(c, ",")
   104  	ret := []string{}
   105  	for _, seg := range segs {
   106  		seg = strings.TrimLeft(seg, "http_")
   107  		seg = strings.TrimSpace(seg)
   108  		seg = strings.Replace(seg, "xx", "00", -1)
   109  		ret = append(ret, seg)
   110  	}
   111  
   112  	return strings.Join(ret, ",")
   113  }
   114  
   115  func ToOnecloudHealthCheckHttpCode(c string) string {
   116  	c = strings.TrimSpace(c)
   117  	segs := strings.Split(c, ",")
   118  	ret := []string{}
   119  	for _, seg := range segs {
   120  		seg = strings.TrimSpace(seg)
   121  		seg = strings.Replace(seg, "00", "xx", -1)
   122  		seg = "http_" + seg
   123  		ret = append(ret, seg)
   124  	}
   125  
   126  	return strings.Join(ret, ",")
   127  }
   128  
   129  func (pool *SLoadbalancerPool) GetILoadbalancer() cloudprovider.ICloudLoadbalancer {
   130  	if len(pool.LoadbalancerIds) != 1 {
   131  		return nil
   132  	}
   133  	loadbalancer, err := pool.region.GetLoadbalancerbyId(pool.LoadbalancerIds[0].ID)
   134  	if err != nil {
   135  		return nil
   136  	}
   137  	return loadbalancer
   138  }
   139  
   140  func (pool *SLoadbalancerPool) GetLoadbalancerId() string {
   141  	if len(pool.LoadbalancerIds) != 1 {
   142  		return ""
   143  	}
   144  	return pool.LoadbalancerIds[0].ID
   145  }
   146  
   147  func (pool *SLoadbalancerPool) GetProtocolType() string {
   148  	switch pool.Protocol {
   149  	case "TCP":
   150  		return api.LB_LISTENER_TYPE_TCP
   151  	case "UDP":
   152  		return api.LB_LISTENER_TYPE_UDP
   153  	case "HTTP":
   154  		return api.LB_LISTENER_TYPE_HTTP
   155  	default:
   156  		return ""
   157  	}
   158  }
   159  
   160  func (pool *SLoadbalancerPool) GetScheduler() string {
   161  	switch pool.LbAlgorithm {
   162  	case "LEAST_CONNECTIONS":
   163  		return api.LB_SCHEDULER_WLC
   164  	case "ROUND_ROBIN":
   165  		return api.LB_SCHEDULER_WRR
   166  	case "SOURCE_IP":
   167  		return api.LB_SCHEDULER_SCH
   168  	case "SOURCE_IP_PORT":
   169  		return api.LB_SCHEDULER_TCH
   170  	default:
   171  		return ""
   172  	}
   173  }
   174  
   175  func (pool *SLoadbalancerPool) GetHealthCheck() (*cloudprovider.SLoadbalancerHealthCheck, error) {
   176  	healthCheck := cloudprovider.SLoadbalancerHealthCheck{}
   177  	healthCheck.HealthCheckDomain = pool.healthmonitor.DomainName
   178  	healthCheck.HealthCheckHttpCode = ToOnecloudHealthCheckHttpCode(pool.healthmonitor.ExpectedCodes)
   179  	healthCheck.HealthCheckInterval = pool.healthmonitor.Delay
   180  	healthCheck.HealthCheckRise = pool.healthmonitor.MaxRetries
   181  	healthCheck.HealthCheckFail = pool.healthmonitor.MaxRetriesDown
   182  	healthCheck.HealthCheckTimeout = pool.healthmonitor.Timeout
   183  	switch pool.healthmonitor.Type {
   184  	case "HTTP":
   185  		healthCheck.HealthCheckType = api.LB_HEALTH_CHECK_HTTP
   186  	case "HTTPS":
   187  		healthCheck.HealthCheckType = api.LB_HEALTH_CHECK_HTTPS
   188  	case "TCP":
   189  		healthCheck.HealthCheckType = api.LB_HEALTH_CHECK_TCP
   190  	case "UDP-CONNECT":
   191  		healthCheck.HealthCheckType = api.LB_HEALTH_CHECK_UDP
   192  	default:
   193  		healthCheck.HealthCheckType = ""
   194  	}
   195  	healthCheck.HealthCheckURI = pool.healthmonitor.URLPath
   196  	return &healthCheck, nil
   197  }
   198  
   199  func (pool *SLoadbalancerPool) GetStickySession() (*cloudprovider.SLoadbalancerStickySession, error) {
   200  	if len(pool.SessionPersistence.Type) == 0 {
   201  		return nil, nil
   202  	}
   203  
   204  	var stickySessionType string
   205  	switch pool.SessionPersistence.Type {
   206  	case "SOURCE_IP":
   207  		stickySessionType = api.LB_STICKY_SESSION_TYPE_INSERT
   208  	case "HTTP_COOKIE":
   209  		stickySessionType = api.LB_STICKY_SESSION_TYPE_INSERT
   210  	case "APP_COOKIE":
   211  		stickySessionType = api.LB_STICKY_SESSION_TYPE_SERVER
   212  	}
   213  
   214  	ret := cloudprovider.SLoadbalancerStickySession{
   215  		StickySession:              api.LB_BOOL_ON,
   216  		StickySessionCookie:        pool.SessionPersistence.CookieName,
   217  		StickySessionType:          stickySessionType,
   218  		StickySessionCookieTimeout: 0,
   219  	}
   220  
   221  	return &ret, nil
   222  }
   223  
   224  func (pool *SLoadbalancerPool) GetName() string {
   225  	return pool.Name
   226  }
   227  
   228  func (pool *SLoadbalancerPool) GetId() string {
   229  	return pool.ID
   230  }
   231  
   232  func (pool *SLoadbalancerPool) GetGlobalId() string {
   233  	return pool.ID
   234  }
   235  
   236  func (pool *SLoadbalancerPool) GetStatus() string {
   237  	switch pool.ProvisioningStatus {
   238  	case "ACTIVE":
   239  		return api.LB_STATUS_ENABLED
   240  	case "PENDING_CREATE":
   241  		return api.LB_CREATING
   242  	case "PENDING_UPDATE":
   243  		return api.LB_SYNC_CONF
   244  	case "PENDING_DELETE":
   245  		return api.LB_STATUS_DELETING
   246  	case "DELETED":
   247  		return api.LB_STATUS_DELETED
   248  	default:
   249  		return api.LB_STATUS_UNKNOWN
   250  	}
   251  }
   252  
   253  func (pool *SLoadbalancerPool) IsDefault() bool {
   254  	return false
   255  }
   256  
   257  func (pool *SLoadbalancerPool) GetType() string {
   258  	return api.LB_BACKENDGROUP_TYPE_NORMAL
   259  }
   260  
   261  func (pool *SLoadbalancerPool) IsEmulated() bool {
   262  	return false
   263  }
   264  
   265  func (region *SRegion) GetLoadbalancerPools() ([]SLoadbalancerPool, error) {
   266  	pools := []SLoadbalancerPool{}
   267  	resource := "/v2/lbaas/pools"
   268  	query := url.Values{}
   269  	for {
   270  		resp, err := region.lbList(resource, query)
   271  		if err != nil {
   272  			return nil, errors.Wrap(err, "lbList")
   273  		}
   274  		part := struct {
   275  			Pools      []SLoadbalancerPool
   276  			PoolsLinks SNextLinks
   277  		}{}
   278  		err = resp.Unmarshal(&part)
   279  		if err != nil {
   280  			return nil, errors.Wrap(err, "resp.Unmarshal")
   281  		}
   282  		pools = append(pools, part.Pools...)
   283  		marker := part.PoolsLinks.GetNextMark()
   284  		if len(marker) == 0 {
   285  			break
   286  		}
   287  		query.Set("marker", marker)
   288  	}
   289  
   290  	for i := 0; i < len(pools); i++ {
   291  		pools[i].region = region
   292  		err := pools[i].fetchLoadbalancerHealthmonitor()
   293  		if err != nil {
   294  			return nil, errors.Wrapf(err, "pools[%d].fetchLoadbalancerHealthmonitor()", i)
   295  		}
   296  
   297  	}
   298  	return pools, nil
   299  }
   300  
   301  func (region *SRegion) GetLoadbalancerPoolById(poolId string) (*SLoadbalancerPool, error) {
   302  	body, err := region.lbGet(fmt.Sprintf("/v2/lbaas/pools/%s", poolId))
   303  	if err != nil {
   304  		return nil, errors.Wrapf(err, "region.lbGet(fmt.Sprintf(/v2/lbaas/pools/%s)", poolId)
   305  	}
   306  	pool := SLoadbalancerPool{}
   307  	err = body.Unmarshal(&pool, "pool")
   308  	if err != nil {
   309  		return nil, errors.Wrap(err, "body.Unmarshal")
   310  	}
   311  	pool.region = region
   312  	err = pool.fetchLoadbalancermembers()
   313  	if err != nil {
   314  		return nil, errors.Wrap(err, "pool.fetchLoadbalancermembers()")
   315  	}
   316  	err = pool.fetchLoadbalancerHealthmonitor()
   317  	if err != nil {
   318  		return nil, errors.Wrap(err, "pool.fetchLoadbalancerHealthmonitor()")
   319  	}
   320  	return &pool, nil
   321  }
   322  
   323  func (region *SRegion) CreateLoadbalancerPool(group *cloudprovider.SLoadbalancerBackendGroup) (*SLoadbalancerPool, error) {
   324  	type CreateParams struct {
   325  		Pool SLoadbalancerPoolCreateParams `json:"pool"`
   326  	}
   327  	params := CreateParams{}
   328  	params.Pool.AdminStateUp = true
   329  	params.Pool.LbAlgorithm = LB_ALGORITHM_MAP[group.Scheduler]
   330  	params.Pool.Name = group.Name
   331  	params.Pool.LoadbalancerID = group.LoadbalancerID
   332  	// 绑定规则时不能指定listener
   333  	params.Pool.ListenerID = group.ListenerID
   334  	params.Pool.Protocol = LB_PROTOCOL_MAP[group.ListenType]
   335  	params.Pool.SessionPersistence = nil
   336  	if group.StickySession != nil {
   337  		session := SSessionPersistence{}
   338  		session.Type = LB_STICKY_SESSION_MAP[group.StickySession.StickySessionType]
   339  		if session.Type == "APP_COOKIE" {
   340  			session.CookieName = group.StickySession.StickySessionCookie
   341  		}
   342  		params.Pool.SessionPersistence = &session
   343  	}
   344  	body, err := region.lbPost("/v2/lbaas/pools", jsonutils.Marshal(params))
   345  	if err != nil {
   346  		return nil, errors.Wrap(err, "region.lbPost(/v2/lbaas/pools)")
   347  	}
   348  	spool := SLoadbalancerPool{}
   349  	spool.region = region
   350  	err = body.Unmarshal(&spool, "pool")
   351  	if err != nil {
   352  		return nil, errors.Wrap(err, "body.Unmarshal(&spool, pool)")
   353  	}
   354  	return &spool, nil
   355  }
   356  
   357  func (pool *SLoadbalancerPool) Refresh() error {
   358  	newPool, err := pool.region.GetLoadbalancerPoolById(pool.ID)
   359  	if err != nil {
   360  		return err
   361  	}
   362  	return jsonutils.Update(pool, newPool)
   363  }
   364  
   365  func (pool *SLoadbalancerPool) fetchLoadbalancermembers() error {
   366  	if len(pool.MemberIds) < 1 {
   367  		return nil
   368  	}
   369  	members, err := pool.region.GetLoadbalancerMenbers(pool.ID)
   370  	if err != nil {
   371  		return err
   372  	}
   373  	pool.members = members
   374  	return nil
   375  }
   376  
   377  func (pool *SLoadbalancerPool) fetchLoadbalancerHealthmonitor() error {
   378  	if len(pool.HealthmonitorID) < 1 {
   379  		return nil
   380  	}
   381  	healthmonitor, err := pool.region.GetLoadbalancerHealthmonitorById(pool.HealthmonitorID)
   382  	if err != nil {
   383  		return errors.Wrap(err, "pool.region.GetLoadbalancerHealthmonitorById")
   384  	}
   385  	pool.healthmonitor = healthmonitor
   386  	return nil
   387  }
   388  
   389  func (pool *SLoadbalancerPool) GetILoadbalancerBackends() ([]cloudprovider.ICloudLoadbalancerBackend, error) {
   390  	ibackends := []cloudprovider.ICloudLoadbalancerBackend{}
   391  	for i := 0; i < len(pool.members); i++ {
   392  		ibackends = append(ibackends, &pool.members[i])
   393  	}
   394  	return ibackends, nil
   395  }
   396  
   397  func (pool *SLoadbalancerPool) GetILoadbalancerBackendById(memberId string) (cloudprovider.ICloudLoadbalancerBackend, error) {
   398  	for i := 0; i < len(pool.members); i++ {
   399  		if pool.members[i].ID == memberId {
   400  			return &pool.members[i], nil
   401  		}
   402  	}
   403  	return nil, errors.Wrapf(cloudprovider.ErrNotFound, "GetILoadbalancerBackendById(%s)", memberId)
   404  }
   405  
   406  func (region *SRegion) UpdateLoadBalancerPool(poolId string, group *cloudprovider.SLoadbalancerBackendGroup) error {
   407  	type UpdateParams struct {
   408  		Pool SLoadbalancerPoolUpdateParams `json:"pool"`
   409  	}
   410  	params := UpdateParams{}
   411  	params.Pool.AdminStateUp = true
   412  	params.Pool.LbAlgorithm = LB_ALGORITHM_MAP[group.Scheduler]
   413  	params.Pool.Name = group.Name
   414  	if group.StickySession != nil {
   415  		session := SSessionPersistence{}
   416  		session.Type = LB_STICKY_SESSION_MAP[group.StickySession.StickySessionType]
   417  		if session.Type == "APP_COOKIE" {
   418  			session.CookieName = group.StickySession.StickySessionCookie
   419  		}
   420  		params.Pool.SessionPersistence = &session
   421  	}
   422  	_, err := region.lbUpdate(fmt.Sprintf("/v2/lbaas/pools/%s", poolId), jsonutils.Marshal(params))
   423  	if err != nil {
   424  		return errors.Wrapf(err, `region.lbUpdate("/v2/lbaas/pools/%s", jsonutils.Marshal(params))`, poolId)
   425  	}
   426  	return nil
   427  }
   428  
   429  func (pool *SLoadbalancerPool) Sync(ctx context.Context, group *cloudprovider.SLoadbalancerBackendGroup) error {
   430  	lb, err := pool.region.GetLoadbalancerbyId(pool.GetLoadbalancerId())
   431  	if err != nil {
   432  		return errors.Wrap(err, "pool.region.GetLoadbalancerbyId(pool.GetLoadbalancerId())")
   433  	}
   434  	// ensure loadbalancer status
   435  	err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
   436  	if err != nil {
   437  		return errors.Wrap(err, `waitLbResStatus(lb, 10*time.Second, 8*time.Minute)`)
   438  	}
   439  	// ensure pool status
   440  	err = waitLbResStatus(pool, 10*time.Second, 8*time.Minute)
   441  	if err != nil {
   442  		return errors.Wrap(err, `waitLbResStatus(pool, 10*time.Second, 8*time.Minute)`)
   443  	}
   444  	// sync healthmonitor
   445  	healthmonitor := SLoadbalancerHealthmonitor{}
   446  	if len(pool.HealthmonitorID) > 0 {
   447  		oldhealthmonitor, err := pool.region.GetLoadbalancerHealthmonitorById(pool.HealthmonitorID)
   448  		if err != nil {
   449  			return errors.Wrap(err, "pool.region.GetLoadbalancerHealthmonitorById(pool.HealthmonitorID)")
   450  		}
   451  		// 不能更新健康检查类型,需要删除重建
   452  		var sHealthCheckType string
   453  		switch oldhealthmonitor.Type {
   454  		case "HTTP":
   455  			sHealthCheckType = api.LB_HEALTH_CHECK_HTTP
   456  		case "HTTPS":
   457  			sHealthCheckType = api.LB_HEALTH_CHECK_HTTPS
   458  		case "TCP":
   459  			sHealthCheckType = api.LB_HEALTH_CHECK_TCP
   460  		case "UDP-CONNECT":
   461  			sHealthCheckType = api.LB_HEALTH_CHECK_UDP
   462  		default:
   463  			sHealthCheckType = ""
   464  		}
   465  
   466  		if sHealthCheckType != group.HealthCheck.HealthCheckType {
   467  			err := pool.region.DeleteLoadbalancerHealthmonitor(pool.HealthmonitorID)
   468  			if err != nil {
   469  				return errors.Wrapf(err, "pool.region.DeleteLoadbalancerHealthmonitor(%s)", pool.HealthmonitorID)
   470  			}
   471  			// 等待删除结束
   472  			err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
   473  			if err != nil {
   474  				return errors.Wrap(err, `waitLbResStatus(lb, 10*time.Second, 8*time.Minute)`)
   475  			}
   476  
   477  			newhealthmonitor, err := pool.region.CreateLoadbalancerHealthmonitor(pool.ID, group.HealthCheck)
   478  			if err != nil {
   479  				return errors.Wrapf(err, "pool.region.CreateLoadbalancerHealthmonitor(%s,group.HealthCheck)", pool.ID)
   480  			}
   481  			healthmonitor = *newhealthmonitor
   482  		} else {
   483  			// ensure healthmonitor status
   484  			err = waitLbResStatus(oldhealthmonitor, 10*time.Second, 8*time.Minute)
   485  			if err != nil {
   486  				return errors.Wrap(err, `waitLbResStatus(oldhealthmonitor, 10*time.Second, 8*time.Minute)`)
   487  			}
   488  			oldhealthmonitor, err = pool.region.UpdateLoadbalancerHealthmonitor(pool.HealthmonitorID, group.HealthCheck)
   489  			if err != nil {
   490  				return errors.Wrapf(err, `pool.region.UpdateLoadbalancerHealthmonitor(%s, group.HealthCheck)`, pool.HealthmonitorID)
   491  			}
   492  			healthmonitor = *oldhealthmonitor
   493  		}
   494  	} else {
   495  		newhealthmonitor, err := pool.region.CreateLoadbalancerHealthmonitor(pool.ID, group.HealthCheck)
   496  		if err != nil {
   497  			return errors.Wrapf(err, "pool.region.CreateLoadbalancerHealthmonitor(%s, group.HealthCheck)", pool.ID)
   498  		}
   499  		healthmonitor = *newhealthmonitor
   500  	}
   501  
   502  	// ensure pool status
   503  	err = waitLbResStatus(pool, 10*time.Second, 8*time.Minute)
   504  	if err != nil {
   505  		return errors.Wrap(err, `waitLbResStatus(pool, 10*time.Second, 8*time.Minute)`)
   506  	}
   507  	// sync pool
   508  	err = pool.region.UpdateLoadBalancerPool(pool.ID, group)
   509  	if err != nil {
   510  		return errors.Wrapf(err, `pool.region.UpdateLoadBalancerPool(%s, group)`, pool.ID)
   511  	}
   512  
   513  	// wait healthmonitor status
   514  	err = waitLbResStatus(&healthmonitor, 10*time.Second, 8*time.Minute)
   515  	if err != nil {
   516  		return errors.Wrap(err, `waitLbResStatus(&healthmonitor, 10*time.Second, 8*time.Minute)`)
   517  	}
   518  	// wait pool status
   519  	err = waitLbResStatus(pool, 10*time.Second, 8*time.Minute)
   520  	if err != nil {
   521  		return errors.Wrap(err, `waitLbResStatus(pool,  10*time.Second, 8*time.Minute)`)
   522  	}
   523  	return nil
   524  }
   525  
   526  func (region *SRegion) DeleteLoadBalancerPool(poolId string) error {
   527  	_, err := region.lbDelete(fmt.Sprintf("/v2/lbaas/pools/%s", poolId))
   528  	if err != nil {
   529  		return errors.Wrapf(err, "lbDelete(/v2/lbaas/pools/%s)", poolId)
   530  	}
   531  	return nil
   532  }
   533  
   534  func (pool *SLoadbalancerPool) Delete(ctx context.Context) error {
   535  	lb, err := pool.region.GetLoadbalancerbyId(pool.GetLoadbalancerId())
   536  	if err != nil {
   537  		return errors.Wrap(err, "pool.region.GetLoadbalancerbyId(pool.GetLoadbalancerId())")
   538  	}
   539  	err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
   540  	if err != nil {
   541  		return errors.Wrap(err, `waitLbResStatus(lb, 10*time.Second, 8*time.Minute)`)
   542  	}
   543  	return pool.region.DeleteLoadBalancerPool(pool.ID)
   544  }
   545  
   546  func (pool *SLoadbalancerPool) AddBackendServer(serverId string, weight, port int) (cloudprovider.ICloudLoadbalancerBackend, error) {
   547  	// ensure lb status
   548  	lb, err := pool.region.GetLoadbalancerbyId(pool.GetLoadbalancerId())
   549  	if err != nil {
   550  		return nil, errors.Wrap(err, "pool.region.GetLoadbalancerbyId(pool.GetLoadbalancerId())")
   551  	}
   552  	err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
   553  	if err != nil {
   554  		return nil, errors.Wrap(err, `waitLbResStatus(lb, 10*time.Second, 8*time.Minute)`)
   555  	}
   556  	smemeber, err := pool.region.CreateLoadbalancerMember(pool.ID, serverId, weight, port)
   557  	if err != nil {
   558  		return nil, errors.Wrapf(err, `CreateLoadbalancerMember(%s,%s,%d,%d)`, pool.ID, serverId, weight, port)
   559  	}
   560  	err = waitLbResStatus(smemeber, 10*time.Second, 8*time.Minute)
   561  	if err != nil {
   562  		return nil, errors.Wrap(err, `waitLbResStatus(smemeber,  10*time.Second, 8*time.Minute)`)
   563  	}
   564  	smemeber.region = pool.region
   565  	smemeber.poolID = pool.ID
   566  	pool.members = append(pool.members, *smemeber)
   567  	return smemeber, nil
   568  }
   569  
   570  // 不是serverId,是memberId
   571  func (pool *SLoadbalancerPool) RemoveBackendServer(id string, weight, port int) error {
   572  	return pool.region.DeleteLoadbalancerMember(pool.ID, id)
   573  }
   574  
   575  func (pool *SLoadbalancerPool) GetProjectId() string {
   576  	return pool.ProjectID
   577  }