yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/azure/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 azure
    16  
    17  import (
    18  	"fmt"
    19  	"net/url"
    20  	"strings"
    21  	"time"
    22  
    23  	"yunion.io/x/jsonutils"
    24  	"yunion.io/x/log"
    25  	"yunion.io/x/pkg/errors"
    26  	"yunion.io/x/pkg/utils"
    27  
    28  	billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
    29  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    30  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    31  	"yunion.io/x/cloudmux/pkg/multicloud"
    32  )
    33  
    34  type PublicIPAddressSku struct {
    35  	Name string
    36  }
    37  
    38  type IPConfigurationPropertiesFormat struct {
    39  	PrivateIPAddress string
    40  }
    41  
    42  type IPConfiguration struct {
    43  	Name string
    44  	ID   string
    45  }
    46  
    47  type PublicIPAddressPropertiesFormat struct {
    48  	PublicIPAddressVersion   string          `json:"publicIPAddressVersion,omitempty"`
    49  	IPAddress                string          `json:"ipAddress,omitempty"`
    50  	PublicIPAllocationMethod string          `json:"publicIPAllocationMethod,omitempty"`
    51  	ProvisioningState        string          `json:"provisioningState,omitempty"`
    52  	IPConfiguration          IPConfiguration `json:"ipConfiguration,omitempty"`
    53  }
    54  
    55  type SEipAddress struct {
    56  	region *SRegion
    57  	multicloud.SEipBase
    58  	AzureTags
    59  
    60  	ID         string
    61  	Name       string
    62  	Location   string
    63  	Properties PublicIPAddressPropertiesFormat `json:"properties,omitempty"`
    64  	Type       string
    65  	Sku        *PublicIPAddressSku
    66  }
    67  
    68  func (self *SRegion) AllocateEIP(name, projectId string) (*SEipAddress, error) {
    69  	params := map[string]interface{}{
    70  		"Location": self.Name,
    71  		"Name":     name,
    72  		"Properties": map[string]string{
    73  			"PublicIPAddressVersion":   "IPv4",
    74  			"PublicIPAllocationMethod": "Static",
    75  		},
    76  		"Type": "Microsoft.Network/publicIPAddresses",
    77  	}
    78  	eip := &SEipAddress{region: self}
    79  	err := self.create(projectId, jsonutils.Marshal(params), eip)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	return eip, cloudprovider.WaitStatus(eip, api.EIP_STATUS_READY, 10*time.Second, 300*time.Second)
    84  }
    85  
    86  func (region *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) {
    87  	return region.AllocateEIP(eip.Name, eip.ProjectId)
    88  }
    89  
    90  func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) {
    91  	eip := SEipAddress{region: region}
    92  	return &eip, region.get(eipId, url.Values{}, &eip)
    93  }
    94  
    95  func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error {
    96  	return self.region.AssociateEip(self.ID, conf.InstanceId)
    97  }
    98  
    99  func (region *SRegion) AssociateEip(eipId string, instanceId string) error {
   100  	instance, err := region.GetInstance(instanceId)
   101  	if err != nil {
   102  		return err
   103  	}
   104  	if len(instance.Properties.NetworkProfile.NetworkInterfaces) > 0 {
   105  		nic, err := region.GetNetworkInterface(instance.Properties.NetworkProfile.NetworkInterfaces[0].ID)
   106  		if err != nil {
   107  			return err
   108  		}
   109  		if len(nic.Properties.IPConfigurations) > 0 {
   110  			nic.Properties.IPConfigurations[0].Properties.PublicIPAddress = &PublicIPAddress{ID: eipId}
   111  			return region.update(jsonutils.Marshal(nic), nil)
   112  		}
   113  		return fmt.Errorf("network interface with no IPConfigurations")
   114  	}
   115  	return fmt.Errorf("Instance with no interface")
   116  }
   117  
   118  func (region *SRegion) GetIEipById(eipId string) (cloudprovider.ICloudEIP, error) {
   119  	return region.GetEip(eipId)
   120  }
   121  
   122  func (self *SEipAddress) ChangeBandwidth(bw int) error {
   123  	return cloudprovider.ErrNotSupported
   124  }
   125  
   126  func (self *SEipAddress) Delete() error {
   127  	self.Dissociate() //避免eip挂载在弹性网卡之上,导致删除失败
   128  	return self.region.DeallocateEIP(self.ID)
   129  }
   130  
   131  func (region *SRegion) DeallocateEIP(eipId string) error {
   132  	return cloudprovider.Wait(time.Second*5, time.Minute*5, func() (bool, error) {
   133  		err := region.del(eipId)
   134  		if err == nil {
   135  			return true, nil
   136  		}
   137  		// {"error":{"code":"PublicIPAddressCannotBeDeleted","details":[],"message":"Public IP address /subscriptions/d4f0ec08-3e28-4ae5-bdf9-3dc7c5b0eeca/resourceGroups/Default/providers/Microsoft.Network/publicIPAddresses/eip-for-test-wwl can not be deleted since it is still allocated to resource /subscriptions/d4f0ec08-3e28-4ae5-bdf9-3dc7c5b0eeca/resourceGroups/Default/providers/Microsoft.Network/networkInterfaces/test-wwl-ipconfig."}}
   138  		// 刚解绑eip后可能数据未刷新,需要再次尝试
   139  		if strings.Contains(err.Error(), "it is still allocated to resource") {
   140  			return false, nil
   141  		}
   142  		return false, errors.Wrapf(err, "del(%s)", eipId)
   143  	})
   144  }
   145  
   146  func (self *SEipAddress) Dissociate() error {
   147  	return self.region.DissociateEip(self.ID)
   148  }
   149  
   150  func (region *SRegion) DissociateEip(eipId string) error {
   151  	eip, err := region.GetEip(eipId)
   152  	if err != nil {
   153  		return errors.Wrapf(err, "GetEip(%s)", eipId)
   154  	}
   155  	interfaceId := eip.Properties.IPConfiguration.ID
   156  	if strings.Index(interfaceId, "/ipConfigurations/") > 0 {
   157  		interfaceId = strings.Split(interfaceId, "/ipConfigurations/")[0]
   158  	}
   159  	nic, err := region.GetNetworkInterface(interfaceId)
   160  	if err != nil {
   161  		return err
   162  	}
   163  	for i := 0; i < len(nic.Properties.IPConfigurations); i++ {
   164  		if nic.Properties.IPConfigurations[i].Properties.PublicIPAddress != nil && nic.Properties.IPConfigurations[i].Properties.PublicIPAddress.ID == eipId {
   165  			nic.Properties.IPConfigurations[i].Properties.PublicIPAddress = nil
   166  			break
   167  		}
   168  	}
   169  	return region.update(jsonutils.Marshal(nic), nil)
   170  }
   171  
   172  func (self *SEipAddress) GetAssociationExternalId() string {
   173  	if self.GetAssociationType() == api.EIP_ASSOCIATE_TYPE_SERVER {
   174  		info := strings.Split(self.Properties.IPConfiguration.ID, "/")
   175  		nic, _ := self.region.GetNetworkInterface(strings.Join(info[:len(info)-2], "/"))
   176  		if nic != nil {
   177  			return strings.ToLower(nic.Properties.VirtualMachine.ID)
   178  		}
   179  	}
   180  	return ""
   181  }
   182  
   183  func (self *SEipAddress) GetAssociationType() string {
   184  	if len(self.Properties.IPConfiguration.ID) == 0 {
   185  		return ""
   186  	}
   187  	if info := strings.Split(self.Properties.IPConfiguration.ID, "/"); len(info) > 7 {
   188  		resType := strings.ToLower(info[7])
   189  		if utils.IsInStringArray(resType, []string{"networkinterfaces"}) {
   190  			return api.EIP_ASSOCIATE_TYPE_SERVER
   191  		}
   192  		return resType
   193  	}
   194  	return ""
   195  }
   196  
   197  func (self *SEipAddress) GetBandwidth() int {
   198  	return 0
   199  }
   200  
   201  func (self *SEipAddress) GetINetworkId() string {
   202  	return ""
   203  }
   204  
   205  func (self *SEipAddress) GetGlobalId() string {
   206  	return strings.ToLower(self.ID)
   207  }
   208  
   209  func (self *SEipAddress) GetId() string {
   210  	return self.ID
   211  }
   212  
   213  func (self *SEipAddress) GetInternetChargeType() string {
   214  	return api.EIP_CHARGE_TYPE_BY_TRAFFIC
   215  }
   216  
   217  func (self *SEipAddress) GetIpAddr() string {
   218  	return self.Properties.IPAddress
   219  }
   220  
   221  func (self *SEipAddress) GetMode() string {
   222  	if self.IsEmulated() {
   223  		return api.EIP_MODE_INSTANCE_PUBLICIP
   224  	}
   225  	return api.EIP_MODE_STANDALONE_EIP
   226  }
   227  
   228  func (self *SEipAddress) GetName() string {
   229  	return self.Name
   230  }
   231  
   232  func (self *SEipAddress) GetStatus() string {
   233  	switch self.Properties.ProvisioningState {
   234  	case "Succeeded", "":
   235  		return api.EIP_STATUS_READY
   236  	case "Updating":
   237  		return api.EIP_STATUS_ALLOCATE
   238  	default:
   239  		log.Errorf("Unknown eip status: %s", self.Properties.ProvisioningState)
   240  		return api.EIP_STATUS_UNKNOWN
   241  	}
   242  }
   243  
   244  func (self *SEipAddress) IsEmulated() bool {
   245  	if strings.ToLower(self.Properties.PublicIPAllocationMethod) == "Dynamic" || len(self.Properties.IPAddress) == 0 {
   246  		return true
   247  	}
   248  	return false
   249  }
   250  
   251  func (self *SEipAddress) Refresh() error {
   252  	eip, err := self.region.GetEip(self.ID)
   253  	if err != nil {
   254  		return err
   255  	}
   256  	return jsonutils.Update(self, eip)
   257  }
   258  
   259  func (self *SEipAddress) GetBillingType() string {
   260  	return billing_api.BILLING_TYPE_POSTPAID
   261  }
   262  
   263  func (self *SEipAddress) GetCreatedAt() time.Time {
   264  	return time.Time{}
   265  }
   266  
   267  func (self *SEipAddress) GetExpiredAt() time.Time {
   268  	return time.Time{}
   269  }
   270  
   271  func (self *SEipAddress) GetProjectId() string {
   272  	return getResourceGroup(self.ID)
   273  }