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