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