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  }