yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcso/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 hcso 16 17 import ( 18 "context" 19 "fmt" 20 "strings" 21 "time" 22 23 "yunion.io/x/jsonutils" 24 "yunion.io/x/pkg/errors" 25 26 api "yunion.io/x/cloudmux/pkg/apis/compute" 27 "yunion.io/x/cloudmux/pkg/cloudprovider" 28 "yunion.io/x/cloudmux/pkg/multicloud" 29 "yunion.io/x/cloudmux/pkg/multicloud/huawei" 30 ) 31 32 type SElbBackendGroup struct { 33 multicloud.SResourceBase 34 huawei.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 m := self.lb.region.ecsClient.ElbBackend 247 err := m.SetBackendGroupId(self.GetId()) 248 if err != nil { 249 return nil, err 250 } 251 252 backend := SElbBackend{} 253 err = DoGet(m.Get, serverId, nil, &backend) 254 if err != nil { 255 return nil, err 256 } 257 258 backend.lb = self.lb 259 backend.backendGroup = self 260 return &backend, nil 261 } 262 263 func (self *SElbBackendGroup) AddBackendServer(serverId string, weight int, port int) (cloudprovider.ICloudLoadbalancerBackend, error) { 264 instance, err := self.lb.region.GetInstanceByID(serverId) 265 if err != nil { 266 return nil, err 267 } 268 269 nics, err := instance.GetINics() 270 if err != nil { 271 return nil, err 272 } else if len(nics) == 0 { 273 return nil, fmt.Errorf("AddBackendServer %s no network interface found", serverId) 274 } 275 276 subnets, err := self.lb.region.getSubnetIdsByInstanceId(instance.GetId()) 277 if err != nil { 278 return nil, err 279 } else if len(subnets) == 0 { 280 return nil, fmt.Errorf("AddBackendServer %s no subnet found", serverId) 281 } 282 283 net, err := self.lb.region.getNetwork(subnets[0]) 284 if err != nil { 285 return nil, err 286 } 287 288 backend, err := self.region.AddLoadBalancerBackend(self.GetId(), net.NeutronSubnetID, nics[0].GetIP(), port, weight) 289 if err != nil { 290 return nil, err 291 } 292 293 backend.lb = self.lb 294 backend.backendGroup = self 295 return &backend, nil 296 } 297 298 func (self *SElbBackendGroup) RemoveBackendServer(backendId string, weight int, port int) error { 299 ibackend, err := self.GetILoadbalancerBackendById(backendId) 300 if err != nil { 301 if errors.Cause(err) == cloudprovider.ErrNotFound { 302 return nil 303 } 304 305 return errors.Wrap(err, "ElbBackendGroup.GetILoadbalancerBackendById") 306 } 307 308 err = self.region.RemoveLoadBalancerBackend(self.GetId(), backendId) 309 if err != nil { 310 return errors.Wrap(err, "ElbBackendGroup.RemoveBackendServer") 311 } 312 313 return cloudprovider.WaitDeleted(ibackend, 2*time.Second, 30*time.Second) 314 } 315 316 func (self *SElbBackendGroup) Delete(ctx context.Context) error { 317 if len(self.HealthMonitorID) > 0 { 318 err := self.region.DeleteLoadbalancerHealthCheck(self.HealthMonitorID) 319 if err != nil { 320 return errors.Wrap(err, "ElbBackendGroup.Delete.DeleteLoadbalancerHealthCheck") 321 } 322 } 323 324 // 删除后端服务器组的同时,删除掉无效的后端服务器数据 325 { 326 backends, err := self.region.getLoadBalancerAdminStateDownBackends(self.GetId()) 327 if err != nil { 328 return errors.Wrap(err, "SElbBackendGroup.Delete.getLoadBalancerAdminStateDownBackends") 329 } 330 331 for i := range backends { 332 backend := backends[i] 333 err := self.RemoveBackendServer(backend.GetId(), backend.GetPort(), backend.GetWeight()) 334 if err != nil { 335 return errors.Wrap(err, "SElbBackendGroup.Delete.RemoveBackendServer") 336 } 337 } 338 } 339 340 err := self.region.DeleteLoadBalancerBackendGroup(self.GetId()) 341 if err != nil { 342 return errors.Wrap(err, "ElbBackendGroup.Delete.DeleteLoadBalancerBackendGroup") 343 } 344 345 return cloudprovider.WaitDeleted(self, 2*time.Second, 30*time.Second) 346 } 347 348 func (self *SElbBackendGroup) Sync(ctx context.Context, group *cloudprovider.SLoadbalancerBackendGroup) error { 349 if group == nil { 350 return nil 351 } 352 353 _, err := self.region.UpdateLoadBalancerBackendGroup(self.GetId(), group) 354 return err 355 } 356 357 func (self *SRegion) GetLoadBalancerBackendGroupId(backendGroupId string) (SElbBackendGroup, error) { 358 ret := SElbBackendGroup{} 359 err := DoGet(self.ecsClient.ElbBackendGroup.Get, backendGroupId, nil, &ret) 360 if err != nil { 361 return ret, err 362 } 363 364 ret.region = self 365 return ret, nil 366 } 367 368 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561550.html 369 func (self *SRegion) UpdateLoadBalancerBackendGroup(backendGroupID string, group *cloudprovider.SLoadbalancerBackendGroup) (SElbBackendGroup, error) { 370 params := jsonutils.NewDict() 371 poolObj := jsonutils.NewDict() 372 poolObj.Set("name", jsonutils.NewString(group.Name)) 373 var scheduler string 374 if s, ok := LB_ALGORITHM_MAP[group.Scheduler]; !ok { 375 return SElbBackendGroup{}, fmt.Errorf("UpdateLoadBalancerBackendGroup unsupported scheduler %s", group.Scheduler) 376 } else { 377 scheduler = s 378 } 379 poolObj.Set("lb_algorithm", jsonutils.NewString(scheduler)) 380 381 if group.StickySession == nil || group.StickySession.StickySession == api.LB_BOOL_OFF { 382 poolObj.Set("session_persistence", jsonutils.JSONNull) 383 } else { 384 s := jsonutils.NewDict() 385 timeout := int64(group.StickySession.StickySessionCookieTimeout / 60) 386 if group.ListenType == api.LB_LISTENER_TYPE_UDP || group.ListenType == api.LB_LISTENER_TYPE_TCP { 387 s.Set("type", jsonutils.NewString("SOURCE_IP")) 388 if timeout > 0 { 389 s.Set("persistence_timeout", jsonutils.NewInt(timeout)) 390 } 391 } else { 392 s.Set("type", jsonutils.NewString(LB_STICKY_SESSION_MAP[group.StickySession.StickySessionType])) 393 if len(group.StickySession.StickySessionCookie) > 0 { 394 s.Set("cookie_name", jsonutils.NewString(group.StickySession.StickySessionCookie)) 395 } else { 396 if timeout > 0 { 397 s.Set("persistence_timeout", jsonutils.NewInt(timeout)) 398 } 399 } 400 } 401 402 poolObj.Set("session_persistence", s) 403 } 404 params.Set("pool", poolObj) 405 406 ret := SElbBackendGroup{} 407 err := DoUpdate(self.ecsClient.ElbBackendGroup.Update, backendGroupID, params, &ret) 408 if err != nil { 409 return ret, errors.Wrap(err, "ElbBackendGroup.Update") 410 } 411 412 if group.HealthCheck == nil && len(ret.HealthMonitorID) > 0 { 413 err := self.DeleteLoadbalancerHealthCheck(ret.HealthMonitorID) 414 if err != nil { 415 return ret, errors.Wrap(err, "DeleteLoadbalancerHealthCheck") 416 } 417 } 418 419 if group.HealthCheck != nil { 420 if len(ret.HealthMonitorID) == 0 { 421 _, err := self.CreateLoadBalancerHealthCheck(ret.GetId(), group.HealthCheck) 422 if err != nil { 423 return ret, errors.Wrap(err, "CreateLoadBalancerHealthCheck") 424 } 425 } else { 426 _, err := self.UpdateLoadBalancerHealthCheck(ret.HealthMonitorID, group.HealthCheck) 427 if err != nil { 428 return ret, errors.Wrap(err, "UpdateLoadBalancerHealthCheck") 429 } 430 } 431 } 432 433 ret.region = self 434 return ret, nil 435 } 436 437 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561551.html 438 func (self *SRegion) DeleteLoadBalancerBackendGroup(backendGroupID string) error { 439 return DoDelete(self.ecsClient.ElbBackendGroup.Delete, backendGroupID, nil, nil) 440 } 441 442 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561556.html 443 func (self *SRegion) AddLoadBalancerBackend(backendGroupId, subnetId, ipaddr string, port, weight int) (SElbBackend, error) { 444 backend := SElbBackend{} 445 params := jsonutils.NewDict() 446 memberObj := jsonutils.NewDict() 447 memberObj.Set("address", jsonutils.NewString(ipaddr)) 448 memberObj.Set("protocol_port", jsonutils.NewInt(int64(port))) 449 memberObj.Set("subnet_id", jsonutils.NewString(subnetId)) 450 memberObj.Set("weight", jsonutils.NewInt(int64(weight))) 451 params.Set("member", memberObj) 452 453 m := self.ecsClient.ElbBackend 454 err := m.SetBackendGroupId(backendGroupId) 455 if err != nil { 456 return backend, err 457 } 458 459 err = DoCreate(m.Create, params, &backend) 460 if err != nil { 461 return backend, err 462 } 463 464 return backend, nil 465 } 466 467 func (self *SRegion) RemoveLoadBalancerBackend(lbbgId string, backendId string) error { 468 m := self.ecsClient.ElbBackend 469 err := m.SetBackendGroupId(lbbgId) 470 if err != nil { 471 return err 472 } 473 474 return DoDelete(m.Delete, backendId, nil, nil) 475 } 476 477 func (self *SRegion) getLoadBalancerBackends(backendGroupId string) ([]SElbBackend, error) { 478 m := self.ecsClient.ElbBackend 479 err := m.SetBackendGroupId(backendGroupId) 480 if err != nil { 481 return nil, err 482 } 483 484 ret := []SElbBackend{} 485 err = doListAll(m.List, nil, &ret) 486 if err != nil { 487 return nil, err 488 } 489 490 for i := range ret { 491 backend := ret[i] 492 backend.region = self 493 } 494 495 return ret, nil 496 } 497 498 func (self *SRegion) GetLoadBalancerBackends(backendGroupId string) ([]SElbBackend, error) { 499 ret, err := self.getLoadBalancerBackends(backendGroupId) 500 if err != nil { 501 return nil, errors.Wrap(err, "SRegion.GetLoadBalancerBackends.getLoadBalancerBackends") 502 } 503 504 // 过滤掉服务器已经被删除的backend。原因是运管平台查询不到已删除的服务器记录,导致同步出错。产生肮数据。 505 filtedRet := []SElbBackend{} 506 for i := range ret { 507 if ret[i].AdminStateUp { 508 backend := ret[i] 509 filtedRet = append(filtedRet, backend) 510 } 511 } 512 513 return filtedRet, nil 514 } 515 516 func (self *SRegion) getLoadBalancerAdminStateDownBackends(backendGroupId string) ([]SElbBackend, error) { 517 ret, err := self.getLoadBalancerBackends(backendGroupId) 518 if err != nil { 519 return nil, errors.Wrap(err, "SRegion.getLoadBalancerAdminStateDownBackends.getLoadBalancerBackends") 520 } 521 522 filtedRet := []SElbBackend{} 523 for i := range ret { 524 if !ret[i].AdminStateUp { 525 backend := ret[i] 526 filtedRet = append(filtedRet, backend) 527 } 528 } 529 530 return filtedRet, nil 531 } 532 533 func (self *SRegion) GetLoadBalancerHealthCheck(healthCheckId string) (SElbHealthCheck, error) { 534 ret := SElbHealthCheck{} 535 err := DoGet(self.ecsClient.ElbHealthCheck.Get, healthCheckId, nil, &ret) 536 if err != nil { 537 return ret, err 538 } 539 540 ret.region = self 541 return ret, nil 542 }