yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/openstack/loadbalbacer.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  // Copyright 2019 Yunion
    16  //
    17  // Licensed under the Apache License, Version 2.0 (the "License");
    18  // you may not use this file except in compliance with the License.
    19  // You may obtain a copy of the License at
    20  //
    21  //     http:// www.apache.org/licenses/LICENSE-2.0
    22  //
    23  // Unless required by applicable law or agreed to in writing, software
    24  // distributed under the License is distributed on an "AS IS" BASIS,
    25  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    26  // See the License for the specific language governing permissions and
    27  // limitations under the License.
    28  
    29  package openstack
    30  
    31  import (
    32  	"context"
    33  	"fmt"
    34  	"net/url"
    35  	"time"
    36  
    37  	"github.com/coredns/coredns/plugin/pkg/log"
    38  
    39  	"yunion.io/x/jsonutils"
    40  	"yunion.io/x/pkg/errors"
    41  
    42  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    43  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    44  	"yunion.io/x/cloudmux/pkg/multicloud"
    45  )
    46  
    47  var LB_ALGORITHM_MAP = map[string]string{
    48  	api.LB_SCHEDULER_RR:  "ROUND_ROBIN",
    49  	api.LB_SCHEDULER_WRR: "ROUND_ROBIN",
    50  	api.LB_SCHEDULER_WLC: "LEAST_CONNECTIONS",
    51  	api.LB_SCHEDULER_SCH: "SOURCE_IP",
    52  	api.LB_SCHEDULER_TCH: "SOURCE_IP_PORT",
    53  }
    54  
    55  var LB_PROTOCOL_MAP = map[string]string{
    56  	api.LB_LISTENER_TYPE_HTTP:             "HTTP",
    57  	api.LB_LISTENER_TYPE_HTTPS:            "HTTPS",
    58  	api.LB_LISTENER_TYPE_TERMINATED_HTTPS: "TERMINATED_HTTPS",
    59  	api.LB_LISTENER_TYPE_UDP:              "UDP",
    60  	api.LB_LISTENER_TYPE_TCP:              "TCP",
    61  }
    62  
    63  var LB_STICKY_SESSION_MAP = map[string]string{
    64  	api.LB_STICKY_SESSION_TYPE_INSERT: "HTTP_COOKIE",
    65  	api.LB_STICKY_SESSION_TYPE_SERVER: "APP_COOKIE",
    66  }
    67  
    68  var LB_HEALTHCHECK_TYPE_MAP = map[string]string{
    69  	api.LB_HEALTH_CHECK_HTTP:  "HTTP",
    70  	api.LB_HEALTH_CHECK_HTTPS: "HTTPS",
    71  	api.LB_HEALTH_CHECK_TCP:   "TCP",
    72  	api.LB_HEALTH_CHECK_UDP:   "UDP_CONNECT",
    73  }
    74  
    75  type SLoadbalancerCreateParams struct {
    76  	Description      string   `json:"description,omitempty"`
    77  	AdminStateUp     bool     `json:"admin_state_up,omitempty"`
    78  	ProjectID        string   `json:"project_id,omitempty"`
    79  	VipNetworkId     string   `json:"vip_network_id,omitempty"`
    80  	VipSubnetID      string   `json:"vip_subnet_id,omitempty"`
    81  	VipAddress       string   `json:"vip_address,omitempty"`
    82  	Provider         string   `json:"provider,omitempty"`
    83  	Name             string   `json:"name,omitempty"`
    84  	VipQosPolicyID   string   `json:"vip_qos_policy_id,omitempty"`
    85  	AvailabilityZone string   `json:"availability_zone,omitempty"`
    86  	Tags             []string `json:"tags,omitempty"`
    87  }
    88  
    89  type SLoadbalancerID struct {
    90  	ID string `json:"id"`
    91  }
    92  
    93  type SPoolID struct {
    94  	ID string `json:"id"`
    95  }
    96  
    97  type SMemberID struct {
    98  	ID string `json:"id"`
    99  }
   100  
   101  type SListenerID struct {
   102  	ID string `json:"id"`
   103  }
   104  
   105  type SL7PolicieID struct {
   106  	ID string `json:"id"`
   107  }
   108  
   109  type SL7RuleID struct {
   110  	ID string `json:"id"`
   111  }
   112  
   113  type SLoadbalancer struct {
   114  	multicloud.SLoadbalancerBase
   115  	OpenStackTags
   116  	region *SRegion
   117  
   118  	Description        string        `json:"description"`
   119  	AdminStateUp       bool          `json:"admin_state_up"`
   120  	ProjectID          string        `json:"project_id"`
   121  	ProvisioningStatus string        `json:"provisioning_status"`
   122  	FlavorID           string        `json:"flavor_id"`
   123  	VipSubnetID        string        `json:"vip_subnet_id"`
   124  	ListenerIds        []SListenerID `json:"listeners"`
   125  	VipAddress         string        `json:"vip_address"`
   126  	VipNetworkID       string        `json:"vip_network_id"`
   127  	VipPortID          string        `json:"vip_port_id"`
   128  	Provider           string        `json:"provider"`
   129  	PoolIds            []SPoolID     `json:"pools"`
   130  	CreatedAt          string        `json:"created_at"`
   131  	UpdatedAt          string        `json:"updated_at"`
   132  	ID                 string        `json:"id"`
   133  	OperatingStatus    string        `json:"operating_status"`
   134  	Name               string        `json:"name"`
   135  	VipQosPolicyID     string        `json:"vip_qos_policy_id"`
   136  	AvailabilityZone   string        `json:"availability_zone"`
   137  	Tags               []string      `json:"tags"`
   138  }
   139  
   140  func waitLbResStatus(res cloudprovider.ICloudResource, interval time.Duration, timeout time.Duration) error {
   141  	err := cloudprovider.WaitMultiStatus(res, []string{api.LB_STATUS_ENABLED, api.LB_STATUS_UNKNOWN}, interval, timeout)
   142  	if err != nil {
   143  		return errors.Wrap(err, "waitLbResStatus(res, interval, timeout)")
   144  	}
   145  	if res.GetStatus() == api.LB_STATUS_UNKNOWN {
   146  		return errors.Wrap(fmt.Errorf("status error"), "check status")
   147  	}
   148  	return nil
   149  }
   150  
   151  func (lb *SLoadbalancer) GetName() string {
   152  	return lb.Name
   153  }
   154  
   155  func (lb *SLoadbalancer) GetId() string {
   156  	return lb.ID
   157  }
   158  
   159  func (lb *SLoadbalancer) GetGlobalId() string {
   160  	return lb.ID
   161  }
   162  
   163  func (lb *SLoadbalancer) GetStatus() string {
   164  	switch lb.ProvisioningStatus {
   165  	case "ACTIVE":
   166  		return api.LB_STATUS_ENABLED
   167  	case "PENDING_CREATE":
   168  		return api.LB_CREATING
   169  	case "PENDING_UPDATE":
   170  		return api.LB_SYNC_CONF
   171  	case "PENDING_DELETE":
   172  		return api.LB_STATUS_DELETING
   173  	case "DELETED":
   174  		return api.LB_STATUS_DELETED
   175  	default:
   176  		return api.LB_STATUS_UNKNOWN
   177  	}
   178  }
   179  
   180  func (lb *SLoadbalancer) GetAddress() string {
   181  	return lb.VipAddress
   182  }
   183  
   184  func (lb *SLoadbalancer) GetAddressType() string {
   185  	eip, err := lb.GetIEIP()
   186  	if err != nil {
   187  		return api.LB_ADDR_TYPE_INTRANET
   188  	}
   189  	if eip == nil {
   190  		return api.LB_ADDR_TYPE_INTRANET
   191  	}
   192  	return api.LB_ADDR_TYPE_INTERNET
   193  }
   194  
   195  func (lb *SLoadbalancer) GetNetworkType() string {
   196  	network, err := lb.region.GetVpc(lb.VipNetworkID)
   197  	if err != nil {
   198  		log.Error(errors.Wrapf(err, "lb.region.GetNetwork(%s)", lb.VipNetworkID))
   199  	}
   200  	if network.NetworkType == "flat" || network.NetworkType == "vlan" {
   201  		return api.LB_NETWORK_TYPE_CLASSIC
   202  	}
   203  	return api.LB_NETWORK_TYPE_VPC
   204  }
   205  
   206  func (lb *SLoadbalancer) GetNetworkIds() []string {
   207  	return []string{lb.VipSubnetID}
   208  }
   209  
   210  func (lb *SLoadbalancer) GetZoneId() string {
   211  	return lb.AvailabilityZone
   212  }
   213  
   214  func (self *SLoadbalancer) GetZone1Id() string {
   215  	return ""
   216  }
   217  
   218  func (lb *SLoadbalancer) IsEmulated() bool {
   219  	return false
   220  }
   221  
   222  func (lb *SLoadbalancer) GetVpcId() string {
   223  	return lb.VipNetworkID
   224  }
   225  
   226  func (lb *SLoadbalancer) Refresh() error {
   227  	loadbalancer, err := lb.region.GetLoadbalancerbyId(lb.ID)
   228  	if err != nil {
   229  		return err
   230  	}
   231  	return jsonutils.Update(lb, loadbalancer)
   232  }
   233  
   234  func (region *SRegion) GetLoadbalancers() ([]SLoadbalancer, error) {
   235  	loadbalancers := []SLoadbalancer{}
   236  	resource := "/v2/lbaas/loadbalancers"
   237  	query := url.Values{}
   238  	for {
   239  		resp, err := region.lbList(resource, query)
   240  		if err != nil {
   241  			return nil, errors.Wrap(err, "lbList")
   242  		}
   243  		part := struct {
   244  			Loadbalancers      []SLoadbalancer
   245  			LoadbalancersLinks SNextLinks
   246  		}{}
   247  		err = resp.Unmarshal(&part)
   248  		if err != nil {
   249  			return nil, errors.Wrap(err, "resp.Unmarshal")
   250  		}
   251  		loadbalancers = append(loadbalancers, part.Loadbalancers...)
   252  		marker := part.LoadbalancersLinks.GetNextMark()
   253  		if len(marker) == 0 {
   254  			break
   255  		}
   256  		query.Set("marker", marker)
   257  	}
   258  	for i := 0; i < len(loadbalancers); i++ {
   259  		loadbalancers[i].region = region
   260  	}
   261  	return loadbalancers, nil
   262  }
   263  
   264  func (region *SRegion) GetLoadbalancerbyId(loadbalancerId string) (*SLoadbalancer, error) {
   265  	// region.client.Debug(true)
   266  	body, err := region.lbGet(fmt.Sprintf("/v2/lbaas/loadbalancers/%s", loadbalancerId))
   267  	if err != nil {
   268  		return nil, errors.Wrapf(err, `region.lbGet(/v2/lbaas/loadbalancers/%s)`, loadbalancerId)
   269  	}
   270  	loadbalancer := SLoadbalancer{}
   271  	err = body.Unmarshal(&loadbalancer, "loadbalancer")
   272  	if err != nil {
   273  		return nil, errors.Wrap(err, "resp.Unmarshal(loadbalancer)")
   274  	}
   275  	loadbalancer.region = region
   276  	return &loadbalancer, nil
   277  }
   278  
   279  func (region *SRegion) CreateLoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (*SLoadbalancer, error) {
   280  	type CreateParams struct {
   281  		Loadbalancer SLoadbalancerCreateParams `json:"loadbalancer"`
   282  	}
   283  	params := CreateParams{}
   284  	params.Loadbalancer.AdminStateUp = true
   285  	params.Loadbalancer.AvailabilityZone = loadbalancer.ZoneID
   286  	params.Loadbalancer.Name = loadbalancer.Name
   287  	params.Loadbalancer.ProjectID = loadbalancer.ProjectId
   288  	params.Loadbalancer.VipSubnetID = loadbalancer.NetworkIDs[0]
   289  	params.Loadbalancer.VipAddress = loadbalancer.Address
   290  
   291  	body, err := region.lbPost("/v2/lbaas/loadbalancers", jsonutils.Marshal(params))
   292  	if err != nil {
   293  		return nil, errors.Wrap(err, `region.lbPost("/v2/lbaas/loadbalancers", jsonutils.Marshal(params))`)
   294  	}
   295  	sloadbalancer := SLoadbalancer{}
   296  	err = body.Unmarshal(&sloadbalancer, "loadbalancer")
   297  	if err != nil {
   298  		return nil, errors.Wrap(err, "body.Unmarshal(sloadbalancer, loadbalancer)")
   299  	}
   300  	sloadbalancer.region = region
   301  	if len(loadbalancer.EipID) > 0 {
   302  		err = region.AssociateEipWithPortId(sloadbalancer.VipPortID, loadbalancer.EipID)
   303  		if err != nil {
   304  			return nil, errors.Wrapf(err, "region.AssociateEipWithPortId(%s, %s)", sloadbalancer.VipPortID, loadbalancer.EipID)
   305  		}
   306  	}
   307  	return &sloadbalancer, nil
   308  }
   309  
   310  func (region *SRegion) DeleteLoadbalancer(loadbalancerId string) error {
   311  	_, err := region.lbDelete(fmt.Sprintf("/v2/lbaas/loadbalancers/%s?cascade=True", loadbalancerId))
   312  	if err != nil {
   313  		return errors.Wrapf(err, `region.lbDelete(/v2/lbaas/loadbalancers/%s?cascade=True)`, loadbalancerId)
   314  	}
   315  	return nil
   316  }
   317  
   318  func (lb *SLoadbalancer) Delete(ctx context.Context) error {
   319  	return lb.region.DeleteLoadbalancer(lb.ID)
   320  }
   321  
   322  func (lb *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) {
   323  	ibackendgroups := []cloudprovider.ICloudLoadbalancerBackendGroup{}
   324  	for i := 0; i < len(lb.PoolIds); i++ {
   325  		pool, err := lb.region.GetLoadbalancerPoolById(lb.PoolIds[i].ID)
   326  		if err != nil {
   327  			return nil, errors.Wrapf(err, "lb.region.GetLoadbalancerPoolById(%s)", lb.PoolIds[i].ID)
   328  		}
   329  		ibackendgroups = append(ibackendgroups, pool)
   330  	}
   331  	return ibackendgroups, nil
   332  }
   333  
   334  func (lb *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
   335  	// ensure lb status
   336  	err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
   337  	if err != nil {
   338  		return nil, errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
   339  	}
   340  	// create pool
   341  	spool, err := lb.region.CreateLoadbalancerPool(group)
   342  	if err != nil {
   343  		return nil, errors.Wrap(err, "lb.region.CreateLoadbalancerPool")
   344  	}
   345  	// wait spool
   346  	err = waitLbResStatus(spool, 10*time.Second, 8*time.Minute)
   347  	if err != nil {
   348  		return nil, errors.Wrap(err, "waitLbResStatus(spool,  10*time.Second, 8*time.Minute)")
   349  	}
   350  	// create healthmonitor
   351  	if group.HealthCheck != nil {
   352  
   353  		healthmonitor, err := lb.region.CreateLoadbalancerHealthmonitor(spool.ID, group.HealthCheck)
   354  		if err != nil {
   355  			return nil, errors.Wrapf(err, "region.CreateLoadbalancerHealthmonitor(%s, group.HealthCheck)", spool.ID)
   356  		}
   357  		spool.healthmonitor = healthmonitor
   358  	}
   359  	// wait health monitor
   360  	if spool.healthmonitor != nil {
   361  		err = waitLbResStatus(spool.healthmonitor, 10*time.Second, 8*time.Minute)
   362  		if err != nil {
   363  			return nil, errors.Wrap(err, "waitLbResStatus(spool.healthmonitor,  10*time.Second, 8*time.Minute)")
   364  		}
   365  	}
   366  	return spool, nil
   367  }
   368  
   369  func (lb *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) {
   370  	// ensure lb status
   371  	err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
   372  	if err != nil {
   373  		return nil, errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
   374  	}
   375  	slistener, err := lb.region.CreateLoadbalancerListener(lb.ID, listener)
   376  	if err != nil {
   377  		return nil, errors.Wrapf(err, "lb.region.CreateLoadbalancerListener(%s, listener)", lb.ID)
   378  	}
   379  	return slistener, nil
   380  }
   381  
   382  func (lb *SLoadbalancer) GetLoadbalancerSpec() string {
   383  	return lb.Description
   384  }
   385  
   386  func (lb *SLoadbalancer) GetChargeType() string {
   387  	eip, err := lb.GetIEIP()
   388  	if err != nil {
   389  		log.Errorf("lb.GetIEIP(): %s", err)
   390  	}
   391  	if err != nil {
   392  		return eip.GetInternetChargeType()
   393  	}
   394  
   395  	return api.EIP_CHARGE_TYPE_BY_TRAFFIC
   396  }
   397  
   398  func (lb *SLoadbalancer) GetEgressMbps() int {
   399  	return 0
   400  }
   401  
   402  func (lb *SLoadbalancer) GetILoadBalancerBackendGroupById(poolId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
   403  	err := lb.Refresh()
   404  	if err != nil {
   405  		return nil, errors.Wrap(err, "lb.Refresh()")
   406  	}
   407  	index := -1
   408  	for i := 0; i < len(lb.PoolIds); i++ {
   409  		if poolId == lb.PoolIds[i].ID {
   410  			index = i
   411  		}
   412  	}
   413  	if index < 0 {
   414  		return nil, cloudprovider.ErrNotFound
   415  	}
   416  	spool, err := lb.region.GetLoadbalancerPoolById(poolId)
   417  	if err != nil {
   418  		return nil, errors.Wrapf(err, "lb.region.GetLoadbalancerPoolById(%s)", poolId)
   419  	}
   420  	if spool.GetStatus() == api.LB_STATUS_DELETING {
   421  		return nil, cloudprovider.ErrNotFound
   422  	}
   423  	return spool, nil
   424  }
   425  
   426  func (lb *SLoadbalancer) GetIEIP() (cloudprovider.ICloudEIP, error) {
   427  	eips, err := lb.region.GetEips("")
   428  	if err != nil {
   429  		return nil, errors.Wrapf(err, "lb.region.GetEips()")
   430  	}
   431  	for _, eip := range eips {
   432  		if eip.PortId == lb.VipPortID {
   433  			return &eip, nil
   434  		}
   435  	}
   436  	return nil, nil
   437  }
   438  
   439  func (region *SRegion) UpdateLoadBalancerAdminStateUp(AdminStateUp bool, loadbalancerId string) error {
   440  	params := jsonutils.NewDict()
   441  	poolParam := jsonutils.NewDict()
   442  	poolParam.Add(jsonutils.NewBool(AdminStateUp), "admin_state_up")
   443  	params.Add(poolParam, "loadbalancer")
   444  	_, err := region.lbUpdate(fmt.Sprintf("/v2/lbaas/loadbalancers/%s", loadbalancerId), params)
   445  	if err != nil {
   446  		return errors.Wrapf(err, `region.lbUpdate(/v2/lbaas/loadbalancers/%s), params)`, loadbalancerId)
   447  	}
   448  	return nil
   449  }
   450  
   451  func (lb *SLoadbalancer) Start() error {
   452  	// ensure lb status
   453  	err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
   454  	if err != nil {
   455  		return errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
   456  	}
   457  	err = lb.region.UpdateLoadBalancerAdminStateUp(true, lb.ID)
   458  	if err != nil {
   459  		return errors.Wrapf(err, "lb.region.UpdateLoadBalancerAdminStateUp(true, %s)", lb.ID)
   460  	}
   461  	err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
   462  	if err != nil {
   463  		return errors.Wrap(err, "waitLbResStatus(lb,  10*time.Second, 8*time.Minute)")
   464  	}
   465  	return nil
   466  }
   467  
   468  func (lb *SLoadbalancer) Stop() error {
   469  	// ensure lb status
   470  	err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
   471  	if err != nil {
   472  		return errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
   473  	}
   474  	err = lb.region.UpdateLoadBalancerAdminStateUp(false, lb.ID)
   475  	if err != nil {
   476  		return errors.Wrapf(err, "lb.region.UpdateLoadBalancerAdminStateUp(false,%s)", lb.ID)
   477  	}
   478  	err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
   479  	if err != nil {
   480  		return errors.Wrap(err, "waitLbResStatus(lb,  10*time.Second, 8*time.Minute)")
   481  	}
   482  	return nil
   483  }
   484  
   485  func (lb *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) {
   486  
   487  	return lb.region.GetLoadbalancerListenerbyId(listenerId)
   488  }
   489  
   490  func (lb *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) {
   491  	ilisteners := []cloudprovider.ICloudLoadbalancerListener{}
   492  	for i := 0; i < len(lb.ListenerIds); i++ {
   493  		listener, err := lb.region.GetLoadbalancerListenerbyId(lb.ListenerIds[i].ID)
   494  		if err != nil {
   495  			return nil, errors.Wrapf(err, "lb.region.GetLoadbalancerListenerbyId(%s)", lb.ListenerIds[i].ID)
   496  		}
   497  		ilisteners = append(ilisteners, listener)
   498  	}
   499  	return ilisteners, nil
   500  }
   501  
   502  func (lb *SLoadbalancer) GetProjectId() string {
   503  	return lb.ProjectID
   504  }
   505  
   506  func (self *SLoadbalancer) SetTags(tags map[string]string, replace bool) error {
   507  	return cloudprovider.ErrNotSupported
   508  }