yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/apsara/eip.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 apsara
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"time"
    21  
    22  	"yunion.io/x/jsonutils"
    23  	"yunion.io/x/log"
    24  	"yunion.io/x/pkg/errors"
    25  	"yunion.io/x/pkg/utils"
    26  
    27  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    28  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    29  	"yunion.io/x/cloudmux/pkg/multicloud"
    30  )
    31  
    32  type TInternetChargeType string
    33  
    34  const (
    35  	InternetChargeByTraffic   = TInternetChargeType("PayByTraffic")
    36  	InternetChargeByBandwidth = TInternetChargeType("PayByBandwidth")
    37  )
    38  
    39  const (
    40  	EIP_STATUS_ASSOCIATING   = "Associating"
    41  	EIP_STATUS_UNASSOCIATING = "Unassociating"
    42  	EIP_STATUS_INUSE         = "InUse"
    43  	EIP_STATUS_AVAILABLE     = "Available"
    44  
    45  	EIP_OPERATION_LOCK_FINANCIAL = "financial"
    46  	EIP_OPERATION_LOCK_SECURITY  = "security"
    47  
    48  	EIP_INSTANCE_TYPE_ECS   = "EcsInstance" // (默认值):VPC类型的ECS实例
    49  	EIP_INTANNCE_TYPE_SLB   = "SlbInstance" // :VPC类型的SLB实例
    50  	EIP_INSTANCE_TYPE_NAT   = "Nat"         // :NAT网关
    51  	EIP_INSTANCE_TYPE_HAVIP = "HaVip"       // :HAVIP
    52  )
    53  
    54  /*
    55  {
    56  	"AllocationId":"eip-2zeddtan63ou44dtyt9s3",
    57  	"AllocationTime":"2019-02-23T06:48:36Z",
    58  	"Bandwidth":"100",
    59  	"ChargeType":"PostPaid",
    60  	"ExpiredTime":"",
    61  	"InstanceId":"",
    62  	"InstanceType":"",
    63  	"InternetChargeType":"PayByTraffic",
    64  	"IpAddress":"39.105.131.32",
    65  	"OperationLocks":{"LockReason":[]},
    66  	"RegionId":"cn-beijing",
    67  	"Status":"Available"
    68  }
    69  */
    70  
    71  type SEipAddress struct {
    72  	region *SRegion
    73  	multicloud.SEipBase
    74  	ApsaraTags
    75  
    76  	AllocationId string
    77  
    78  	InternetChargeType TInternetChargeType
    79  
    80  	IpAddress string
    81  	Status    string
    82  
    83  	InstanceType string
    84  	InstanceId   string
    85  	Bandwidth    int /* Mbps */
    86  
    87  	AllocationTime time.Time
    88  
    89  	OperationLocks string
    90  
    91  	ChargeType  TChargeType
    92  	ExpiredTime time.Time
    93  	DepartmentInfo
    94  }
    95  
    96  func (self *SEipAddress) GetId() string {
    97  	return self.AllocationId
    98  }
    99  
   100  func (self *SEipAddress) GetName() string {
   101  	return self.IpAddress
   102  }
   103  
   104  func (self *SEipAddress) GetGlobalId() string {
   105  	return self.AllocationId
   106  }
   107  
   108  func (self *SEipAddress) GetStatus() string {
   109  	switch self.Status {
   110  	case EIP_STATUS_AVAILABLE, EIP_STATUS_INUSE:
   111  		return api.EIP_STATUS_READY
   112  	case EIP_STATUS_ASSOCIATING:
   113  		return api.EIP_STATUS_ASSOCIATE
   114  	case EIP_STATUS_UNASSOCIATING:
   115  		return api.EIP_STATUS_DISSOCIATE
   116  	default:
   117  		return api.EIP_STATUS_UNKNOWN
   118  	}
   119  }
   120  
   121  func (self *SEipAddress) Refresh() error {
   122  	if self.IsEmulated() {
   123  		return nil
   124  	}
   125  	new, err := self.region.GetEip(self.AllocationId)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	return jsonutils.Update(self, new)
   130  }
   131  
   132  func (self *SEipAddress) IsEmulated() bool {
   133  	if self.AllocationId == self.InstanceId {
   134  		// fixed Public IP
   135  		return true
   136  	} else {
   137  		return false
   138  	}
   139  }
   140  
   141  func (self *SEipAddress) GetIpAddr() string {
   142  	return self.IpAddress
   143  }
   144  
   145  func (self *SEipAddress) GetMode() string {
   146  	if self.InstanceId == self.AllocationId {
   147  		return api.EIP_MODE_INSTANCE_PUBLICIP
   148  	} else {
   149  		return api.EIP_MODE_STANDALONE_EIP
   150  	}
   151  }
   152  
   153  func (self *SEipAddress) GetAssociationType() string {
   154  	switch self.InstanceType {
   155  	case EIP_INSTANCE_TYPE_ECS, "NetworkInterface":
   156  		return api.EIP_ASSOCIATE_TYPE_SERVER
   157  	case EIP_INSTANCE_TYPE_NAT:
   158  		return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY
   159  	case EIP_INTANNCE_TYPE_SLB:
   160  		return api.EIP_ASSOCIATE_TYPE_LOADBALANCER
   161  	default:
   162  		return self.InstanceType
   163  	}
   164  }
   165  
   166  func (self *SEipAddress) GetAssociationExternalId() string {
   167  	return self.InstanceId
   168  }
   169  
   170  func (self *SEipAddress) GetBillingType() string {
   171  	return convertChargeType(self.ChargeType)
   172  }
   173  
   174  func (self *SEipAddress) GetCreatedAt() time.Time {
   175  	return self.AllocationTime
   176  }
   177  
   178  func (self *SEipAddress) GetExpiredAt() time.Time {
   179  	return convertExpiredAt(self.ExpiredTime)
   180  }
   181  
   182  func (self *SEipAddress) Delete() error {
   183  	return self.region.DeallocateEIP(self.AllocationId)
   184  }
   185  
   186  func (self *SEipAddress) GetBandwidth() int {
   187  	return self.Bandwidth
   188  }
   189  
   190  func (self *SEipAddress) GetINetworkId() string {
   191  	return ""
   192  }
   193  
   194  func (self *SEipAddress) GetInternetChargeType() string {
   195  	switch self.InternetChargeType {
   196  	case InternetChargeByTraffic:
   197  		return api.EIP_CHARGE_TYPE_BY_TRAFFIC
   198  	case InternetChargeByBandwidth:
   199  		return api.EIP_CHARGE_TYPE_BY_BANDWIDTH
   200  	default:
   201  		return api.EIP_CHARGE_TYPE_BY_TRAFFIC
   202  	}
   203  }
   204  
   205  func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error {
   206  	err := cloudprovider.Wait(20*time.Second, 60*time.Second, func() (bool, error) {
   207  		err := self.region.AssociateEip(self.AllocationId, conf.InstanceId)
   208  		if err != nil {
   209  			if isError(err, "IncorrectInstanceStatus") {
   210  				return false, nil
   211  			}
   212  			return false, errors.Wrap(err, "region.AssociateEip")
   213  		}
   214  		return true, nil
   215  	})
   216  	err = cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
   217  	return err
   218  }
   219  
   220  func (self *SEipAddress) Dissociate() error {
   221  	err := self.region.DissociateEip(self.AllocationId, self.InstanceId)
   222  	if err != nil {
   223  		return err
   224  	}
   225  	err = cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
   226  	return err
   227  }
   228  
   229  func (self *SEipAddress) ChangeBandwidth(bw int) error {
   230  	return self.region.UpdateEipBandwidth(self.AllocationId, bw)
   231  }
   232  
   233  func (region *SRegion) GetEips(eipId string, associatedId string, offset int, limit int) ([]SEipAddress, int, error) {
   234  	if limit > 50 || limit <= 0 {
   235  		limit = 50
   236  	}
   237  
   238  	params := make(map[string]string)
   239  	params["RegionId"] = region.RegionId
   240  	params["PageSize"] = fmt.Sprintf("%d", limit)
   241  	params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1)
   242  
   243  	if len(eipId) > 0 {
   244  		params["AllocationId"] = eipId
   245  	}
   246  
   247  	if len(associatedId) > 0 {
   248  		params["AssociatedInstanceId"] = associatedId
   249  		for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "ngw-": "Nat", "lb-": "SlbInstance"} {
   250  			if strings.HasPrefix(associatedId, prefix) {
   251  				params["AssociatedInstanceType"] = instanceType
   252  			}
   253  		}
   254  	}
   255  
   256  	body, err := region.vpcRequest("DescribeEipAddresses", params)
   257  	if err != nil {
   258  		log.Errorf("DescribeEipAddresses fail %s", err)
   259  		return nil, 0, err
   260  	}
   261  
   262  	eips := make([]SEipAddress, 0)
   263  	err = body.Unmarshal(&eips, "EipAddresses", "EipAddress")
   264  	if err != nil {
   265  		log.Errorf("Unmarshal EipAddress details fail %s", err)
   266  		return nil, 0, err
   267  	}
   268  	total, _ := body.Int("TotalCount")
   269  	for i := 0; i < len(eips); i += 1 {
   270  		eips[i].region = region
   271  	}
   272  	return eips, int(total), nil
   273  }
   274  
   275  func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) {
   276  	eips, total, err := region.GetEips(eipId, "", 0, 1)
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  	if total != 1 {
   281  		return nil, cloudprovider.ErrNotFound
   282  	}
   283  	return &eips[0], nil
   284  }
   285  
   286  func (region *SRegion) AllocateEIP(bwMbps int, chargeType TInternetChargeType, projectId string) (*SEipAddress, error) {
   287  	params := make(map[string]string)
   288  
   289  	params["Bandwidth"] = fmt.Sprintf("%d", bwMbps)
   290  	params["InternetChargeType"] = string(chargeType)
   291  	params["InstanceChargeType"] = "PostPaid"
   292  	params["ClientToken"] = utils.GenRequestId(20)
   293  	if len(projectId) > 0 {
   294  		params["ResourceGroup"] = projectId
   295  	}
   296  
   297  	body, err := region.vpcRequest("AllocateEipAddress", params)
   298  	if err != nil {
   299  		log.Errorf("AllocateEipAddress fail %s", err)
   300  		return nil, err
   301  	}
   302  
   303  	eipId, err := body.GetString("AllocationId")
   304  	if err != nil {
   305  		log.Errorf("fail to get AllocationId after EIP allocation??? %s", err)
   306  		return nil, err
   307  	}
   308  
   309  	return region.GetEip(eipId)
   310  }
   311  
   312  func (region *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) {
   313  	var ctype TInternetChargeType
   314  	switch eip.ChargeType {
   315  	case api.EIP_CHARGE_TYPE_BY_TRAFFIC:
   316  		ctype = InternetChargeByTraffic
   317  	case api.EIP_CHARGE_TYPE_BY_BANDWIDTH:
   318  		ctype = InternetChargeByBandwidth
   319  	}
   320  	return region.AllocateEIP(eip.BandwidthMbps, ctype, eip.ProjectId)
   321  }
   322  
   323  func (region *SRegion) DeallocateEIP(eipId string) error {
   324  	params := make(map[string]string)
   325  	params["AllocationId"] = eipId
   326  
   327  	_, err := region.ecsRequest("ReleaseEipAddress", params)
   328  	if err != nil {
   329  		log.Errorf("ReleaseEipAddress fail %s", err)
   330  	}
   331  	return err
   332  }
   333  
   334  func (region *SRegion) AssociateEip(eipId string, instanceId string) error {
   335  	params := make(map[string]string)
   336  	params["AllocationId"] = eipId
   337  	params["InstanceId"] = instanceId
   338  	for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "lb-": "SlbInstance", "ngw-": "Nat"} {
   339  		if strings.HasPrefix(instanceId, prefix) {
   340  			params["InstanceType"] = instanceType
   341  		}
   342  	}
   343  
   344  	_, err := region.ecsRequest("AssociateEipAddress", params)
   345  	if err != nil {
   346  		log.Errorf("AssociateEipAddress fail %s", err)
   347  	}
   348  	return err
   349  }
   350  
   351  func (region *SRegion) DissociateEip(eipId string, instanceId string) error {
   352  	params := make(map[string]string)
   353  	params["AllocationId"] = eipId
   354  	params["InstanceId"] = instanceId
   355  	for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "lb-": "SlbInstance", "ngw-": "Nat"} {
   356  		if strings.HasPrefix(instanceId, prefix) {
   357  			params["InstanceType"] = instanceType
   358  		}
   359  	}
   360  
   361  	_, err := region.ecsRequest("UnassociateEipAddress", params)
   362  	if err != nil {
   363  		log.Errorf("UnassociateEipAddress fail %s", err)
   364  	}
   365  	return err
   366  }
   367  
   368  func (region *SRegion) UpdateEipBandwidth(eipId string, bw int) error {
   369  	params := make(map[string]string)
   370  	params["AllocationId"] = eipId
   371  	params["Bandwidth"] = fmt.Sprintf("%d", bw)
   372  
   373  	_, err := region.ecsRequest("ModifyEipAddressAttribute", params)
   374  	if err != nil {
   375  		log.Errorf("ModifyEipAddressAttribute fail %s", err)
   376  	}
   377  	return err
   378  }