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 }