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 }