yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/ctyun/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 ctyun
    16  
    17  import (
    18  	"fmt"
    19  	"strconv"
    20  	"strings"
    21  	"time"
    22  
    23  	"yunion.io/x/jsonutils"
    24  	"yunion.io/x/log"
    25  	"yunion.io/x/pkg/errors"
    26  
    27  	billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
    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  type SEip struct {
    34  	region *SRegion
    35  	multicloud.SEipBase
    36  	CtyunTags
    37  
    38  	IPVersion           int64   `json:"ip_version"`
    39  	BandwidthShareType  string  `json:"bandwidth_share_type"`
    40  	Type                string  `json:"type"`
    41  	PrivateIPAddress    string  `json:"private_ip_address"`
    42  	EnterpriseProjectID string  `json:"enterprise_project_id"`
    43  	Status              string  `json:"status"`
    44  	PublicIPAddress     string  `json:"public_ip_address"`
    45  	ID                  string  `json:"id"`
    46  	TenantID            string  `json:"tenant_id"`
    47  	Profile             Profile `json:"profile"`
    48  	BandwidthName       string  `json:"bandwidth_name"`
    49  	BandwidthID         string  `json:"bandwidth_id"`
    50  	PortID              string  `json:"port_id"`
    51  	BandwidthSize       int     `json:"bandwidth_size"`
    52  	CreateTime          int64   `json:"create_time"`
    53  	MasterOrderID       string  `json:"masterOrderId"`
    54  	WorkOrderResourceID string  `json:"workOrderResourceId"`
    55  	ExpireTime          int64   `json:"expireTime"`
    56  	IsFreeze            int64   `json:"isFreeze"`
    57  }
    58  
    59  func (self *SEip) GetBillingType() string {
    60  	if len(self.MasterOrderID) > 0 {
    61  		return billing_api.BILLING_TYPE_PREPAID
    62  	} else {
    63  		return billing_api.BILLING_TYPE_POSTPAID
    64  	}
    65  }
    66  
    67  func (self *SEip) GetCreatedAt() time.Time {
    68  	return time.Unix(self.CreateTime/1000, 0)
    69  }
    70  
    71  func (self *SEip) GetExpiredAt() time.Time {
    72  	if self.ExpireTime == 0 {
    73  		return time.Time{}
    74  	}
    75  
    76  	return time.Unix(self.ExpireTime/1000, 0)
    77  }
    78  
    79  func (self *SEip) GetId() string {
    80  	return self.ID
    81  }
    82  
    83  func (self *SEip) GetName() string {
    84  	return self.BandwidthName
    85  }
    86  
    87  func (self *SEip) GetGlobalId() string {
    88  	return self.GetId()
    89  }
    90  
    91  func (self *SEip) GetStatus() string {
    92  	switch self.Status {
    93  	case "ACTIVE", "DOWN":
    94  		return api.EIP_STATUS_READY
    95  	case "ERROR":
    96  		return api.EIP_STATUS_ALLOCATE_FAIL
    97  	default:
    98  		return api.EIP_STATUS_UNKNOWN
    99  	}
   100  }
   101  
   102  func (self *SEip) Refresh() error {
   103  	if self.IsEmulated() {
   104  		return nil
   105  	}
   106  	new, err := self.region.GetEip(self.ID)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	return jsonutils.Update(self, new)
   111  }
   112  
   113  func (self *SEip) IsEmulated() bool {
   114  	return false
   115  }
   116  
   117  func (self *SEip) GetProjectId() string {
   118  	return ""
   119  }
   120  
   121  func (self *SEip) GetIpAddr() string {
   122  	return self.PublicIPAddress
   123  }
   124  
   125  func (self *SEip) GetMode() string {
   126  	return api.EIP_MODE_STANDALONE_EIP
   127  }
   128  
   129  func (self *SEip) GetINetworkId() string {
   130  	return ""
   131  }
   132  
   133  func (self *SEip) GetAssociationType() string {
   134  	orders, err := self.region.GetOrder(self.WorkOrderResourceID)
   135  	if err != nil {
   136  		// bugfix: 由于没有接口可以返向查询出关联的device,这里默认关联的是server?
   137  		if len(self.PortID) > 0 || len(self.PrivateIPAddress) > 0 {
   138  			return api.EIP_ASSOCIATE_TYPE_SERVER
   139  		}
   140  
   141  		log.Errorf("SEip.GetAssociationType %s", err)
   142  		return ""
   143  	}
   144  
   145  	for i := range orders {
   146  		order := orders[i]
   147  		if strings.Contains(order.ResourceType, "LOADBALANCER") {
   148  			return api.EIP_ASSOCIATE_TYPE_LOADBALANCER
   149  		} else {
   150  			return api.EIP_ASSOCIATE_TYPE_SERVER
   151  		}
   152  	}
   153  
   154  	return ""
   155  }
   156  
   157  // eip查询接口未返回 绑定的实例ID/网卡port id。导致不能正常找出关联的主机
   158  func (self *SEip) GetAssociationExternalId() string {
   159  	vms, err := self.region.GetVMs()
   160  	if err != nil {
   161  		log.Errorf("SEip.GetAssociationExternalId.GetVMs %s", err)
   162  		return ""
   163  	}
   164  
   165  	for i := range vms {
   166  		vm := vms[i]
   167  		nics, err := self.region.GetNics(vm.GetId())
   168  		if err != nil {
   169  			log.Errorf("SEip.GetAssociationExternalId.GetNics %s", err)
   170  			return ""
   171  		}
   172  
   173  		for _, nic := range nics {
   174  			if nic.PortID == self.PortID {
   175  				return vm.GetGlobalId()
   176  			}
   177  		}
   178  	}
   179  
   180  	return ""
   181  }
   182  
   183  // http://ctyun-api-url/apiproxy/v3/queryNetworkDetail
   184  func (self *SEip) GetBandwidth() int {
   185  	return self.BandwidthSize
   186  }
   187  
   188  func (self *SEip) GetInternetChargeType() string {
   189  	// todo: fix me
   190  	return ""
   191  }
   192  
   193  func (self *SEip) Delete() error {
   194  	return self.region.DeleteEip(self.GetId())
   195  }
   196  
   197  func (self *SEip) Associate(conf *cloudprovider.AssociateConfig) error {
   198  	nics, err := self.region.GetNics(conf.InstanceId)
   199  	if err != nil {
   200  		return errors.Wrap(err, "Eip.Associate.GetNics")
   201  	}
   202  
   203  	if len(nics) == 0 {
   204  		return errors.Wrap(fmt.Errorf("no network card found"), "Eip.Associate.GetINics")
   205  	}
   206  
   207  	return self.region.AssociateEip(self.GetId(), nics[0].PortID)
   208  }
   209  
   210  func (self *SEip) Dissociate() error {
   211  	err := self.region.DissociateEip(self.GetId())
   212  	if err != nil {
   213  		return err
   214  	}
   215  
   216  	err = cloudprovider.WaitStatusWithDelay(self, api.EIP_STATUS_READY, 5*time.Second, 10*time.Second, 180*time.Second)
   217  	return errors.Wrap(err, "SEip.Dissociate")
   218  }
   219  
   220  func (self *SEip) ChangeBandwidth(bw int) error {
   221  	return self.region.ChangeBandwidthEip(self.GetName(), self.GetId(), strconv.Itoa(bw))
   222  }
   223  
   224  type Profile struct {
   225  	OrderID   string `json:"order_id"`
   226  	RegionID  string `json:"region_id"`
   227  	UserID    string `json:"user_id"`
   228  	ProductID string `json:"product_id"`
   229  }
   230  
   231  func (self *SRegion) GetEips() ([]SEip, error) {
   232  	params := map[string]string{
   233  		"regionId": self.GetId(),
   234  	}
   235  
   236  	eips := make([]SEip, 0)
   237  	resp, err := self.client.DoGet("/apiproxy/v3/ondemand/queryIps", params)
   238  	if err != nil {
   239  		return nil, errors.Wrap(err, "SRegion.GetEips.DoGet")
   240  	}
   241  
   242  	err = resp.Unmarshal(&eips, "returnObj", "publicips")
   243  	if err != nil {
   244  		return nil, errors.Wrap(err, "SRegion.GetEips.Unmarshal")
   245  	}
   246  
   247  	for i := range eips {
   248  		eips[i].region = self
   249  	}
   250  
   251  	return eips, nil
   252  }
   253  
   254  func (self *SRegion) GetEip(eipId string) (*SEip, error) {
   255  	params := map[string]string{
   256  		"regionId":   self.GetId(),
   257  		"publicIpId": eipId,
   258  	}
   259  
   260  	eips := make([]SEip, 0)
   261  	resp, err := self.client.DoGet("/apiproxy/v3/ondemand/queryIps", params)
   262  	if err != nil {
   263  		return nil, errors.Wrap(err, "SRegion.GetEip.DoGet")
   264  	}
   265  
   266  	err = resp.Unmarshal(&eips, "returnObj", "publicips")
   267  	if err != nil {
   268  		return nil, errors.Wrap(err, "SRegion.GetEip.Unmarshal")
   269  	}
   270  
   271  	if len(eips) == 0 {
   272  		return nil, errors.Wrap(cloudprovider.ErrNotFound, "SRegion.GetEip")
   273  	} else if len(eips) == 1 {
   274  		eips[0].region = self
   275  		return &eips[0], nil
   276  	} else {
   277  		return nil, errors.Wrap(cloudprovider.ErrDuplicateId, "SRegion.GetEip")
   278  	}
   279  }
   280  
   281  func (self *SRegion) CreateEip(zoneId, name, size, shareType, chargeType string) (*SEip, error) {
   282  	eipParams := jsonutils.NewDict()
   283  	eipParams.Set("regionId", jsonutils.NewString(self.GetId()))
   284  	eipParams.Set("zoneId", jsonutils.NewString(zoneId))
   285  	eipParams.Set("name", jsonutils.NewString(name))
   286  	eipParams.Set("type", jsonutils.NewString("5_telcom"))
   287  	eipParams.Set("size", jsonutils.NewString(size))
   288  	eipParams.Set("shareType", jsonutils.NewString(shareType))
   289  	eipParams.Set("chargeMode", jsonutils.NewString(chargeType))
   290  
   291  	params := map[string]jsonutils.JSONObject{
   292  		"createIpInfo": eipParams,
   293  	}
   294  
   295  	resp, err := self.client.DoPost("/apiproxy/v3/ondemand/createIp", params)
   296  	if err != nil {
   297  		return nil, errors.Wrap(err, "SRegion.CreateEip.DoPost")
   298  	}
   299  
   300  	eipId, err := resp.GetString("returnObj", "id")
   301  	if err != nil {
   302  		return nil, errors.Wrap(err, "SRegion.CreateEip.GetEipId")
   303  	}
   304  
   305  	return self.GetEip(eipId)
   306  }
   307  
   308  func (self *SRegion) DeleteEip(publicIpId string) error {
   309  	params := map[string]jsonutils.JSONObject{
   310  		"regionId":   jsonutils.NewString(self.GetId()),
   311  		"publicIpId": jsonutils.NewString(publicIpId),
   312  	}
   313  
   314  	resp, err := self.client.DoPost("/apiproxy/v3/ondemand/deleteIp", params)
   315  	if err != nil {
   316  		return errors.Wrap(err, "SRegion.DeleteEip.DoPost")
   317  	}
   318  
   319  	var ok bool
   320  	err = resp.Unmarshal(&ok, "returnObj")
   321  	if !ok {
   322  		msg, _ := resp.GetString("message")
   323  		return errors.Wrap(fmt.Errorf(msg), "SRegion.DeleteEip.JobFailed")
   324  	}
   325  
   326  	return nil
   327  }
   328  
   329  // 这里networkCardId 实际指的是port id
   330  func (self *SRegion) AssociateEip(publicIpId, networkCardId string) error {
   331  	params := map[string]jsonutils.JSONObject{
   332  		"regionId":      jsonutils.NewString(self.GetId()),
   333  		"publicIpId":    jsonutils.NewString(publicIpId),
   334  		"networkCardId": jsonutils.NewString(networkCardId),
   335  	}
   336  
   337  	_, err := self.client.DoPost("/apiproxy/v3/ondemand/bindIp", params)
   338  	if err != nil {
   339  		return errors.Wrap(err, "SRegion.AssociateEip.DoPost")
   340  	}
   341  
   342  	return nil
   343  }
   344  
   345  func (self *SRegion) DissociateEip(publicIpId string) error {
   346  	params := map[string]jsonutils.JSONObject{
   347  		"regionId":   jsonutils.NewString(self.GetId()),
   348  		"publicIpId": jsonutils.NewString(publicIpId),
   349  	}
   350  
   351  	_, err := self.client.DoPost("/apiproxy/v3/ondemand/unbindIp", params)
   352  	if err != nil {
   353  		return errors.Wrap(err, "SRegion.DissociateEip.DoPost")
   354  	}
   355  
   356  	return nil
   357  }
   358  
   359  type SBandwidth struct {
   360  	PublicipInfo        []PublicipInfo `json:"publicip_info"`
   361  	EnterpriseProjectID string         `json:"enterprise_project_id"`
   362  	Name                string         `json:"name"`
   363  	ID                  string         `json:"id"`
   364  	ShareType           string         `json:"share_type"`
   365  	Size                int64          `json:"size"`
   366  	TenantID            string         `json:"tenant_id"`
   367  	ChargeMode          string         `json:"charge_mode"`
   368  	BandwidthType       string         `json:"bandwidth_type"`
   369  }
   370  
   371  type PublicipInfo struct {
   372  	PublicipType    string `json:"publicip_type"`
   373  	PublicipAddress string `json:"publicip_address"`
   374  	IPVersion       int64  `json:"ip_version"`
   375  	PublicipID      string `json:"publicip_id"`
   376  }
   377  
   378  func (self *SRegion) ChangeBandwidthEip(name, publicIpId, sizeMb string) error {
   379  	params := map[string]jsonutils.JSONObject{
   380  		"regionId":   jsonutils.NewString(self.GetId()),
   381  		"publicIpId": jsonutils.NewString(publicIpId),
   382  		"name":       jsonutils.NewString(name),
   383  		"size":       jsonutils.NewString(sizeMb),
   384  	}
   385  
   386  	resp, err := self.client.DoPost("/apiproxy/v3/ondemand/upgradeNetwork", params)
   387  	if err != nil {
   388  		return errors.Wrap(err, "SRegion.ChangeBandwidthEip.DoPost")
   389  	}
   390  
   391  	bandwidth := SBandwidth{}
   392  	err = resp.Unmarshal(&bandwidth, "returnObj")
   393  	if err != nil {
   394  		return errors.Wrap(err, "SRegion.ChangeBandwidthEip.Unmarshal")
   395  	}
   396  
   397  	return nil
   398  }