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