yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aliyun/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 aliyun 16 17 import ( 18 "context" 19 "fmt" 20 "strings" 21 "time" 22 23 "yunion.io/x/jsonutils" 24 "yunion.io/x/log" 25 "yunion.io/x/pkg/errors" 26 "yunion.io/x/pkg/utils" 27 28 api "yunion.io/x/cloudmux/pkg/apis/compute" 29 "yunion.io/x/cloudmux/pkg/cloudprovider" 30 "yunion.io/x/cloudmux/pkg/multicloud" 31 ) 32 33 type ListenerProtocol string 34 35 const ( 36 ListenerProtocolTCP ListenerProtocol = "tcp" 37 ListenerProtocolUDP ListenerProtocol = "udp" 38 ListenerProtocolHTTP ListenerProtocol = "http" 39 ListenerProtocolHTTPS ListenerProtocol = "https" 40 ) 41 42 type ListenerPorts struct { 43 ListenerPort []int 44 } 45 46 type ListenerPortsAndProtocol struct { 47 ListenerPortAndProtocol []ListenerPortAndProtocol 48 } 49 50 type ListenerPortAndProtocol struct { 51 Description string 52 ListenerPort int 53 ListenerProtocol ListenerProtocol 54 } 55 56 type BackendServers struct { 57 BackendServer []SLoadbalancerDefaultBackend 58 } 59 60 type SLoadbalancer struct { 61 multicloud.SLoadbalancerBase 62 AliyunTags 63 region *SRegion 64 65 LoadBalancerId string //负载均衡实例ID。 66 LoadBalancerName string //负载均衡实例的名称。 67 LoadBalancerStatus string //负载均衡实例状态:inactive: 此状态的实例监听不会再转发流量。active: 实例创建后,默认状态为active。 locked: 实例已经被锁定。 68 Address string //负载均衡实例的服务地址。 69 RegionId string //负载均衡实例的地域ID。 70 RegionIdAlias string //负载均衡实例的地域名称。 71 AddressType string //负载均衡实例的网络类型。 72 VSwitchId string //私网负载均衡实例的交换机ID。 73 VpcId string //私网负载均衡实例的专有网络ID。 74 NetworkType string //私网负载均衡实例的网络类型:vpc:专有网络实例 classic:经典网络实例 75 ListenerPorts ListenerPorts 76 ListenerPortsAndProtocol ListenerPortsAndProtocol 77 BackendServers BackendServers 78 CreateTime time.Time //负载均衡实例的创建时间。 79 MasterZoneId string //实例的主可用区ID。 80 SlaveZoneId string //实例的备可用区ID。 81 InternetChargeType TInternetChargeType //公网实例的计费方式。取值:paybybandwidth:按带宽计费 paybytraffic:按流量计费(默认值) 说明 当 PayType参数的值为PrePay时,只支持按带宽计费。 82 InternetChargeTypeAlias TInternetChargeType 83 84 PayType string //实例的计费类型,取值:PayOnDemand:按量付费 PrePay:预付费 85 ResourceGroupId string //企业资源组ID。 86 LoadBalancerSpec string //负载均衡实例的的性能规格 87 Bandwidth int //按带宽计费的公网型实例的带宽峰值 88 } 89 90 func (lb *SLoadbalancer) GetName() string { 91 return lb.LoadBalancerName 92 } 93 94 func (lb *SLoadbalancer) GetId() string { 95 return lb.LoadBalancerId 96 } 97 98 func (lb *SLoadbalancer) GetGlobalId() string { 99 return lb.LoadBalancerId 100 } 101 102 func (lb *SLoadbalancer) GetStatus() string { 103 if lb.LoadBalancerStatus == "active" { 104 return api.LB_STATUS_ENABLED 105 } 106 return api.LB_STATUS_DISABLED 107 } 108 109 func (lb *SLoadbalancer) GetAddress() string { 110 return lb.Address 111 } 112 113 func (lb *SLoadbalancer) GetAddressType() string { 114 return lb.AddressType 115 } 116 117 func (lb *SLoadbalancer) GetNetworkType() string { 118 return lb.NetworkType 119 } 120 121 func (lb *SLoadbalancer) GetNetworkIds() []string { 122 return []string{lb.VSwitchId} 123 } 124 125 func (lb *SLoadbalancer) GetZoneId() string { 126 zone, err := lb.region.getZoneById(transZoneIdToEcsZoneId(lb.region, "elb", lb.MasterZoneId)) 127 if err != nil { 128 log.Errorf("failed to find zone for lb %s error: %v", lb.LoadBalancerName, err) 129 return "" 130 } 131 return zone.GetGlobalId() 132 } 133 134 func (self *SLoadbalancer) GetZone1Id() string { 135 return "" 136 } 137 138 func (lb *SLoadbalancer) IsEmulated() bool { 139 return false 140 } 141 142 func (lb *SLoadbalancer) GetVpcId() string { 143 return lb.VpcId 144 } 145 146 func (lb *SLoadbalancer) Refresh() error { 147 loadbalancer, err := lb.region.GetLoadbalancerDetail(lb.LoadBalancerId) 148 if err != nil { 149 return err 150 } 151 return jsonutils.Update(lb, loadbalancer) 152 } 153 154 func (region *SRegion) GetLoadbalancers(ids []string) ([]SLoadbalancer, error) { 155 params := map[string]string{} 156 params["RegionId"] = region.RegionId 157 if ids != nil && len(ids) > 0 { 158 params["LoadBalancerId"] = strings.Join(ids, ",") 159 } 160 body, err := region.lbRequest("DescribeLoadBalancers", params) 161 if err != nil { 162 return nil, err 163 } 164 lbs := []SLoadbalancer{} 165 return lbs, body.Unmarshal(&lbs, "LoadBalancers", "LoadBalancer") 166 } 167 168 func (region *SRegion) GetLoadbalancerDetail(loadbalancerId string) (*SLoadbalancer, error) { 169 params := map[string]string{} 170 params["RegionId"] = region.RegionId 171 params["LoadBalancerId"] = loadbalancerId 172 body, err := region.lbRequest("DescribeLoadBalancerAttribute", params) 173 if err != nil { 174 return nil, err 175 } 176 lb := SLoadbalancer{region: region} 177 return &lb, body.Unmarshal(&lb) 178 } 179 180 func (lb *SLoadbalancer) Delete(ctx context.Context) error { 181 params := map[string]string{} 182 params["RegionId"] = lb.region.RegionId 183 params["LoadBalancerId"] = lb.LoadBalancerId 184 _, err := lb.region.lbRequest("DeleteLoadBalancer", params) 185 return err 186 } 187 188 func (lb *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { 189 ibackendgroups := []cloudprovider.ICloudLoadbalancerBackendGroup{} 190 { 191 backendgroups, err := lb.region.GetLoadbalancerBackendgroups(lb.LoadBalancerId) 192 if err != nil { 193 return nil, err 194 } 195 196 for i := 0; i < len(backendgroups); i++ { 197 backendgroups[i].lb = lb 198 ibackendgroups = append(ibackendgroups, &backendgroups[i]) 199 } 200 } 201 { 202 iDefaultBackendgroup := SLoadbalancerDefaultBackendGroup{lb: lb} 203 ibackendgroups = append(ibackendgroups, &iDefaultBackendgroup) 204 205 } 206 { 207 backendgroups, err := lb.region.GetLoadbalancerMasterSlaveBackendgroups(lb.LoadBalancerId) 208 if err != nil { 209 return nil, err 210 } 211 for i := 0; i < len(backendgroups); i++ { 212 backendgroups[i].lb = lb 213 ibackendgroups = append(ibackendgroups, &backendgroups[i]) 214 } 215 } 216 return ibackendgroups, nil 217 } 218 219 func (lb *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { 220 switch group.GroupType { 221 case api.LB_BACKENDGROUP_TYPE_NORMAL: 222 group, err := lb.region.CreateLoadbalancerBackendGroup(group.Name, lb.LoadBalancerId, group.Backends) 223 if err != nil { 224 return nil, err 225 } 226 group.lb = lb 227 return group, nil 228 case api.LB_BACKENDGROUP_TYPE_MASTER_SLAVE: 229 group, err := lb.region.CreateLoadbalancerMasterSlaveBackendGroup(group.Name, lb.LoadBalancerId, group.Backends) 230 if err != nil { 231 return nil, err 232 } 233 group.lb = lb 234 return group, nil 235 default: 236 return nil, fmt.Errorf("Unsupport backendgroup type %s", group.GroupType) 237 } 238 } 239 240 func (lb *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) { 241 switch listener.ListenerType { 242 case api.LB_LISTENER_TYPE_TCP: 243 return lb.region.CreateLoadbalancerTCPListener(lb, listener) 244 case api.LB_LISTENER_TYPE_UDP: 245 return lb.region.CreateLoadbalancerUDPListener(lb, listener) 246 case api.LB_LISTENER_TYPE_HTTP: 247 return lb.region.CreateLoadbalancerHTTPListener(lb, listener) 248 case api.LB_LISTENER_TYPE_HTTPS: 249 return lb.region.CreateLoadbalancerHTTPSListener(lb, listener) 250 } 251 return nil, fmt.Errorf("unsupport listener type %s", listener.ListenerType) 252 } 253 254 func (lb *SLoadbalancer) GetLoadbalancerSpec() string { 255 if len(lb.LoadBalancerSpec) == 0 { 256 lb.Refresh() 257 } 258 return lb.LoadBalancerSpec 259 } 260 261 func (lb *SLoadbalancer) GetChargeType() string { 262 chargeType := lb.InternetChargeType 263 if len(lb.InternetChargeTypeAlias) > 0 { 264 chargeType = lb.InternetChargeTypeAlias 265 } 266 switch chargeType { 267 case "paybybandwidth": 268 return api.LB_CHARGE_TYPE_BY_BANDWIDTH 269 case "paybytraffic": 270 return api.LB_CHARGE_TYPE_BY_TRAFFIC 271 default: 272 return string(chargeType) 273 } 274 } 275 276 func (lb *SLoadbalancer) GetCreatedAt() time.Time { 277 return lb.CreateTime 278 } 279 280 func (lb *SLoadbalancer) GetEgressMbps() int { 281 if lb.Bandwidth < 1 { 282 return 0 283 } 284 return lb.Bandwidth 285 } 286 287 func (lb *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { 288 groups, err := lb.GetILoadBalancerBackendGroups() 289 if err != nil { 290 return nil, err 291 } 292 for i := 0; i < len(groups); i++ { 293 if groups[i].GetGlobalId() == groupId { 294 return groups[i], nil 295 } 296 } 297 return nil, cloudprovider.ErrNotFound 298 } 299 300 func (lb *SLoadbalancer) GetIEIP() (cloudprovider.ICloudEIP, error) { 301 if lb.AddressType == "internet" { 302 eip := SEipAddress{ 303 region: lb.region, 304 IpAddress: lb.Address, 305 InstanceId: lb.GetGlobalId(), 306 InstanceType: EIP_INTANNCE_TYPE_SLB, 307 Status: EIP_STATUS_INUSE, 308 AllocationId: lb.GetGlobalId(), 309 AllocationTime: lb.CreateTime, 310 Bandwidth: lb.Bandwidth, 311 } 312 switch lb.GetChargeType() { 313 case api.LB_CHARGE_TYPE_BY_BANDWIDTH: 314 eip.InternetChargeType = InternetChargeByBandwidth 315 case api.LB_CHARGE_TYPE_BY_TRAFFIC: 316 eip.InternetChargeType = InternetChargeByTraffic 317 } 318 return &eip, nil 319 } 320 eips, total, err := lb.region.GetEips("", lb.LoadBalancerId, "", 0, 1) 321 if err != nil { 322 return nil, errors.Wrapf(err, "lb.region.GetEips(%s)", lb.LoadBalancerId) 323 } 324 if total != 1 { 325 return nil, cloudprovider.ErrNotFound 326 } 327 eips[0].region = lb.region 328 return &eips[0], nil 329 } 330 331 func (region *SRegion) loadbalancerOperation(loadbalancerId, status string) error { 332 params := map[string]string{} 333 params["RegionId"] = region.RegionId 334 params["LoadBalancerId"] = loadbalancerId 335 params["LoadBalancerStatus"] = status 336 _, err := region.lbRequest("SetLoadBalancerStatus", params) 337 return err 338 } 339 340 func (lb *SLoadbalancer) Start() error { 341 if lb.LoadBalancerStatus != "active" { 342 return lb.region.loadbalancerOperation(lb.LoadBalancerId, "active") 343 } 344 return nil 345 } 346 347 func (lb *SLoadbalancer) Stop() error { 348 if lb.LoadBalancerStatus != "inactive" { 349 return lb.region.loadbalancerOperation(lb.LoadBalancerId, "inactive") 350 } 351 return nil 352 } 353 354 func (lb *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) { 355 listener, err := lb.GetILoadBalancerListeners() 356 if err != nil { 357 return nil, err 358 } 359 for i := 0; i < len(listener); i++ { 360 if listener[i].GetGlobalId() == listenerId { 361 return listener[i], nil 362 } 363 } 364 return nil, cloudprovider.ErrNotFound 365 } 366 367 func (lb *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) { 368 loadbalancer, err := lb.region.GetLoadbalancerDetail(lb.LoadBalancerId) 369 if err != nil { 370 return nil, err 371 } 372 listeners := []cloudprovider.ICloudLoadbalancerListener{} 373 for _, listenerInfo := range loadbalancer.ListenerPortsAndProtocol.ListenerPortAndProtocol { 374 switch listenerInfo.ListenerProtocol { 375 case ListenerProtocolHTTP: 376 listener, err := lb.region.GetLoadbalancerHTTPListener(lb.LoadBalancerId, listenerInfo.ListenerPort) 377 if err != nil { 378 return nil, err 379 } 380 listener.lb = lb 381 listeners = append(listeners, listener) 382 case ListenerProtocolHTTPS: 383 listener, err := lb.region.GetLoadbalancerHTTPSListener(lb.LoadBalancerId, listenerInfo.ListenerPort) 384 if err != nil { 385 return nil, err 386 } 387 listener.lb = lb 388 listeners = append(listeners, listener) 389 case ListenerProtocolTCP: 390 listener, err := lb.region.GetLoadbalancerTCPListener(lb.LoadBalancerId, listenerInfo.ListenerPort) 391 if err != nil { 392 return nil, err 393 } 394 listener.lb = lb 395 listeners = append(listeners, listener) 396 case ListenerProtocolUDP: 397 listener, err := lb.region.GetLoadbalancerUDPListener(lb.LoadBalancerId, listenerInfo.ListenerPort) 398 if err != nil { 399 return nil, err 400 } 401 listener.lb = lb 402 listeners = append(listeners, listener) 403 default: 404 return nil, fmt.Errorf("failed to recognize %s type listener", listenerInfo.ListenerProtocol) 405 } 406 } 407 return listeners, nil 408 } 409 410 func (lb *SLoadbalancer) GetProjectId() string { 411 return lb.ResourceGroupId 412 } 413 414 func (lb *SLoadbalancer) SetTags(tags map[string]string, replace bool) error { 415 return lb.region.SetResourceTags(ALIYUN_SERVICE_SLB, "instance", lb.LoadBalancerId, tags, replace) 416 } 417 418 // mapping aliyun finance zoneId to aliyun finance ecs zoneId 419 func transZoneIdToEcsZoneId(region *SRegion, service, zoneId string) string { 420 if region.GetCloudEnv() == ALIYUN_FINANCE_CLOUDENV { 421 switch service { 422 case "elb", "redis": 423 if utils.IsInStringArray(zoneId, []string{"cn-hangzhou-finance-b", "cn-hangzhou-finance-c", "cn-hangzhou-finance-d"}) { 424 return strings.Replace(zoneId, "-finance", "", -1) 425 } 426 default: 427 return zoneId 428 } 429 } 430 431 return zoneId 432 } 433 434 // mapping aliyun finance ecs zoneId to dest service zone id 435 func transZoneIdFromEcsZoneId(region *SRegion, service, zoneId string) string { 436 if region.GetCloudEnv() == ALIYUN_FINANCE_CLOUDENV { 437 switch service { 438 case "elb", "redis": 439 if utils.IsInStringArray(zoneId, []string{"cn-hangzhou-b", "cn-hangzhou-c", "cn-hangzhou-d"}) { 440 return strings.Replace(zoneId, "cn-hangzhou", "cn-hangzhou-finance", -1) 441 } 442 default: 443 return zoneId 444 } 445 } 446 447 return zoneId 448 } 449 450 // mapping aliyun finance regionId to aliyun finance ecs regionId 451 func transRegionIdToEcsRegionId(region *SRegion, service string) string { 452 if region.GetCloudEnv() == ALIYUN_FINANCE_CLOUDENV { 453 switch service { 454 case "redis": 455 if region.GetId() == "cn-hangzhou-finance" { 456 return "cn-hangzhou" 457 } 458 default: 459 return region.GetId() 460 } 461 } 462 463 return region.GetId() 464 } 465 466 // mapping aliyun finance regionId from aliyun finance ecs regionId 467 func transRegionIdFromEcsRegionId(region *SRegion, service string) string { 468 if region.GetCloudEnv() == ALIYUN_FINANCE_CLOUDENV { 469 switch service { 470 case "redis": 471 if region.GetId() == "cn-hangzhou" { 472 return "cn-hangzhou-finance" 473 } 474 default: 475 return region.GetId() 476 } 477 } 478 479 return region.GetId() 480 } 481 482 func fetchMasterZoneId(zoneId string) string { 483 // cn-shenzhen-finance-1MAZ2(d,e) 484 i := strings.Index(zoneId, "MAZ") 485 if i > 0 { 486 s := strings.Index(zoneId, "(") 487 e := strings.Index(zoneId, ",") 488 if s >= 0 && e >= 0 { 489 return zoneId[0:i] + zoneId[s+1:e] 490 } 491 } 492 493 return zoneId 494 }