yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/huawei/loadbalancer_backendgroup.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 huawei 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 SElbBackendGroup struct { 33 multicloud.SResourceBase 34 HuaweiTags 35 lb *SLoadbalancer 36 region *SRegion 37 38 LBAlgorithm string `json:"lb_algorithm"` 39 Protocol string `json:"protocol"` 40 Description string `json:"description"` 41 AdminStateUp bool `json:"admin_state_up"` 42 Loadbalancers []Listener `json:"loadbalancers"` 43 TenantID string `json:"tenant_id"` 44 ProjectID string `json:"project_id"` 45 Listeners []Listener `json:"listeners"` 46 ID string `json:"id"` 47 Name string `json:"name"` 48 HealthMonitorID string `json:"healthmonitor_id"` 49 SessionPersistence StickySession `json:"session_persistence"` 50 } 51 52 func (self *SElbBackendGroup) GetLoadbalancerId() string { 53 return self.lb.GetId() 54 } 55 56 func (self *SElbBackendGroup) GetILoadbalancer() cloudprovider.ICloudLoadbalancer { 57 return self.lb 58 } 59 60 type StickySession struct { 61 Type string `json:"type"` 62 CookieName string `json:"cookie_name"` 63 PersistenceTimeout int `json:"persistence_timeout"` 64 } 65 66 func (self *SElbBackendGroup) GetProtocolType() string { 67 switch self.Protocol { 68 case "TCP": 69 return api.LB_LISTENER_TYPE_TCP 70 case "UDP": 71 return api.LB_LISTENER_TYPE_UDP 72 case "HTTP": 73 return api.LB_LISTENER_TYPE_HTTP 74 default: 75 return "" 76 } 77 } 78 79 func (self *SElbBackendGroup) GetScheduler() string { 80 switch self.LBAlgorithm { 81 case "ROUND_ROBIN": 82 return api.LB_SCHEDULER_WRR 83 case "LEAST_CONNECTIONS": 84 return api.LB_SCHEDULER_WLC 85 case "SOURCE_IP": 86 return api.LB_SCHEDULER_SCH 87 default: 88 return "" 89 } 90 } 91 92 func ToHuaweiHealthCheckHttpCode(c string) string { 93 c = strings.TrimSpace(c) 94 segs := strings.Split(c, ",") 95 ret := []string{} 96 for _, seg := range segs { 97 seg = strings.TrimLeft(seg, "http_") 98 seg = strings.TrimSpace(seg) 99 seg = strings.Replace(seg, "xx", "00", -1) 100 ret = append(ret, seg) 101 } 102 103 return strings.Join(ret, ",") 104 } 105 106 func ToOnecloudHealthCheckHttpCode(c string) string { 107 c = strings.TrimSpace(c) 108 segs := strings.Split(c, ",") 109 ret := []string{} 110 for _, seg := range segs { 111 seg = strings.TrimSpace(seg) 112 seg = strings.Replace(seg, "00", "xx", -1) 113 seg = "http_" + seg 114 ret = append(ret, seg) 115 } 116 117 return strings.Join(ret, ",") 118 } 119 120 func (self *SElbBackendGroup) GetHealthCheck() (*cloudprovider.SLoadbalancerHealthCheck, error) { 121 if len(self.HealthMonitorID) == 0 { 122 return nil, nil 123 } 124 125 health, err := self.region.GetLoadBalancerHealthCheck(self.HealthMonitorID) 126 if err != nil { 127 return nil, err 128 } 129 130 var healthCheckType string 131 switch health.Type { 132 case "TCP": 133 healthCheckType = api.LB_HEALTH_CHECK_TCP 134 case "UDP_CONNECT": 135 healthCheckType = api.LB_HEALTH_CHECK_UDP 136 case "HTTP": 137 healthCheckType = api.LB_HEALTH_CHECK_HTTP 138 default: 139 healthCheckType = "" 140 } 141 142 ret := cloudprovider.SLoadbalancerHealthCheck{ 143 HealthCheckType: healthCheckType, 144 HealthCheckTimeout: health.Timeout, 145 HealthCheckDomain: health.DomainName, 146 HealthCheckURI: health.URLPath, 147 HealthCheckInterval: health.Delay, 148 HealthCheckRise: health.MaxRetries, 149 HealthCheckHttpCode: ToOnecloudHealthCheckHttpCode(health.ExpectedCodes), 150 } 151 152 return &ret, nil 153 } 154 155 func (self *SElbBackendGroup) GetStickySession() (*cloudprovider.SLoadbalancerStickySession, error) { 156 if len(self.SessionPersistence.Type) == 0 { 157 return nil, nil 158 } 159 160 var stickySessionType string 161 switch self.SessionPersistence.Type { 162 case "SOURCE_IP": 163 stickySessionType = api.LB_STICKY_SESSION_TYPE_INSERT 164 case "HTTP_COOKIE": 165 stickySessionType = api.LB_STICKY_SESSION_TYPE_INSERT 166 case "APP_COOKIE": 167 stickySessionType = api.LB_STICKY_SESSION_TYPE_SERVER 168 } 169 170 ret := cloudprovider.SLoadbalancerStickySession{ 171 StickySession: api.LB_BOOL_ON, 172 StickySessionCookie: self.SessionPersistence.CookieName, 173 StickySessionType: stickySessionType, 174 StickySessionCookieTimeout: self.SessionPersistence.PersistenceTimeout * 60, 175 } 176 177 return &ret, nil 178 } 179 180 func (self *SElbBackendGroup) GetId() string { 181 return self.ID 182 } 183 184 func (self *SElbBackendGroup) GetName() string { 185 return self.Name 186 } 187 188 func (self *SElbBackendGroup) GetGlobalId() string { 189 return self.GetId() 190 } 191 192 func (self *SElbBackendGroup) GetStatus() string { 193 return api.LB_STATUS_ENABLED 194 } 195 196 func (self *SElbBackendGroup) Refresh() error { 197 ret, err := self.lb.region.GetLoadBalancerBackendGroupId(self.GetId()) 198 if err != nil { 199 return err 200 } 201 ret.lb = self.lb 202 203 err = jsonutils.Update(self, ret) 204 if err != nil { 205 return err 206 } 207 208 return nil 209 } 210 211 func (self *SElbBackendGroup) IsEmulated() bool { 212 return false 213 } 214 215 func (self *SElbBackendGroup) GetProjectId() string { 216 return self.ProjectID 217 } 218 219 func (self *SElbBackendGroup) IsDefault() bool { 220 return false 221 } 222 223 func (self *SElbBackendGroup) GetType() string { 224 return api.LB_BACKENDGROUP_TYPE_NORMAL 225 } 226 227 func (self *SElbBackendGroup) GetILoadbalancerBackends() ([]cloudprovider.ICloudLoadbalancerBackend, error) { 228 ret, err := self.region.GetLoadBalancerBackends(self.GetId()) 229 if err != nil { 230 return nil, err 231 } 232 233 iret := []cloudprovider.ICloudLoadbalancerBackend{} 234 for i := range ret { 235 backend := ret[i] 236 backend.lb = self.lb 237 backend.backendGroup = self 238 239 iret = append(iret, &backend) 240 } 241 242 return iret, nil 243 } 244 245 func (self *SElbBackendGroup) GetILoadbalancerBackendById(serverId string) (cloudprovider.ICloudLoadbalancerBackend, error) { 246 backend, err := self.region.GetElbBackend(self.GetId(), serverId) 247 if err != nil { 248 return nil, err 249 } 250 backend.lb = self.lb 251 backend.backendGroup = self 252 return backend, nil 253 } 254 255 func (self *SElbBackendGroup) AddBackendServer(serverId string, weight int, port int) (cloudprovider.ICloudLoadbalancerBackend, error) { 256 instance, err := self.lb.region.GetInstanceByID(serverId) 257 if err != nil { 258 return nil, err 259 } 260 261 nics, err := instance.GetINics() 262 if err != nil { 263 return nil, err 264 } else if len(nics) == 0 { 265 return nil, fmt.Errorf("AddBackendServer %s no network interface found", serverId) 266 } 267 268 subnets, err := self.lb.region.getSubnetIdsByInstanceId(instance.GetId()) 269 if err != nil { 270 return nil, err 271 } else if len(subnets) == 0 { 272 return nil, fmt.Errorf("AddBackendServer %s no subnet found", serverId) 273 } 274 275 net, err := self.lb.region.getNetwork(subnets[0]) 276 if err != nil { 277 return nil, err 278 } 279 280 backend, err := self.region.AddLoadBalancerBackend(self.GetId(), net.NeutronSubnetID, nics[0].GetIP(), port, weight) 281 if err != nil { 282 return nil, err 283 } 284 285 backend.lb = self.lb 286 backend.backendGroup = self 287 return backend, nil 288 } 289 290 func (self *SElbBackendGroup) RemoveBackendServer(backendId string, weight int, port int) error { 291 ibackend, err := self.GetILoadbalancerBackendById(backendId) 292 if err != nil { 293 if errors.Cause(err) == cloudprovider.ErrNotFound { 294 return nil 295 } 296 297 return errors.Wrap(err, "ElbBackendGroup.GetILoadbalancerBackendById") 298 } 299 300 err = self.region.RemoveLoadBalancerBackend(self.GetId(), backendId) 301 if err != nil { 302 return errors.Wrap(err, "ElbBackendGroup.RemoveBackendServer") 303 } 304 305 return cloudprovider.WaitDeleted(ibackend, 2*time.Second, 30*time.Second) 306 } 307 308 func (self *SElbBackendGroup) Delete(ctx context.Context) error { 309 if len(self.HealthMonitorID) > 0 { 310 err := self.region.DeleteLoadbalancerHealthCheck(self.HealthMonitorID) 311 if err != nil { 312 return errors.Wrap(err, "ElbBackendGroup.Delete.DeleteLoadbalancerHealthCheck") 313 } 314 } 315 316 // 删除后端服务器组的同时,删除掉无效的后端服务器数据 317 { 318 backends, err := self.region.getLoadBalancerAdminStateDownBackends(self.GetId()) 319 if err != nil { 320 return errors.Wrap(err, "SElbBackendGroup.Delete.getLoadBalancerAdminStateDownBackends") 321 } 322 323 for i := range backends { 324 backend := backends[i] 325 err := self.RemoveBackendServer(backend.GetId(), backend.GetPort(), backend.GetWeight()) 326 if err != nil { 327 return errors.Wrap(err, "SElbBackendGroup.Delete.RemoveBackendServer") 328 } 329 } 330 } 331 332 err := self.region.DeleteLoadBalancerBackendGroup(self.GetId()) 333 if err != nil { 334 return errors.Wrap(err, "ElbBackendGroup.Delete.DeleteLoadBalancerBackendGroup") 335 } 336 337 return cloudprovider.WaitDeleted(self, 2*time.Second, 30*time.Second) 338 } 339 340 func (self *SElbBackendGroup) Sync(ctx context.Context, group *cloudprovider.SLoadbalancerBackendGroup) error { 341 if group == nil { 342 return nil 343 } 344 345 _, err := self.region.UpdateLoadBalancerBackendGroup(self.GetId(), group) 346 return err 347 } 348 349 func (self *SRegion) GetLoadBalancerBackendGroupId(backendGroupId string) (*SElbBackendGroup, error) { 350 ret := &SElbBackendGroup{region: self} 351 res := fmt.Sprintf("elb/pools/" + backendGroupId) 352 resp, err := self.lbGet(res) 353 if err != nil { 354 return nil, err 355 } 356 return ret, resp.Unmarshal(ret, "pool") 357 } 358 359 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561550.html 360 func (self *SRegion) UpdateLoadBalancerBackendGroup(backendGroupID string, group *cloudprovider.SLoadbalancerBackendGroup) (*SElbBackendGroup, error) { 361 params := map[string]interface{}{ 362 "name": group.Name, 363 } 364 var scheduler string 365 if s, ok := LB_ALGORITHM_MAP[group.Scheduler]; !ok { 366 return nil, fmt.Errorf("UpdateLoadBalancerBackendGroup unsupported scheduler %s", group.Scheduler) 367 } else { 368 scheduler = s 369 } 370 params["lb_algorithm"] = scheduler 371 372 if group.StickySession == nil || group.StickySession.StickySession == api.LB_BOOL_OFF { 373 params["session_persistence"] = jsonutils.JSONNull 374 } else { 375 s := map[string]interface{}{} 376 timeout := int64(group.StickySession.StickySessionCookieTimeout / 60) 377 if group.ListenType == api.LB_LISTENER_TYPE_UDP || group.ListenType == api.LB_LISTENER_TYPE_TCP { 378 s["type"] = "SOURCE_IP" 379 if timeout > 0 { 380 s["persistence_timeout"] = timeout 381 } 382 } else { 383 s["type"] = LB_STICKY_SESSION_MAP[group.StickySession.StickySessionType] 384 if len(group.StickySession.StickySessionCookie) > 0 { 385 s["cookie_name"] = group.StickySession.StickySessionCookie 386 } else { 387 if timeout > 0 { 388 s["persistence_timeout"] = timeout 389 } 390 } 391 } 392 params["session_persistence"] = s 393 } 394 resp, err := self.lbUpdate("elb/pools/"+backendGroupID, map[string]interface{}{"pool": params}) 395 if err != nil { 396 return nil, err 397 } 398 399 ret := &SElbBackendGroup{} 400 err = resp.Unmarshal(ret, "pool") 401 if err != nil { 402 return nil, errors.Wrapf(err, "resp.Unmarshal") 403 } 404 405 if group.HealthCheck == nil && len(ret.HealthMonitorID) > 0 { 406 err := self.DeleteLoadbalancerHealthCheck(ret.HealthMonitorID) 407 if err != nil { 408 return ret, errors.Wrap(err, "DeleteLoadbalancerHealthCheck") 409 } 410 } 411 412 if group.HealthCheck != nil { 413 if len(ret.HealthMonitorID) == 0 { 414 _, err := self.CreateLoadBalancerHealthCheck(ret.GetId(), group.HealthCheck) 415 if err != nil { 416 return ret, errors.Wrap(err, "CreateLoadBalancerHealthCheck") 417 } 418 } else { 419 _, err := self.UpdateLoadBalancerHealthCheck(ret.HealthMonitorID, group.HealthCheck) 420 if err != nil { 421 return ret, errors.Wrap(err, "UpdateLoadBalancerHealthCheck") 422 } 423 } 424 } 425 426 ret.region = self 427 return ret, nil 428 } 429 430 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561551.html 431 func (self *SRegion) DeleteLoadBalancerBackendGroup(id string) error { 432 _, err := self.lbDelete("elb/pools/" + id) 433 return err 434 } 435 436 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561556.html 437 func (self *SRegion) AddLoadBalancerBackend(backendGroupId, subnetId, ipaddr string, port, weight int) (*SElbBackend, error) { 438 params := map[string]interface{}{ 439 "address": ipaddr, 440 "protocol_port": port, 441 "subnet_id": subnetId, 442 "weight": weight, 443 } 444 ret := &SElbBackend{} 445 resp, err := self.lbCreate(fmt.Sprintf("elb/pools/%s/members", backendGroupId), map[string]interface{}{"member": params}) 446 if err != nil { 447 return nil, err 448 } 449 return ret, resp.Unmarshal(ret, "member") 450 } 451 452 func (self *SRegion) RemoveLoadBalancerBackend(lbbgId string, backendId string) error { 453 _, err := self.lbDelete(fmt.Sprintf("elb/pools/%s/members/%s", lbbgId, backendId)) 454 return err 455 } 456 457 func (self *SRegion) getLoadBalancerBackends(backendGroupId string) ([]SElbBackend, error) { 458 res := fmt.Sprintf("elb/pools/%s/members", backendGroupId) 459 resp, err := self.lbList(res, url.Values{}) 460 if err != nil { 461 return nil, err 462 } 463 ret := []SElbBackend{} 464 return ret, resp.Unmarshal(&ret, "members") 465 } 466 467 func (self *SRegion) GetLoadBalancerBackends(backendGroupId string) ([]SElbBackend, error) { 468 ret, err := self.getLoadBalancerBackends(backendGroupId) 469 if err != nil { 470 return nil, errors.Wrap(err, "SRegion.GetLoadBalancerBackends.getLoadBalancerBackends") 471 } 472 473 // 过滤掉服务器已经被删除的backend。原因是运管平台查询不到已删除的服务器记录,导致同步出错。产生肮数据。 474 filtedRet := []SElbBackend{} 475 for i := range ret { 476 if ret[i].AdminStateUp { 477 backend := ret[i] 478 filtedRet = append(filtedRet, backend) 479 } 480 } 481 482 return filtedRet, nil 483 } 484 485 func (self *SRegion) getLoadBalancerAdminStateDownBackends(backendGroupId string) ([]SElbBackend, error) { 486 ret, err := self.getLoadBalancerBackends(backendGroupId) 487 if err != nil { 488 return nil, errors.Wrap(err, "SRegion.getLoadBalancerAdminStateDownBackends.getLoadBalancerBackends") 489 } 490 491 filtedRet := []SElbBackend{} 492 for i := range ret { 493 if !ret[i].AdminStateUp { 494 backend := ret[i] 495 filtedRet = append(filtedRet, backend) 496 } 497 } 498 499 return filtedRet, nil 500 } 501 502 func (self *SRegion) GetLoadBalancerHealthCheck(healthCheckId string) (*SElbHealthCheck, error) { 503 resp, err := self.lbGet("elb/healthmonitors/" + healthCheckId) 504 if err != nil { 505 return nil, err 506 } 507 ret := &SElbHealthCheck{region: self} 508 return ret, resp.Unmarshal(ret, "healthmonitor") 509 }