yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/azure/loadbalancerlistener.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 azure
    16  
    17  import (
    18  	"context"
    19  	"net/url"
    20  	"regexp"
    21  	"strconv"
    22  	"strings"
    23  
    24  	"yunion.io/x/jsonutils"
    25  	"yunion.io/x/log"
    26  	"yunion.io/x/pkg/errors"
    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  // todo: 目前不支持 入站 NAT 规则
    34  // todo: HTTP 设置(端口+协议,其余信息丢弃) + 后端池  = onecloud 路径的路由 的 onecloud 后端服务器组
    35  /*
    36  应用型LB: urlPathMaps(defaultBackendAddressPool+defaultBackendHttpSettings+requestRoutingRules+httpListeners)= Onecloud监听器
    37  4层LB: loadBalancingRules(前端) = Onecloud监听器
    38  */
    39  type SLoadBalancerListener struct {
    40  	multicloud.SResourceBase
    41  	multicloud.SLoadbalancerRedirectBase
    42  
    43  	lb   *SLoadbalancer
    44  	lbrs []cloudprovider.ICloudLoadbalancerListenerRule
    45  
    46  	/*
    47  		IPVersion    string
    48  		FrontendIP   string // 前端IP
    49  		FrontendIPId string // 前端IP ID
    50  	*/
    51  	fp *FrontendIPConfiguration
    52  
    53  	/*
    54  		    Protocol     string // 监听协议
    55  			HostName     string // 监听器 监听域名
    56  			FrontendPort int    // 前端端口
    57  	*/
    58  	listener *HTTPListener
    59  
    60  	/*
    61  		   Protocol     string // 后端协议
    62  		   BackendPort  int    // 后端端口
    63  			IdleTimeoutInMinutes      int    // 空闲超时(分钟)
    64  			LoadDistribution          string // 会话保持方法SourceIP|SourceIPProtocol
    65  			CookieBasedAffinity       string // cookies 关联
    66  			CookieName                string // cookie 名称
    67  			EnabledConnectionDraining bool   //   排出超时这应用于通过 API 调用从后端池明确删除的后端实例,以及运行状况探测报告的运行不正常的实例。
    68  			ConnectionDrainingSec     int    // 排出超时时长(秒)
    69  			RequestTimeout            int    // 请求超时时间(秒)
    70  		    probe                            // 健康检查
    71  	*/
    72  	backendSetting *BackendHTTPSettingsCollection
    73  
    74  	//
    75  	backendGroup *BackendAddressPool
    76  
    77  	/*
    78  			redirectType string // 重定向类型
    79  		    targetListener      // 重定向到监听
    80  	*/
    81  	redirect *RedirectConfiguration
    82  
    83  	/*
    84  	 "protocol": "Http",
    85  	 "host": "baidu.com",
    86  	 "path": "/test",
    87  	 "interval": 30,
    88  	 "timeout": 30,
    89  	 "unhealthyThreshold": 3,
    90  	 "pickHostNameFromBackendHttpSettings": false,
    91  	 "minServers": 0,
    92  	 "match": {
    93  	 	"body": "500",
    94  	 	"statusCodes": [
    95  	 		"200-399"
    96  	 	]
    97  	 },
    98  	*/
    99  	healthcheck *Probe
   100  
   101  	Name              string
   102  	ID                string
   103  	ProvisioningState string
   104  	IPVersion         string
   105  	Protocol          string // 监听协议
   106  	LoadDistribution  string // 调度算法
   107  	FrontendPort      int    // 前端端口
   108  	BackendPort       int    // 后端端口
   109  	ClientIdleTimeout int    // 客户端连接超时
   110  	EnableFloatingIP  bool   // 浮动 IP
   111  	EnableTcpReset    bool
   112  
   113  	rules []PathRule
   114  }
   115  
   116  func (self *SLoadBalancerListener) GetId() string {
   117  	return self.ID
   118  }
   119  
   120  func (self *SLoadBalancerListener) GetName() string {
   121  	return self.Name
   122  }
   123  
   124  func (self *SLoadBalancerListener) GetGlobalId() string {
   125  	return strings.ToLower(self.GetId())
   126  }
   127  
   128  func (self *SLoadBalancerListener) GetStatus() string {
   129  	switch self.ProvisioningState {
   130  	case "Succeeded", "Updating", "Deleting":
   131  		return api.LB_STATUS_ENABLED
   132  	case "Failed":
   133  		return api.LB_STATUS_DISABLED
   134  	default:
   135  		return api.LB_STATUS_UNKNOWN
   136  	}
   137  }
   138  
   139  func (self *SLoadBalancerListener) Refresh() error {
   140  	lbl, err := self.lb.GetILoadBalancerListenerById(self.GetId())
   141  	if err != nil {
   142  		return errors.Wrap(err, "GetILoadBalancerListenerById")
   143  	}
   144  
   145  	err = jsonutils.Update(self, lbl)
   146  	if err != nil {
   147  		return errors.Wrap(err, "refresh.Update")
   148  	}
   149  
   150  	self.lbrs = nil
   151  	return nil
   152  }
   153  
   154  func (self *SLoadBalancerListener) IsEmulated() bool {
   155  	return false
   156  }
   157  
   158  func (self *SLoadBalancerListener) GetSysTags() map[string]string {
   159  	return nil
   160  }
   161  
   162  func (self *SLoadBalancerListener) GetTags() (map[string]string, error) {
   163  	if self.fp != nil {
   164  		if self.fp.Properties.PublicIPAddress != nil && len(self.fp.Properties.PublicIPAddress.ID) > 0 {
   165  			eip, _ := self.lb.GetIEIPById(self.fp.Properties.PublicIPAddress.ID)
   166  			if eip != nil {
   167  				return map[string]string{"FrontendIP": eip.GetIpAddr()}, nil
   168  			}
   169  		}
   170  
   171  		if len(self.fp.Properties.PrivateIPAddress) > 0 {
   172  			return map[string]string{"FrontendIP": self.fp.Properties.PrivateIPAddress}, nil
   173  		}
   174  	}
   175  
   176  	return map[string]string{}, nil
   177  }
   178  
   179  func (self *SLoadBalancerListener) SetTags(tags map[string]string, replace bool) error {
   180  	return errors.Wrap(cloudprovider.ErrNotImplemented, "SetTags")
   181  }
   182  
   183  func (self *SLoadBalancerListener) GetProjectId() string {
   184  	return getResourceGroup(self.GetId())
   185  }
   186  
   187  func (self *SLoadBalancerListener) GetListenerType() string {
   188  	switch strings.ToLower(self.Protocol) {
   189  	case "tcp":
   190  		return api.LB_LISTENER_TYPE_TCP
   191  	case "udp":
   192  		return api.LB_LISTENER_TYPE_UDP
   193  	case "http":
   194  		return api.LB_LISTENER_TYPE_HTTP
   195  	case "https":
   196  		return api.LB_LISTENER_TYPE_HTTPS
   197  	default:
   198  		return ""
   199  	}
   200  }
   201  
   202  func (self *SLoadBalancerListener) GetListenerPort() int {
   203  	return int(self.FrontendPort)
   204  }
   205  
   206  func (self *SLoadBalancerListener) GetScheduler() string {
   207  	switch self.LoadDistribution {
   208  	case "SourceIPProtocol", "SourceIP":
   209  		return api.LB_SCHEDULER_SCH
   210  	default:
   211  		return ""
   212  	}
   213  }
   214  
   215  func (self *SLoadBalancerListener) GetAclStatus() string {
   216  	return api.LB_BOOL_OFF
   217  }
   218  
   219  func (self *SLoadBalancerListener) GetAclType() string {
   220  	return ""
   221  }
   222  
   223  func (self *SLoadBalancerListener) GetAclId() string {
   224  	return ""
   225  }
   226  
   227  func (self *SLoadBalancerListener) GetEgressMbps() int {
   228  	return 0
   229  }
   230  
   231  func (self *SLoadBalancerListener) GetHealthCheck() string {
   232  	// if self.probe != nil
   233  	if self.healthcheck != nil {
   234  		return api.LB_BOOL_ON
   235  	}
   236  
   237  	return api.LB_BOOL_OFF
   238  }
   239  
   240  func (self *SLoadBalancerListener) GetHealthCheckType() string {
   241  	if self.healthcheck == nil {
   242  		return ""
   243  	}
   244  	switch strings.ToLower(self.healthcheck.Properties.Protocol) {
   245  	case "tcp":
   246  		return api.LB_HEALTH_CHECK_TCP
   247  	case "udp":
   248  		return api.LB_HEALTH_CHECK_UDP
   249  	case "http":
   250  		return api.LB_HEALTH_CHECK_HTTP
   251  	case "https":
   252  		return api.LB_HEALTH_CHECK_HTTPS
   253  	default:
   254  		return ""
   255  	}
   256  }
   257  
   258  func (self *SLoadBalancerListener) GetHealthCheckTimeout() int {
   259  	if self.healthcheck == nil {
   260  		return 0
   261  	}
   262  	switch self.GetHealthCheckType() {
   263  	case api.LB_HEALTH_CHECK_HTTP, api.LB_HEALTH_CHECK_HTTPS:
   264  		return self.healthcheck.Properties.Timeout
   265  	}
   266  
   267  	return self.healthcheck.Properties.IntervalInSeconds
   268  }
   269  
   270  func (self *SLoadBalancerListener) GetHealthCheckInterval() int {
   271  	if self.healthcheck == nil {
   272  		return 0
   273  	}
   274  	switch self.GetHealthCheckType() {
   275  	case api.LB_HEALTH_CHECK_HTTP, api.LB_HEALTH_CHECK_HTTPS:
   276  		if self.healthcheck.Properties.Interval > 0 {
   277  			return self.healthcheck.Properties.Interval
   278  		}
   279  	}
   280  
   281  	return self.healthcheck.Properties.IntervalInSeconds
   282  }
   283  
   284  func (self *SLoadBalancerListener) GetHealthCheckRise() int {
   285  	return 0
   286  }
   287  
   288  func (self *SLoadBalancerListener) GetHealthCheckFail() int {
   289  	if self.healthcheck == nil {
   290  		return 0
   291  	}
   292  	switch self.GetHealthCheckType() {
   293  	case api.LB_HEALTH_CHECK_HTTP, api.LB_HEALTH_CHECK_HTTPS:
   294  		if self.healthcheck.Properties.UnhealthyThreshold > 0 {
   295  			return self.healthcheck.Properties.UnhealthyThreshold
   296  		}
   297  	}
   298  
   299  	return self.healthcheck.Properties.NumberOfProbes
   300  }
   301  
   302  func (self *SLoadBalancerListener) GetHealthCheckReq() string {
   303  	return ""
   304  }
   305  
   306  func (self *SLoadBalancerListener) GetHealthCheckExp() string {
   307  	return ""
   308  }
   309  
   310  func (self *SLoadBalancerListener) GetBackendGroupId() string {
   311  	if self.backendGroup != nil {
   312  		return self.backendGroup.ID + "::" + strconv.Itoa(self.BackendPort)
   313  	}
   314  
   315  	return ""
   316  }
   317  
   318  func (self *SLoadBalancerListener) GetBackendServerPort() int {
   319  	return self.BackendPort
   320  }
   321  
   322  func (self *SLoadBalancerListener) GetHealthCheckDomain() string {
   323  	if self.healthcheck == nil {
   324  		return ""
   325  	}
   326  	switch self.GetHealthCheckType() {
   327  	case api.LB_HEALTH_CHECK_HTTP, api.LB_HEALTH_CHECK_HTTPS:
   328  		return self.healthcheck.Properties.Host
   329  	}
   330  
   331  	return ""
   332  }
   333  
   334  func (self *SLoadBalancerListener) GetHealthCheckURI() string {
   335  	if self.healthcheck == nil {
   336  		return ""
   337  	}
   338  	switch self.GetHealthCheckType() {
   339  	case api.LB_HEALTH_CHECK_HTTP, api.LB_HEALTH_CHECK_HTTPS:
   340  		return self.healthcheck.Properties.Path
   341  	}
   342  
   343  	return ""
   344  }
   345  
   346  /*
   347  todo: 和onecloud code不兼容?
   348  与此处输入的 HTTP 状态代码或代码范围相匹配的响应将被视为成功。请输入逗号分隔的代码列表(例如 200, 201)或者输入一个代码范围(例如 220-226)
   349  */
   350  func (self *SLoadBalancerListener) GetHealthCheckCode() string {
   351  	return ""
   352  }
   353  
   354  func (self *SLoadBalancerListener) CreateILoadBalancerListenerRule(rule *cloudprovider.SLoadbalancerListenerRule) (cloudprovider.ICloudLoadbalancerListenerRule, error) {
   355  	return nil, errors.Wrap(cloudprovider.ErrNotImplemented, "CreateILoadBalancerListenerRule")
   356  }
   357  
   358  func (self *SLoadBalancerListener) GetILoadBalancerListenerRuleById(ruleId string) (cloudprovider.ICloudLoadbalancerListenerRule, error) {
   359  	lbrs, err := self.GetILoadbalancerListenerRules()
   360  	if err != nil {
   361  		return nil, errors.Wrap(err, "GetILoadbalancerListenerRules")
   362  	}
   363  
   364  	for i := range lbrs {
   365  		if lbrs[i].GetId() == ruleId {
   366  			return lbrs[i], nil
   367  		}
   368  	}
   369  
   370  	return nil, errors.Wrap(err, "GetILoadBalancerListenerRuleById")
   371  }
   372  
   373  func (self *SLoadBalancerListener) GetILoadbalancerListenerRules() ([]cloudprovider.ICloudLoadbalancerListenerRule, error) {
   374  	if self.lbrs != nil {
   375  		return self.lbrs, nil
   376  	}
   377  
   378  	irules := []cloudprovider.ICloudLoadbalancerListenerRule{}
   379  	for i := range self.rules {
   380  		r := self.rules[i]
   381  		var redirect *RedirectConfiguration
   382  		var lbbg *SLoadbalancerBackendGroup
   383  		if r.Properties.RedirectConfiguration != nil {
   384  			redirect = self.lb.getRedirectConfiguration(r.Properties.RedirectConfiguration.ID)
   385  		} else {
   386  			if r.Properties.BackendAddressPool == nil || r.Properties.BackendHTTPSettings == nil {
   387  				continue
   388  			}
   389  
   390  			pool := self.lb.getBackendAddressPool(r.Properties.BackendAddressPool.ID)
   391  			if pool == nil {
   392  				log.Debugf("getBackendAddressPool %s not found", r.Properties.BackendAddressPool.ID)
   393  				continue
   394  			}
   395  			backsetting := self.lb.getBackendHTTPSettingsCollection(r.Properties.BackendHTTPSettings.ID)
   396  			if backsetting == nil {
   397  				log.Debugf("getBackendHTTPSettingsCollection %s not found", r.Properties.BackendHTTPSettings.ID)
   398  				continue
   399  			}
   400  
   401  			lbbg = &SLoadbalancerBackendGroup{
   402  				lb:           self.lb,
   403  				Pool:         *pool,
   404  				DefaultPort:  backsetting.Properties.Port,
   405  				HttpSettings: backsetting,
   406  				BackendIps:   pool.Properties.BackendIPConfigurations,
   407  			}
   408  		}
   409  
   410  		domain := ""
   411  		if self.listener != nil {
   412  			domain = self.listener.Properties.HostName
   413  		}
   414  
   415  		rule := SLoadbalancerListenerRule{
   416  			SResourceBase: multicloud.SResourceBase{},
   417  			listener:      self,
   418  			lbbg:          lbbg,
   419  			redirect:      redirect,
   420  			Name:          r.Name,
   421  			ID:            r.ID,
   422  			Domain:        domain,
   423  			Properties:    r.Properties,
   424  		}
   425  		irules = append(irules, &rule)
   426  	}
   427  
   428  	return irules, nil
   429  }
   430  
   431  func (self *SLoadBalancerListener) GetStickySession() string {
   432  	if self.backendSetting == nil {
   433  		return api.LB_BOOL_OFF
   434  	}
   435  
   436  	if self.backendSetting.Properties.CookieBasedAffinity == "Enabled" {
   437  		return api.LB_BOOL_ON
   438  	} else {
   439  		return api.LB_BOOL_OFF
   440  	}
   441  }
   442  
   443  func (self *SLoadBalancerListener) GetStickySessionType() string {
   444  	if self.GetStickySession() == api.LB_BOOL_ON {
   445  		return api.LB_STICKY_SESSION_TYPE_INSERT
   446  	}
   447  	return ""
   448  }
   449  
   450  func (self *SLoadBalancerListener) GetStickySessionCookie() string {
   451  	if self.backendSetting == nil {
   452  		return ""
   453  	}
   454  
   455  	return self.backendSetting.Properties.AffinityCookieName
   456  }
   457  
   458  func (self *SLoadBalancerListener) GetStickySessionCookieTimeout() int {
   459  	if self.backendSetting == nil {
   460  		return 0
   461  	}
   462  
   463  	if self.backendSetting.Properties.ConnectionDraining.Enabled {
   464  		_sec := strings.TrimSpace(self.backendSetting.Properties.ConnectionDraining.DrainTimeoutInSEC)
   465  		sec, _ := strconv.ParseInt(_sec, 10, 64)
   466  		return int(sec)
   467  	}
   468  
   469  	return 0
   470  }
   471  
   472  func (self *SLoadBalancerListener) XForwardedForEnabled() bool {
   473  	return false
   474  }
   475  
   476  func (self *SLoadBalancerListener) GzipEnabled() bool {
   477  	return false
   478  }
   479  
   480  func (self *SLoadBalancerListener) GetCertificateId() string {
   481  	if self.listener != nil {
   482  		return self.listener.Properties.SSLCertificate.ID
   483  	}
   484  
   485  	return ""
   486  }
   487  
   488  func (self *SLoadBalancerListener) GetTLSCipherPolicy() string {
   489  	return ""
   490  }
   491  
   492  func (self *SLoadBalancerListener) HTTP2Enabled() bool {
   493  	return self.lb.Properties.EnableHttp2
   494  }
   495  
   496  func (self *SLoadBalancerListener) Start() error {
   497  	return errors.Wrap(cloudprovider.ErrNotImplemented, "Start")
   498  }
   499  
   500  func (self *SLoadBalancerListener) Stop() error {
   501  	return errors.Wrap(cloudprovider.ErrNotImplemented, "Stop")
   502  }
   503  
   504  func (self *SLoadBalancerListener) Sync(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) error {
   505  	return errors.Wrap(cloudprovider.ErrNotImplemented, "Sync")
   506  }
   507  
   508  func (self *SLoadBalancerListener) Delete(ctx context.Context) error {
   509  	return errors.Wrap(cloudprovider.ErrNotImplemented, "Delete")
   510  }
   511  
   512  func (self *SLoadBalancerListener) GetRedirect() string {
   513  	if self.redirect != nil {
   514  		return api.LB_REDIRECT_RAW
   515  	}
   516  
   517  	return api.LB_REDIRECT_OFF
   518  }
   519  
   520  func (self *SLoadBalancerListener) GetRedirectCode() int64 {
   521  	if self.redirect == nil {
   522  		return 0
   523  	}
   524  
   525  	switch self.redirect.Properties.RedirectType {
   526  	case "Permanent":
   527  		return api.LB_REDIRECT_CODE_301
   528  	case "Found":
   529  		return api.LB_REDIRECT_CODE_302
   530  	case "Temporary", "SeeOther":
   531  		return api.LB_REDIRECT_CODE_307
   532  	default:
   533  		return 0
   534  	}
   535  }
   536  
   537  func (self *SLoadBalancerListener) getRedirectUrl() *url.URL {
   538  	if self.redirect == nil {
   539  		return nil
   540  	}
   541  
   542  	if len(self.redirect.Properties.TargetUrl) == 0 {
   543  		return nil
   544  	}
   545  
   546  	_url := self.redirect.Properties.TargetUrl
   547  	if matched, _ := regexp.MatchString("^\\w{0,5}://", _url); !matched {
   548  		_url = "http://" + _url
   549  	}
   550  
   551  	u, err := url.Parse(_url)
   552  	if err != nil {
   553  		log.Debugf("url Parse %s : %s", self.redirect.Properties.TargetUrl, err)
   554  		return nil
   555  	}
   556  
   557  	return u
   558  }
   559  func (self *SLoadBalancerListener) GetRedirectScheme() string {
   560  	u := self.getRedirectUrl()
   561  	if u == nil {
   562  		return ""
   563  	}
   564  
   565  	return strings.ToLower(u.Scheme)
   566  }
   567  
   568  func (self *SLoadBalancerListener) GetRedirectHost() string {
   569  	u := self.getRedirectUrl()
   570  	if u == nil {
   571  		if self.redirect != nil && len(self.redirect.Properties.TargetListener.ID) > 0 {
   572  			segs := strings.Split(self.redirect.Properties.TargetListener.ID, "/")
   573  			return segs[len(segs)-1]
   574  		}
   575  		return ""
   576  	}
   577  
   578  	return u.Host
   579  }
   580  
   581  func (self *SLoadBalancerListener) GetRedirectPath() string {
   582  	u := self.getRedirectUrl()
   583  	if u == nil {
   584  		return ""
   585  	}
   586  
   587  	return u.Path
   588  }
   589  
   590  func (self *SLoadBalancerListener) GetClientIdleTimeout() int {
   591  	return self.ClientIdleTimeout
   592  }
   593  
   594  func (self *SLoadBalancerListener) GetBackendConnectTimeout() int {
   595  	if self.backendSetting == nil {
   596  		return 0
   597  	}
   598  
   599  	return self.backendSetting.Properties.RequestTimeout
   600  }