yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcs/loadbalancer.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 "time" 22 23 "yunion.io/x/jsonutils" 24 "yunion.io/x/log" 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 var LB_ALGORITHM_MAP = map[string]string{ 34 api.LB_SCHEDULER_WRR: "ROUND_ROBIN", 35 api.LB_SCHEDULER_WLC: "LEAST_CONNECTIONS", 36 api.LB_SCHEDULER_SCH: "SOURCE_IP", 37 } 38 39 var LB_PROTOCOL_MAP = map[string]string{ 40 api.LB_LISTENER_TYPE_HTTP: "HTTP", 41 api.LB_LISTENER_TYPE_HTTPS: "TERMINATED_HTTPS", 42 api.LB_LISTENER_TYPE_UDP: "UDP", 43 api.LB_LISTENER_TYPE_TCP: "TCP", 44 } 45 46 var LBBG_PROTOCOL_MAP = map[string]string{ 47 api.LB_LISTENER_TYPE_HTTP: "HTTP", 48 api.LB_LISTENER_TYPE_HTTPS: "HTTP", 49 api.LB_LISTENER_TYPE_UDP: "UDP", 50 api.LB_LISTENER_TYPE_TCP: "TCP", 51 } 52 53 var LB_STICKY_SESSION_MAP = map[string]string{ 54 api.LB_STICKY_SESSION_TYPE_INSERT: "HTTP_COOKIE", 55 api.LB_STICKY_SESSION_TYPE_SERVER: "APP_COOKIE", 56 } 57 58 var LB_HEALTHCHECK_TYPE_MAP = map[string]string{ 59 api.LB_HEALTH_CHECK_HTTP: "HTTP", 60 api.LB_HEALTH_CHECK_TCP: "TCP", 61 api.LB_HEALTH_CHECK_UDP: "UDP_CONNECT", 62 } 63 64 type SLoadbalancer struct { 65 multicloud.SResourceBase 66 huawei.HuaweiTags 67 region *SRegion 68 subnet *SNetwork 69 eip *SEip 70 71 Description string `json:"description"` 72 ProvisioningStatus string `json:"provisioning_status"` 73 TenantId string `json:"tenant_id"` 74 ProjectId string `json:"project_id"` 75 AdminStateUp bool `json:"admin_state_up"` 76 Provider string `json:"provider"` 77 Pools []Pool `json:"pools"` 78 Listeners []Listener `json:"listeners"` 79 VipPortId string `json:"vip_port_id"` 80 OperatingStatus string `json:"operating_status"` 81 VipAddress string `json:"vip_address"` 82 VipSubnetId string `json:"vip_subnet_id"` 83 Id string `json:"id"` 84 Name string `json:"name"` 85 CreatedAt time.Time `json:"created_at"` 86 UpdatedAt time.Time `json:"updated_at"` 87 } 88 89 type Listener struct { 90 Id string `json:"id"` 91 } 92 93 type Pool struct { 94 Id string `json:"id"` 95 } 96 97 func (self *SLoadbalancer) GetIEIP() (cloudprovider.ICloudEIP, error) { 98 if self.GetEip() == nil { 99 return nil, nil 100 } 101 102 return self.eip, nil 103 } 104 105 func (self *SLoadbalancer) GetId() string { 106 return self.Id 107 } 108 109 func (self *SLoadbalancer) GetName() string { 110 return self.Name 111 } 112 113 func (self *SLoadbalancer) GetGlobalId() string { 114 return self.Id 115 } 116 117 func (self *SLoadbalancer) GetStatus() string { 118 return api.LB_STATUS_ENABLED 119 } 120 121 func (self *SLoadbalancer) Refresh() error { 122 lb, err := self.region.GetLoadbalancer(self.GetId()) 123 if err != nil { 124 return err 125 } 126 127 return jsonutils.Update(self, lb) 128 } 129 130 func (self *SLoadbalancer) IsEmulated() bool { 131 return false 132 } 133 134 func (self *SLoadbalancer) GetProjectId() string { 135 return self.ProjectId 136 } 137 138 func (self *SLoadbalancer) GetAddress() string { 139 return self.VipAddress 140 } 141 142 // todo: api.LB_ADDR_TYPE_INTERNET? 143 func (self *SLoadbalancer) GetAddressType() string { 144 return api.LB_ADDR_TYPE_INTRANET 145 } 146 147 func (self *SLoadbalancer) GetNetworkType() string { 148 return api.LB_NETWORK_TYPE_VPC 149 } 150 151 func (self *SLoadbalancer) GetNetworkIds() []string { 152 net := self.GetNetwork() 153 if net != nil { 154 return []string{net.GetId()} 155 } 156 157 return []string{} 158 } 159 160 func (self *SLoadbalancer) GetNetwork() *SNetwork { 161 if self.subnet == nil { 162 port, err := self.region.GetPort(self.VipPortId) 163 if err == nil { 164 net, err := self.region.GetNetwork(port.NetworkId) 165 if err == nil { 166 self.subnet = net 167 } else { 168 log.Debugf("huawei.SLoadbalancer.getNetwork %s", err) 169 } 170 } else { 171 log.Debugf("huawei.SLoadbalancer.GetPort %s", err) 172 } 173 } 174 175 return self.subnet 176 } 177 178 func (self *SLoadbalancer) GetEip() *SEip { 179 if self.eip == nil { 180 eips, _ := self.region.GetEips(self.VipPortId, nil) 181 for i := range eips { 182 self.eip = &eips[i] 183 } 184 } 185 return self.eip 186 } 187 188 func (self *SLoadbalancer) GetVpcId() string { 189 net := self.GetNetwork() 190 if net != nil { 191 return net.VpcId 192 } 193 194 return "" 195 } 196 197 func (self *SLoadbalancer) GetZoneId() string { 198 net := self.GetNetwork() 199 if net != nil { 200 z, err := self.region.GetZoneById(net.AvailabilityZone) 201 if err != nil { 202 log.Infof("getZoneById %s %s", net.AvailabilityZone, err) 203 return "" 204 } 205 206 return z.GetGlobalId() 207 } 208 209 return "" 210 } 211 212 func (self *SLoadbalancer) GetZone1Id() string { 213 return "" 214 } 215 216 func (self *SLoadbalancer) GetLoadbalancerSpec() string { 217 return "" 218 } 219 220 func (self *SLoadbalancer) GetChargeType() string { 221 eip := self.GetEip() 222 if eip != nil { 223 return eip.GetInternetChargeType() 224 } 225 226 return api.EIP_CHARGE_TYPE_BY_TRAFFIC 227 } 228 229 func (self *SLoadbalancer) GetEgressMbps() int { 230 eip := self.GetEip() 231 if eip != nil { 232 return eip.GetBandwidth() 233 } 234 235 return 0 236 } 237 238 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0141008275.html 239 func (self *SLoadbalancer) Delete(ctx context.Context) error { 240 return self.region.DeleteLoadBalancer(self.GetId()) 241 } 242 243 func (self *SLoadbalancer) Start() error { 244 return nil 245 } 246 247 func (self *SLoadbalancer) Stop() error { 248 return cloudprovider.ErrNotSupported 249 } 250 251 func (self *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) { 252 ret, err := self.region.GetLoadBalancerListeners(self.GetId()) 253 if err != nil { 254 return nil, err 255 } 256 257 iret := make([]cloudprovider.ICloudLoadbalancerListener, 0) 258 for i := range ret { 259 listener := ret[i] 260 listener.lb = self 261 iret = append(iret, &listener) 262 } 263 264 return iret, nil 265 } 266 267 func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { 268 ret, err := self.region.GetLoadBalancerBackendGroups(self.GetId()) 269 if err != nil { 270 return nil, err 271 } 272 273 iret := make([]cloudprovider.ICloudLoadbalancerBackendGroup, 0) 274 for i := range ret { 275 bg := ret[i] 276 bg.lb = self 277 bg.region = self.region 278 iret = append(iret, &bg) 279 } 280 281 return iret, nil 282 } 283 284 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561549.html 285 func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { 286 ret, err := self.region.CreateLoadBalancerBackendGroup(group) 287 ret.lb = self 288 return &ret, err 289 } 290 291 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561563.html 292 func (self *SLoadbalancer) CreateHealthCheck(backendGroupId string, healthcheck *cloudprovider.SLoadbalancerHealthCheck) error { 293 _, err := self.region.CreateLoadBalancerHealthCheck(backendGroupId, healthcheck) 294 return err 295 } 296 297 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561548.html 298 func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { 299 ret := &SElbBackendGroup{lb: self, region: self.region} 300 return ret, self.region.lbGet("lbaas/pools/"+groupId, ret) 301 } 302 303 func (self *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) { 304 ret, err := self.region.CreateLoadBalancerListener(listener) 305 if err != nil { 306 return nil, err 307 } 308 309 ret.lb = self 310 return &ret, nil 311 } 312 313 func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) { 314 ret := &SElbListener{lb: self} 315 return ret, self.region.lbGet("lbaas/listeners/"+listenerId, ret) 316 } 317 318 func (self *SRegion) GetLoadbalancer(id string) (*SLoadbalancer, error) { 319 ret := &SLoadbalancer{region: self} 320 return ret, self.lbGet("lbaas/loadbalancers/"+id, ret) 321 } 322 323 func (self *SRegion) DeleteLoadBalancer(elbId string) error { 324 resource := fmt.Sprintf("lbaas/loadbalancers/%s", elbId) 325 return self.lbDelete(resource) 326 } 327 328 func (self *SRegion) GetLoadBalancerListeners(lbId string) ([]SElbListener, error) { 329 ret := []SElbListener{} 330 params := url.Values{} 331 if len(lbId) > 0 { 332 params.Set("loadbalancer_id", lbId) 333 } 334 return ret, self.lbList("lbaas/listeners", params, &ret) 335 } 336 337 func (self *SRegion) CreateLoadBalancerListener(listener *cloudprovider.SLoadbalancerListener) (SElbListener, error) { 338 params := map[string]interface{}{ 339 "name": listener.Name, 340 "description": listener.Description, 341 "protocol": LB_PROTOCOL_MAP[listener.ListenerType], 342 "protocol_port": listener.ListenerPort, 343 "loadbalancer_id": listener.LoadbalancerID, 344 "http2_enable": listener.EnableHTTP2, 345 } 346 if len(listener.BackendGroupID) > 0 { 347 params["default_pool_id"] = listener.BackendGroupID 348 } 349 350 if listener.ListenerType == api.LB_LISTENER_TYPE_HTTPS { 351 params["default_tls_container_ref"] = listener.CertificateID 352 } 353 354 if listener.XForwardedFor { 355 params["insert_headers"] = map[string]interface{}{ 356 "X-Forwarded-ELB-IP": listener.XForwardedFor, 357 } 358 } 359 360 ret := SElbListener{} 361 err := self.lbCreate("lbaas/listeners", map[string]interface{}{"listener": params}, &ret) 362 if err != nil { 363 return ret, err 364 } 365 return ret, nil 366 } 367 368 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561547.html 369 func (self *SRegion) GetLoadBalancerBackendGroups(elbId string) ([]SElbBackendGroup, error) { 370 query := url.Values{} 371 if len(elbId) > 0 { 372 query.Set("loadbalancer_id", elbId) 373 } 374 375 ret := []SElbBackendGroup{} 376 return ret, self.lbList("lbaas/pools", query, &ret) 377 } 378 379 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561547.html 380 func (self *SRegion) CreateLoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (SElbBackendGroup, error) { 381 ret := SElbBackendGroup{region: self} 382 var protocol, scheduler string 383 if s, ok := LB_ALGORITHM_MAP[group.Scheduler]; !ok { 384 return ret, fmt.Errorf("CreateILoadBalancerBackendGroup unsupported scheduler %s", group.Scheduler) 385 } else { 386 scheduler = s 387 } 388 389 if t, ok := LBBG_PROTOCOL_MAP[group.ListenType]; !ok { 390 return ret, fmt.Errorf("CreateILoadBalancerBackendGroup unsupported listener type %s", group.ListenType) 391 } else { 392 protocol = t 393 } 394 395 params := map[string]interface{}{ 396 "project_id": self.client.projectId, 397 "name": group.Name, 398 "protocol": protocol, 399 "lb_algorithm": scheduler, 400 } 401 402 if len(group.ListenerID) > 0 { 403 params["listener_id"] = group.ListenerID 404 } else if len(group.LoadbalancerID) > 0 { 405 params["loadbalancer_id"] = group.LoadbalancerID 406 } else { 407 return ret, fmt.Errorf("CreateLoadBalancerBackendGroup one of listener id / loadbalancer id must be specified") 408 } 409 410 if group.StickySession != nil { 411 s := map[string]interface{}{} 412 timeout := int64(group.StickySession.StickySessionCookieTimeout / 60) 413 if group.ListenType == api.LB_LISTENER_TYPE_UDP || group.ListenType == api.LB_LISTENER_TYPE_TCP { 414 s["type"] = "SOURCE_IP" 415 if timeout > 0 { 416 s["persistence_timeout"] = timeout 417 } 418 } else { 419 s["type"] = LB_STICKY_SESSION_MAP[group.StickySession.StickySessionType] 420 if len(group.StickySession.StickySessionCookie) > 0 { 421 s["cookie_name"] = group.StickySession.StickySessionCookie 422 } else { 423 if timeout > 0 { 424 s["persistence_timeout"] = timeout 425 } 426 } 427 } 428 params["session_persistence"] = s 429 } 430 err := self.lbCreate("lbaas/pools", map[string]interface{}{"pool": params}, &ret) 431 if err != nil { 432 return ret, err 433 } 434 435 if group.HealthCheck != nil { 436 _, err := self.CreateLoadBalancerHealthCheck(ret.GetId(), group.HealthCheck) 437 if err != nil { 438 return ret, err 439 } 440 } 441 442 return ret, nil 443 } 444 445 func (self *SRegion) CreateLoadBalancerHealthCheck(backendGroupId string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) { 446 params := map[string]interface{}{ 447 "delay": healthCheck.HealthCheckInterval, 448 "max_retries": healthCheck.HealthCheckRise, 449 "pool_id": backendGroupId, 450 "timeout": healthCheck.HealthCheckTimeout, 451 "type": LB_HEALTHCHECK_TYPE_MAP[healthCheck.HealthCheckType], 452 } 453 if healthCheck.HealthCheckType == api.LB_HEALTH_CHECK_HTTP { 454 if len(healthCheck.HealthCheckDomain) > 0 { 455 params["domain_name"] = healthCheck.HealthCheckDomain 456 } 457 458 if len(healthCheck.HealthCheckURI) > 0 { 459 params["url_path"] = healthCheck.HealthCheckURI 460 } 461 462 if len(healthCheck.HealthCheckHttpCode) > 0 { 463 params["expected_codes"] = ToHuaweiHealthCheckHttpCode(healthCheck.HealthCheckHttpCode) 464 } 465 } 466 467 ret := SElbHealthCheck{region: self} 468 err := self.lbCreate("lbaas/healthmonitors", map[string]interface{}{"healthmonitor": params}, &ret) 469 if err != nil { 470 return ret, err 471 } 472 473 return ret, nil 474 } 475 476 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561564.html 477 func (self *SRegion) UpdateLoadBalancerHealthCheck(healthCheckId string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) { 478 params := map[string]interface{}{ 479 "delay": healthCheck.HealthCheckInterval, 480 "max_retries": healthCheck.HealthCheckRise, 481 "timeout": healthCheck.HealthCheckTimeout, 482 } 483 if healthCheck.HealthCheckType == api.LB_HEALTH_CHECK_HTTP { 484 if len(healthCheck.HealthCheckDomain) > 0 { 485 params["domain_name"] = healthCheck.HealthCheckDomain 486 } 487 488 if len(healthCheck.HealthCheckURI) > 0 { 489 params["url_path"] = healthCheck.HealthCheckURI 490 } 491 492 if len(healthCheck.HealthCheckHttpCode) > 0 { 493 params["expected_codes"] = ToHuaweiHealthCheckHttpCode(healthCheck.HealthCheckHttpCode) 494 } 495 } 496 497 ret := SElbHealthCheck{region: self} 498 res := "lbaas/healthmonitors/" + healthCheckId 499 err := self.lbUpdate(res, map[string]interface{}{"healthmonitor": params}) 500 if err != nil { 501 return ret, err 502 } 503 return ret, self.lbGet(res, &ret) 504 } 505 506 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561565.html 507 func (self *SRegion) DeleteLoadbalancerHealthCheck(healthCheckId string) error { 508 return self.lbDelete("lbaas/healthmonitors/" + healthCheckId) 509 } 510 511 func (self *SLoadbalancer) SetTags(tags map[string]string, replace bool) error { 512 return cloudprovider.ErrNotSupported 513 } 514 515 func (self *SRegion) lbGet(resource string, retVal interface{}) error { 516 return self.client.get("vpc", "v2.0", self.Id, resource, retVal) 517 } 518 519 func (self *SRegion) lbList(resource string, params url.Values, retVal interface{}) error { 520 return self.list("vpc", "v2.0", resource, params, retVal) 521 } 522 523 func (self *SRegion) lbDelete(resource string) error { 524 return self.delete("vpc", "v2.0", resource) 525 } 526 527 func (self *SRegion) lbCreate(resource string, params map[string]interface{}, retVal interface{}) error { 528 return self.create("vpc", "v2.0", resource, params, retVal) 529 } 530 531 func (self *SRegion) lbUpdate(resource string, params map[string]interface{}) error { 532 return self.update("vpc", "v2.0", resource, params) 533 } 534 535 // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561535.html 536 func (self *SRegion) CreateLoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (*SLoadbalancer, error) { 537 subnet, err := self.GetNetwork(loadbalancer.NetworkIDs[0]) 538 if err != nil { 539 return nil, errors.Wrap(err, "SRegion.CreateLoadBalancer.getNetwork") 540 } 541 542 params := map[string]interface{}{ 543 "name": loadbalancer.Name, 544 "vip_subnet_id": subnet.NeutronSubnetId, 545 "tenant_id": self.client.projectId, 546 } 547 if len(loadbalancer.Address) > 0 { 548 params["vip_address"] = loadbalancer.Address 549 } 550 ret := &SLoadbalancer{region: self} 551 err = self.lbCreate("lbaas/loadbalancers", map[string]interface{}{"loadbalancer": params}, ret) 552 if err != nil { 553 return nil, err 554 } 555 556 // 创建公网类型ELB 557 if len(loadbalancer.EipID) > 0 { 558 err := self.AssociateEipWithPortId(loadbalancer.EipID, ret.VipPortId) 559 if err != nil { 560 return ret, errors.Wrap(err, "SRegion.CreateLoadBalancer.AssociateEipWithPortId") 561 } 562 } 563 return ret, nil 564 } 565 566 func (self *SRegion) GetLoadBalancers() ([]SLoadbalancer, error) { 567 lbs := []SLoadbalancer{} 568 params := url.Values{} 569 return lbs, self.lbList("lbaas/loadbalancers", params, &lbs) 570 } 571 572 func (self *SRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) { 573 elbs, err := self.GetLoadBalancers() 574 if err != nil { 575 return nil, err 576 } 577 578 ret := []cloudprovider.ICloudLoadbalancer{} 579 for i := range elbs { 580 elbs[i].region = self 581 ret = append(ret, &elbs[i]) 582 } 583 584 return ret, nil 585 } 586 587 func (self *SRegion) GetILoadBalancerById(id string) (cloudprovider.ICloudLoadbalancer, error) { 588 elb, err := self.GetLoadbalancer(id) 589 if err != nil { 590 return nil, err 591 } 592 return elb, nil 593 } 594 595 func (self *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (cloudprovider.ICloudLoadbalancer, error) { 596 ret, err := self.CreateLoadBalancer(loadbalancer) 597 if err != nil { 598 return nil, err 599 } 600 601 return ret, nil 602 }