yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/huawei/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 huawei
    16  
    17  import (
    18  	"fmt"
    19  	"net/url"
    20  	"strings"
    21  	"time"
    22  
    23  	"yunion.io/x/jsonutils"
    24  	"yunion.io/x/log"
    25  
    26  	billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
    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("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  	HuaweiTags
    72  
    73  	Alias               string
    74  	ID                  string    `json:"id"`
    75  	Status              string    `json:"status"`
    76  	Profile             *SProfile `json:"profile,omitempty"`
    77  	Type                string    `json:"type"`
    78  	PublicIPAddress     string    `json:"public_ip_address"`
    79  	PrivateIPAddress    string    `json:"private_ip_address"`
    80  	TenantID            string    `json:"tenant_id"`
    81  	CreateTime          time.Time `json:"create_time"`
    82  	BandwidthID         string    `json:"bandwidth_id"`
    83  	BandwidthShareType  string    `json:"bandwidth_share_type"`
    84  	BandwidthSize       int64     `json:"bandwidth_size"`
    85  	BandwidthName       string    `json:"bandwidth_name"`
    86  	EnterpriseProjectID string    `json:"enterprise_project_id"`
    87  	IPVersion           int64     `json:"ip_version"`
    88  	PortId              string    `json:"port_id"`
    89  	EnterpriseProjectId string
    90  }
    91  
    92  func (self *SEipAddress) GetId() string {
    93  	return self.ID
    94  }
    95  
    96  func (self *SEipAddress) GetName() string {
    97  	if len(self.Alias) > 0 {
    98  		return self.Alias
    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  	if strings.HasPrefix(port.DeviceOwner, "compute") {
   178  		return api.EIP_ASSOCIATE_TYPE_SERVER
   179  	}
   180  
   181  	switch port.DeviceOwner {
   182  	case "neutron:LOADBALANCER", "neutron:LOADBALANCERV2":
   183  		return api.EIP_ASSOCIATE_TYPE_LOADBALANCER
   184  	case "network:nat_gateway":
   185  		return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY
   186  	default:
   187  		return port.DeviceOwner
   188  	}
   189  }
   190  
   191  func (self *SEipAddress) GetAssociationExternalId() string {
   192  	// network/0273a359d61847fc83405926c958c746/ext-floatingips?tenantId=0273a359d61847fc83405926c958c746&limit=2000
   193  	// 只能通过 port id 反查device id.
   194  	if len(self.PortId) > 0 {
   195  		port, _ := self.region.GetPort(self.PortId)
   196  		return port.DeviceID
   197  	}
   198  	return ""
   199  }
   200  
   201  func (self *SEipAddress) GetBandwidth() int {
   202  	return int(self.BandwidthSize) // Mb
   203  }
   204  
   205  func (self *SEipAddress) GetINetworkId() string {
   206  	return ""
   207  }
   208  
   209  func (self *SEipAddress) GetInternetChargeType() string {
   210  	// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090603.html
   211  	bandwidth, err := self.region.GetEipBandwidth(self.BandwidthID)
   212  	if err != nil {
   213  		return api.EIP_CHARGE_TYPE_BY_TRAFFIC
   214  	}
   215  	if bandwidth.ChargeMode == "traffic" {
   216  		return api.EIP_CHARGE_TYPE_BY_TRAFFIC
   217  	}
   218  	return api.EIP_CHARGE_TYPE_BY_BANDWIDTH
   219  }
   220  
   221  func (self *SEipAddress) GetBillingType() string {
   222  	if self.Profile == nil {
   223  		return billing_api.BILLING_TYPE_POSTPAID
   224  	}
   225  	return billing_api.BILLING_TYPE_PREPAID
   226  }
   227  
   228  func (self *SEipAddress) GetCreatedAt() time.Time {
   229  	return self.CreateTime
   230  }
   231  
   232  func (self *SEipAddress) GetExpiredAt() time.Time {
   233  	return time.Time{}
   234  }
   235  
   236  func (self *SEipAddress) Delete() error {
   237  	return self.region.DeallocateEIP(self.ID)
   238  }
   239  
   240  func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error {
   241  	portId, err := self.region.GetInstancePortId(conf.InstanceId)
   242  	if err != nil {
   243  		return err
   244  	}
   245  
   246  	if len(self.PortId) > 0 {
   247  		if self.PortId == portId {
   248  			return nil
   249  		}
   250  
   251  		return fmt.Errorf("eip %s aready associate with port %s", self.GetId(), self.PortId)
   252  	}
   253  
   254  	err = self.region.AssociateEipWithPortId(self.ID, portId)
   255  	if err != nil {
   256  		return err
   257  	}
   258  
   259  	err = cloudprovider.WaitStatusWithDelay(self, api.EIP_STATUS_READY, 10*time.Second, 10*time.Second, 180*time.Second)
   260  	return err
   261  }
   262  
   263  func (self *SEipAddress) Dissociate() error {
   264  	err := self.region.DissociateEip(self.ID)
   265  	if err != nil {
   266  		return err
   267  	}
   268  	return cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
   269  }
   270  
   271  func (self *SEipAddress) ChangeBandwidth(bw int) error {
   272  	return self.region.UpdateEipBandwidth(self.BandwidthID, bw)
   273  }
   274  
   275  func (self *SRegion) GetInstancePortId(instanceId string) (string, error) {
   276  	// 目前只绑定一个网卡
   277  	// todo: 还需要按照ports状态进行过滤
   278  	ports, err := self.GetPorts(instanceId)
   279  	if err != nil {
   280  		return "", err
   281  	}
   282  
   283  	if len(ports) == 0 {
   284  		return "", fmt.Errorf("AssociateEip instance %s port is empty", instanceId)
   285  	}
   286  
   287  	return ports[0].ID, nil
   288  }
   289  
   290  // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090596.html
   291  func (self *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternetChargeType, bgpType string, projectId string) (*SEipAddress, error) {
   292  	params := map[string]interface{}{
   293  		"bandwidth": map[string]interface{}{
   294  			"name":        name,
   295  			"size":        bwMbps,
   296  			"share_type":  "PER",
   297  			"charge_mode": chargeType,
   298  		},
   299  		"publicip": map[string]interface{}{
   300  			"type":       bgpType,
   301  			"ip_version": 4,
   302  			"alias":      name,
   303  		},
   304  	}
   305  	if len(projectId) > 0 {
   306  		params["enterprise_project_id"] = projectId
   307  	}
   308  	resp, err := self.vpcCreate("publicips", params)
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  	eip := &SEipAddress{region: self}
   313  	return eip, resp.Unmarshal(eip, "publicip")
   314  }
   315  
   316  func (self *SRegion) GetEip(eipId string) (*SEipAddress, error) {
   317  	resp, err := self.vpcGet("publicips/" + eipId)
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  	eip := &SEipAddress{region: self}
   322  	return eip, resp.Unmarshal(eip, "publicip")
   323  }
   324  
   325  func (self *SRegion) DeallocateEIP(eipId string) error {
   326  	_, err := self.vpcDelete("publicips/" + eipId)
   327  	return err
   328  }
   329  
   330  func (self *SRegion) AssociateEip(eipId string, instanceId string) error {
   331  	portId, err := self.GetInstancePortId(instanceId)
   332  	if err != nil {
   333  		return err
   334  	}
   335  	return self.AssociateEipWithPortId(eipId, portId)
   336  }
   337  
   338  func (self *SRegion) AssociateEipWithPortId(eipId string, portId string) error {
   339  	params := map[string]interface{}{
   340  		"publicip": map[string]interface{}{
   341  			"port_id": portId,
   342  		},
   343  	}
   344  	_, err := self.vpcUpdate("publicips/"+eipId, params)
   345  	return err
   346  }
   347  
   348  func (self *SRegion) DissociateEip(eipId string) error {
   349  	return self.AssociateEipWithPortId(eipId, "")
   350  }
   351  
   352  func (self *SRegion) UpdateEipBandwidth(bandwidthId string, bw int) error {
   353  	params := map[string]interface{}{
   354  		"bandwidth": map[string]interface{}{
   355  			"size": bw,
   356  		},
   357  	}
   358  	_, err := self.vpcUpdate("bandwidths/"+bandwidthId, params)
   359  	return err
   360  }
   361  
   362  func (self *SRegion) GetEipBandwidth(id string) (*Bandwidth, error) {
   363  	resp, err := self.vpcGet("bandwidths/" + id)
   364  	if err != nil {
   365  		return nil, err
   366  	}
   367  	ret := &Bandwidth{}
   368  	return ret, resp.Unmarshal(ret, "bandwidth")
   369  }
   370  
   371  func (self *SEipAddress) GetProjectId() string {
   372  	return self.EnterpriseProjectId
   373  }
   374  
   375  func (self *SRegion) GetEips(portId string, addrs []string) ([]SEipAddress, error) {
   376  	query := url.Values{}
   377  	for _, addr := range addrs {
   378  		query.Add("public_ip_address", addr)
   379  	}
   380  	if len(portId) > 0 {
   381  		query.Set("port_id", portId)
   382  	}
   383  	resp, err := self.vpcList("publicips", query)
   384  	if err != nil {
   385  		return nil, err
   386  	}
   387  	eips := []SEipAddress{}
   388  	err = resp.Unmarshal(&eips, "publicips")
   389  	if err != nil {
   390  		return nil, err
   391  	}
   392  	for i := range eips {
   393  		eips[i].region = self
   394  	}
   395  	return eips, nil
   396  }