yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcso/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 hcso
    16  
    17  import (
    18  	"fmt"
    19  	"time"
    20  
    21  	"yunion.io/x/jsonutils"
    22  	"yunion.io/x/log"
    23  	"yunion.io/x/pkg/errors"
    24  
    25  	billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
    26  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    27  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    28  	"yunion.io/x/cloudmux/pkg/multicloud"
    29  	"yunion.io/x/cloudmux/pkg/multicloud/huawei"
    30  )
    31  
    32  type TInternetChargeType string
    33  
    34  const (
    35  	InternetChargeByTraffic   = TInternetChargeType("traffic")
    36  	InternetChargeByBandwidth = TInternetChargeType("bandwidth")
    37  )
    38  
    39  type Bandwidth struct {
    40  	ID                  string         `json:"id"`
    41  	Name                string         `json:"name"`
    42  	Size                int64          `json:"size"`
    43  	ShareType           string         `json:"share_type"`
    44  	PublicipInfo        []PublicipInfo `json:"publicip_info"`
    45  	TenantID            string         `json:"tenant_id"`
    46  	BandwidthType       string         `json:"bandwidth_type"`
    47  	ChargeMode          string         `json:"charge_mode"`
    48  	BillingInfo         string         `json:"billing_info"`
    49  	EnterpriseProjectID string         `json:"enterprise_project_id"`
    50  }
    51  
    52  type PublicipInfo struct {
    53  	PublicipID      string `json:"publicip_id"`
    54  	PublicipAddress string `json:"publicip_address"`
    55  	PublicipType    string `json:"publicip_type"`
    56  	IPVersion       int64  `json:"ip_version"`
    57  }
    58  
    59  type SProfile struct {
    60  	UserID    string `json:"user_id"`
    61  	ProductID string `json:"product_id"`
    62  	RegionID  string `json:"region_id"`
    63  	OrderID   string `json:"order_id"`
    64  }
    65  
    66  // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090598.html
    67  type SEipAddress struct {
    68  	region *SRegion
    69  	port   *Port
    70  	multicloud.SEipBase
    71  	huawei.HuaweiTags
    72  
    73  	ID                  string    `json:"id"`
    74  	Status              string    `json:"status"`
    75  	Profile             *SProfile `json:"profile,omitempty"`
    76  	Type                string    `json:"type"`
    77  	PublicIPAddress     string    `json:"public_ip_address"`
    78  	PrivateIPAddress    string    `json:"private_ip_address"`
    79  	TenantID            string    `json:"tenant_id"`
    80  	CreateTime          time.Time `json:"create_time"`
    81  	BandwidthID         string    `json:"bandwidth_id"`
    82  	BandwidthShareType  string    `json:"bandwidth_share_type"`
    83  	BandwidthSize       int64     `json:"bandwidth_size"`
    84  	BandwidthName       string    `json:"bandwidth_name"`
    85  	EnterpriseProjectID string    `json:"enterprise_project_id"`
    86  	IPVersion           int64     `json:"ip_version"`
    87  	PortId              string    `json:"port_id"`
    88  	EnterpriseProjectId string
    89  }
    90  
    91  func (self *SEipAddress) GetId() string {
    92  	return self.ID
    93  }
    94  
    95  func (self *SEipAddress) GetName() string {
    96  	if len(self.BandwidthName) == 0 {
    97  		return self.BandwidthName
    98  	}
    99  
   100  	return self.PublicIPAddress
   101  }
   102  
   103  func (self *SEipAddress) GetGlobalId() string {
   104  	return self.ID
   105  }
   106  
   107  func (self *SEipAddress) GetStatus() string {
   108  	// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090598.html
   109  	switch self.Status {
   110  	case "ACTIVE", "DOWN", "ELB":
   111  		return api.EIP_STATUS_READY
   112  	case "PENDING_CREATE", "NOTIFYING":
   113  		return api.EIP_STATUS_ALLOCATE
   114  	case "BINDING":
   115  		return api.EIP_STATUS_ALLOCATE
   116  	case "BIND_ERROR":
   117  		return api.EIP_STATUS_ALLOCATE_FAIL
   118  	case "PENDING_DELETE", "NOTIFY_DELETE":
   119  		return api.EIP_STATUS_DEALLOCATE
   120  	default:
   121  		return api.EIP_STATUS_UNKNOWN
   122  	}
   123  }
   124  
   125  func (self *SEipAddress) Refresh() error {
   126  	if self.IsEmulated() {
   127  		return nil
   128  	}
   129  	new, err := self.region.GetEip(self.ID)
   130  	if err != nil {
   131  		return err
   132  	}
   133  	return jsonutils.Update(self, new)
   134  }
   135  
   136  func (self *SEipAddress) IsEmulated() bool {
   137  	return false
   138  }
   139  
   140  func (self *SEipAddress) GetIpAddr() string {
   141  	return self.PublicIPAddress
   142  }
   143  
   144  func (self *SEipAddress) GetMode() string {
   145  	return api.EIP_MODE_STANDALONE_EIP
   146  }
   147  
   148  func (self *SEipAddress) GetPort() *Port {
   149  	if len(self.PortId) == 0 {
   150  		return nil
   151  	}
   152  
   153  	if self.port != nil {
   154  		return self.port
   155  	}
   156  
   157  	port, err := self.region.GetPort(self.PortId)
   158  	if err != nil {
   159  		return nil
   160  	} else {
   161  		self.port = &port
   162  	}
   163  
   164  	return self.port
   165  }
   166  
   167  func (self *SEipAddress) GetAssociationType() string {
   168  	if len(self.PortId) == 0 {
   169  		return ""
   170  	}
   171  	port, err := self.region.GetPort(self.PortId)
   172  	if err != nil {
   173  		log.Errorf("Get eip %s port %s error: %v", self.ID, self.PortId, err)
   174  		return ""
   175  	}
   176  
   177  	switch port.DeviceOwner {
   178  	case "neutron:LOADBALANCER", "neutron:LOADBALANCERV2":
   179  		return api.EIP_ASSOCIATE_TYPE_LOADBALANCER
   180  	case "network:nat_gateway":
   181  		return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY
   182  	default:
   183  		return port.DeviceOwner
   184  	}
   185  }
   186  
   187  func (self *SEipAddress) GetAssociationExternalId() string {
   188  	// network/0273a359d61847fc83405926c958c746/ext-floatingips?tenantId=0273a359d61847fc83405926c958c746&limit=2000
   189  	// 只能通过 port id 反查device id.
   190  	if len(self.PortId) > 0 {
   191  		port, _ := self.region.GetPort(self.PortId)
   192  		return port.DeviceID
   193  	}
   194  	return ""
   195  }
   196  
   197  func (self *SEipAddress) GetBandwidth() int {
   198  	return int(self.BandwidthSize) // Mb
   199  }
   200  
   201  func (self *SEipAddress) GetINetworkId() string {
   202  	return ""
   203  }
   204  
   205  func (self *SEipAddress) GetInternetChargeType() string {
   206  	// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090603.html
   207  	bandwidth, err := self.region.GetEipBandwidth(self.BandwidthID)
   208  	if err != nil {
   209  		return api.EIP_CHARGE_TYPE_BY_TRAFFIC
   210  	}
   211  
   212  	if bandwidth.ChargeMode != "traffic" {
   213  		return api.EIP_CHARGE_TYPE_BY_BANDWIDTH
   214  	} else {
   215  		return api.EIP_CHARGE_TYPE_BY_TRAFFIC
   216  	}
   217  }
   218  
   219  func (self *SEipAddress) GetBillingType() string {
   220  	if self.Profile == nil {
   221  		return billing_api.BILLING_TYPE_POSTPAID
   222  	} else {
   223  		return billing_api.BILLING_TYPE_PREPAID
   224  	}
   225  }
   226  
   227  func (self *SEipAddress) GetCreatedAt() time.Time {
   228  	return self.CreateTime
   229  }
   230  
   231  func (self *SEipAddress) GetExpiredAt() time.Time {
   232  	return time.Time{}
   233  }
   234  
   235  func (self *SEipAddress) Delete() error {
   236  	return self.region.DeallocateEIP(self.ID)
   237  }
   238  
   239  func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error {
   240  	portId, err := self.region.GetInstancePortId(conf.InstanceId)
   241  	if err != nil {
   242  		return err
   243  	}
   244  
   245  	if len(self.PortId) > 0 {
   246  		if self.PortId == portId {
   247  			return nil
   248  		}
   249  
   250  		return fmt.Errorf("eip %s aready associate with port %s", self.GetId(), self.PortId)
   251  	}
   252  
   253  	err = self.region.AssociateEipWithPortId(self.ID, portId)
   254  	if err != nil {
   255  		return err
   256  	}
   257  
   258  	err = cloudprovider.WaitStatusWithDelay(self, api.EIP_STATUS_READY, 10*time.Second, 10*time.Second, 180*time.Second)
   259  	return err
   260  }
   261  
   262  func (self *SEipAddress) Dissociate() error {
   263  	if len(self.PortId) == 0 {
   264  		return nil
   265  	}
   266  	port, err := self.region.GetPort(self.PortId)
   267  	if err != nil {
   268  		return errors.Wrapf(err, "GetPort(%s)", self.PortId)
   269  	}
   270  
   271  	err = self.region.DissociateEip(self.ID, port.DeviceID)
   272  	if err != nil {
   273  		return errors.Wrapf(err, "DissociateEip")
   274  	}
   275  	err = cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
   276  	return err
   277  }
   278  
   279  func (self *SEipAddress) ChangeBandwidth(bw int) error {
   280  	return self.region.UpdateEipBandwidth(self.BandwidthID, bw)
   281  }
   282  
   283  func (self *SRegion) GetInstancePortId(instanceId string) (string, error) {
   284  	// 目前只绑定一个网卡
   285  	// todo: 还需要按照ports状态进行过滤
   286  	ports, err := self.GetPorts(instanceId)
   287  	if err != nil {
   288  		return "", err
   289  	}
   290  
   291  	if len(ports) == 0 {
   292  		return "", fmt.Errorf("AssociateEip instance %s port is empty", instanceId)
   293  	}
   294  
   295  	return ports[0].ID, nil
   296  }
   297  
   298  // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090596.html
   299  func (self *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternetChargeType, bgpType string, projectId string) (*SEipAddress, error) {
   300  	paramsStr := `
   301  {
   302      "publicip": {
   303          "type": "%s",
   304     "ip_version": 4
   305      },
   306      "bandwidth": {
   307          "name": "%s",
   308          "size": %d,
   309          "share_type": "PER",
   310          "charge_mode": "%s"
   311      }
   312  }
   313  `
   314  	if len(bgpType) == 0 {
   315  		return nil, fmt.Errorf("AllocateEIP bgp type should not be empty")
   316  	}
   317  	paramsStr = fmt.Sprintf(paramsStr, bgpType, name, bwMbps, chargeType)
   318  	_params, _ := jsonutils.ParseString(paramsStr)
   319  	params := _params.(*jsonutils.JSONDict)
   320  	if len(projectId) > 0 {
   321  		params.Set("enterprise_project_id", jsonutils.NewString(projectId))
   322  	}
   323  	eip := SEipAddress{}
   324  	err := DoCreate(self.ecsClient.Eips.Create, params, &eip)
   325  	return &eip, err
   326  }
   327  
   328  func (self *SRegion) GetEip(eipId string) (*SEipAddress, error) {
   329  	var eip SEipAddress
   330  	err := DoGet(self.ecsClient.Eips.Get, eipId, nil, &eip)
   331  	eip.region = self
   332  	return &eip, err
   333  }
   334  
   335  func (self *SRegion) DeallocateEIP(eipId string) error {
   336  	_, err := self.ecsClient.Eips.Delete(eipId, nil)
   337  	return err
   338  }
   339  
   340  func (self *SRegion) AssociateEip(eipId string, instanceId string) error {
   341  	portId, err := self.GetInstancePortId(instanceId)
   342  	if err != nil {
   343  		return err
   344  	}
   345  	return self.AssociateEipWithPortId(eipId, portId)
   346  }
   347  
   348  func (self *SRegion) AssociateEipWithPortId(eipId string, portId string) error {
   349  	params := jsonutils.NewDict()
   350  	publicIPObj := jsonutils.NewDict()
   351  	publicIPObj.Add(jsonutils.NewString(portId), "port_id")
   352  	params.Add(publicIPObj, "publicip")
   353  
   354  	_, err := self.ecsClient.Eips.Update(eipId, params)
   355  	return err
   356  }
   357  
   358  func (self *SRegion) DissociateEip(eipId string, instanceId string) error {
   359  	eip, err := self.GetEip(eipId)
   360  	if err != nil {
   361  		return err
   362  	}
   363  
   364  	// 已经是解绑状态
   365  	if eip.Status == "DOWN" {
   366  		return nil
   367  	}
   368  
   369  	remoteInstanceId := eip.GetAssociationExternalId()
   370  	if remoteInstanceId != instanceId {
   371  		return fmt.Errorf("eip %s associate with another instance %s", eipId, remoteInstanceId)
   372  	}
   373  
   374  	paramsStr := `{"publicip":{"port_id":null}}`
   375  	params, _ := jsonutils.ParseString(paramsStr)
   376  	_, err = self.ecsClient.Eips.Update(eipId, params)
   377  	return err
   378  }
   379  
   380  func (self *SRegion) UpdateEipBandwidth(bandwidthId string, bw int) error {
   381  	paramStr := `{
   382  		"bandwidth":
   383  		{
   384             "size": %d
   385  		}
   386  	}`
   387  
   388  	paramStr = fmt.Sprintf(paramStr, bw)
   389  	params, _ := jsonutils.ParseString(paramStr)
   390  	_, err := self.ecsClient.Bandwidths.Update(bandwidthId, params)
   391  	return err
   392  }
   393  
   394  func (self *SRegion) GetEipBandwidth(bandwidthId string) (Bandwidth, error) {
   395  	bandwidth := Bandwidth{}
   396  	err := DoGet(self.ecsClient.Bandwidths.Get, bandwidthId, nil, &bandwidth)
   397  	return bandwidth, err
   398  }
   399  
   400  func (self *SEipAddress) GetProjectId() string {
   401  	return self.EnterpriseProjectId
   402  }