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  }