yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aliyun/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 aliyun 16 17 import ( 18 "fmt" 19 "strings" 20 "time" 21 22 "yunion.io/x/jsonutils" 23 "yunion.io/x/log" 24 "yunion.io/x/pkg/errors" 25 "yunion.io/x/pkg/utils" 26 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("PayByTraffic") 36 InternetChargeByBandwidth = TInternetChargeType("PayByBandwidth") 37 ) 38 39 const ( 40 EIP_STATUS_ASSOCIATING = "Associating" 41 EIP_STATUS_UNASSOCIATING = "Unassociating" 42 EIP_STATUS_INUSE = "InUse" 43 EIP_STATUS_AVAILABLE = "Available" 44 45 EIP_OPERATION_LOCK_FINANCIAL = "financial" 46 EIP_OPERATION_LOCK_SECURITY = "security" 47 48 EIP_INSTANCE_TYPE_ECS = "EcsInstance" // (默认值):VPC类型的ECS实例 49 EIP_INTANNCE_TYPE_SLB = "SlbInstance" // :VPC类型的SLB实例 50 EIP_INSTANCE_TYPE_NAT = "Nat" // :NAT网关 51 EIP_INSTANCE_TYPE_HAVIP = "HaVip" // :HAVIP 52 ) 53 54 /* 55 { 56 "AllocationId":"eip-2zeddtan63ou44dtyt9s3", 57 "AllocationTime":"2019-02-23T06:48:36Z", 58 "Bandwidth":"100", 59 "ChargeType":"PostPaid", 60 "ExpiredTime":"", 61 "InstanceId":"", 62 "InstanceType":"", 63 "InternetChargeType":"PayByTraffic", 64 "IpAddress":"39.105.131.32", 65 "OperationLocks":{"LockReason":[]}, 66 "RegionId":"cn-beijing", 67 "Status":"Available" 68 } 69 */ 70 71 type SEipAddress struct { 72 region *SRegion 73 multicloud.SEipBase 74 AliyunTags 75 76 Name string 77 AllocationId string 78 79 InternetChargeType TInternetChargeType 80 81 IpAddress string 82 Status string 83 84 InstanceType string 85 InstanceId string 86 Bandwidth int /* Mbps */ 87 88 BusinessStatus string 89 AllocationTime time.Time 90 DeletionProtection bool 91 Descritpion string 92 ISP string 93 Mode string 94 Netmode string 95 96 OperationLocks string 97 98 ChargeType TChargeType 99 ExpiredTime time.Time 100 ResourceGroupId string 101 } 102 103 func (self *SEipAddress) GetId() string { 104 return self.AllocationId 105 } 106 107 func (self *SEipAddress) GetName() string { 108 if len(self.Name) > 0 { 109 return self.Name 110 } 111 return self.IpAddress 112 } 113 114 func (self *SEipAddress) GetGlobalId() string { 115 return self.AllocationId 116 } 117 118 func (self *SEipAddress) GetStatus() string { 119 switch self.Status { 120 case EIP_STATUS_AVAILABLE, EIP_STATUS_INUSE: 121 return api.EIP_STATUS_READY 122 case EIP_STATUS_ASSOCIATING: 123 return api.EIP_STATUS_ASSOCIATE 124 case EIP_STATUS_UNASSOCIATING: 125 return api.EIP_STATUS_DISSOCIATE 126 default: 127 return api.EIP_STATUS_UNKNOWN 128 } 129 } 130 131 func (self *SEipAddress) Refresh() error { 132 if self.IsEmulated() { 133 return nil 134 } 135 new, err := self.region.GetEip(self.AllocationId) 136 if err != nil { 137 return err 138 } 139 return jsonutils.Update(self, new) 140 } 141 142 func (self *SEipAddress) IsEmulated() bool { 143 if self.AllocationId == self.InstanceId { 144 // fixed Public IP 145 return true 146 } else { 147 return false 148 } 149 } 150 151 func (self *SEipAddress) GetIpAddr() string { 152 return self.IpAddress 153 } 154 155 func (self *SEipAddress) GetMode() string { 156 if self.InstanceId == self.AllocationId { 157 return api.EIP_MODE_INSTANCE_PUBLICIP 158 } else { 159 return api.EIP_MODE_STANDALONE_EIP 160 } 161 } 162 163 func (self *SEipAddress) GetAssociationType() string { 164 switch self.InstanceType { 165 case EIP_INSTANCE_TYPE_ECS, "NetworkInterface": 166 return api.EIP_ASSOCIATE_TYPE_SERVER 167 case EIP_INSTANCE_TYPE_NAT: 168 return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY 169 case EIP_INTANNCE_TYPE_SLB: 170 return api.EIP_ASSOCIATE_TYPE_LOADBALANCER 171 default: 172 return self.InstanceType 173 } 174 } 175 176 func (self *SEipAddress) GetAssociationExternalId() string { 177 return self.InstanceId 178 } 179 180 func (self *SEipAddress) GetBillingType() string { 181 return convertChargeType(self.ChargeType) 182 } 183 184 func (self *SEipAddress) GetCreatedAt() time.Time { 185 return self.AllocationTime 186 } 187 188 func (self *SEipAddress) GetExpiredAt() time.Time { 189 return convertExpiredAt(self.ExpiredTime) 190 } 191 192 func (self *SEipAddress) Delete() error { 193 return self.region.DeallocateEIP(self.AllocationId) 194 } 195 196 func (self *SEipAddress) GetBandwidth() int { 197 return self.Bandwidth 198 } 199 200 func (self *SEipAddress) GetINetworkId() string { 201 return "" 202 } 203 204 func (self *SEipAddress) GetInternetChargeType() string { 205 switch self.InternetChargeType { 206 case InternetChargeByTraffic: 207 return api.EIP_CHARGE_TYPE_BY_TRAFFIC 208 case InternetChargeByBandwidth: 209 return api.EIP_CHARGE_TYPE_BY_BANDWIDTH 210 default: 211 return api.EIP_CHARGE_TYPE_BY_TRAFFIC 212 } 213 } 214 215 func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error { 216 err := cloudprovider.Wait(20*time.Second, 60*time.Second, func() (bool, error) { 217 err := self.region.AssociateEip(self.AllocationId, conf.InstanceId) 218 if err != nil { 219 if isError(err, "IncorrectInstanceStatus") { 220 return false, nil 221 } 222 return false, errors.Wrap(err, "region.AssociateEip") 223 } 224 return true, nil 225 }) 226 err = cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second) 227 return err 228 } 229 230 func (self *SEipAddress) Dissociate() error { 231 err := self.region.DissociateEip(self.AllocationId, self.InstanceId) 232 if err != nil { 233 return err 234 } 235 err = cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second) 236 return err 237 } 238 239 func (self *SEipAddress) ChangeBandwidth(bw int) error { 240 return self.region.UpdateEipBandwidth(self.AllocationId, bw) 241 } 242 243 func (region *SRegion) GetEips(eipId string, associatedId, addr string, offset int, limit int) ([]SEipAddress, int, error) { 244 if limit > 50 || limit <= 0 { 245 limit = 50 246 } 247 248 params := make(map[string]string) 249 params["RegionId"] = region.RegionId 250 params["PageSize"] = fmt.Sprintf("%d", limit) 251 params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1) 252 if len(addr) > 0 { 253 params["EipAddress"] = addr 254 } 255 256 if len(eipId) > 0 { 257 params["AllocationId"] = eipId 258 } 259 260 if len(associatedId) > 0 { 261 params["AssociatedInstanceId"] = associatedId 262 for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "ngw-": "Nat", "lb-": "SlbInstance"} { 263 if strings.HasPrefix(associatedId, prefix) { 264 params["AssociatedInstanceType"] = instanceType 265 } 266 } 267 } 268 269 body, err := region.vpcRequest("DescribeEipAddresses", params) 270 if err != nil { 271 log.Errorf("DescribeEipAddresses fail %s", err) 272 return nil, 0, err 273 } 274 275 eips := make([]SEipAddress, 0) 276 err = body.Unmarshal(&eips, "EipAddresses", "EipAddress") 277 if err != nil { 278 log.Errorf("Unmarshal EipAddress details fail %s", err) 279 return nil, 0, err 280 } 281 total, _ := body.Int("TotalCount") 282 for i := 0; i < len(eips); i += 1 { 283 eips[i].region = region 284 } 285 return eips, int(total), nil 286 } 287 288 func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) { 289 eips, total, err := region.GetEips(eipId, "", "", 0, 1) 290 if err != nil { 291 return nil, err 292 } 293 if total != 1 { 294 return nil, cloudprovider.ErrNotFound 295 } 296 return &eips[0], nil 297 } 298 299 func (region *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternetChargeType, projectId string) (*SEipAddress, error) { 300 params := make(map[string]string) 301 302 if len(name) > 0 { 303 params["Name"] = name 304 } 305 params["Bandwidth"] = fmt.Sprintf("%d", bwMbps) 306 params["InternetChargeType"] = string(chargeType) 307 params["InstanceChargeType"] = "PostPaid" 308 params["ClientToken"] = utils.GenRequestId(20) 309 if len(projectId) > 0 { 310 params["ResourceGroupId"] = projectId 311 } 312 313 body, err := region.vpcRequest("AllocateEipAddress", params) 314 if err != nil { 315 log.Errorf("AllocateEipAddress fail %s", err) 316 return nil, err 317 } 318 319 eipId, err := body.GetString("AllocationId") 320 if err != nil { 321 log.Errorf("fail to get AllocationId after EIP allocation??? %s", err) 322 return nil, err 323 } 324 325 return region.GetEip(eipId) 326 } 327 328 func (region *SRegion) CreateEIP(opts *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { 329 var ctype TInternetChargeType 330 switch opts.ChargeType { 331 case api.EIP_CHARGE_TYPE_BY_TRAFFIC: 332 ctype = InternetChargeByTraffic 333 case api.EIP_CHARGE_TYPE_BY_BANDWIDTH: 334 ctype = InternetChargeByBandwidth 335 } 336 return region.AllocateEIP(opts.Name, opts.BandwidthMbps, ctype, opts.ProjectId) 337 } 338 339 func (region *SRegion) DeallocateEIP(eipId string) error { 340 params := make(map[string]string) 341 params["AllocationId"] = eipId 342 343 _, err := region.vpcRequest("ReleaseEipAddress", params) 344 if err != nil { 345 log.Errorf("ReleaseEipAddress fail %s", err) 346 } 347 return err 348 } 349 350 func (region *SRegion) AssociateEip(eipId string, instanceId string) error { 351 params := make(map[string]string) 352 params["AllocationId"] = eipId 353 params["InstanceId"] = instanceId 354 for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "lb-": "SlbInstance", "ngw-": "Nat"} { 355 if strings.HasPrefix(instanceId, prefix) { 356 params["InstanceType"] = instanceType 357 } 358 } 359 360 _, err := region.vpcRequest("AssociateEipAddress", params) 361 return errors.Wrapf(err, "AssociateEipAddress") 362 } 363 364 func (region *SRegion) DissociateEip(eipId string, instanceId string) error { 365 params := make(map[string]string) 366 params["AllocationId"] = eipId 367 params["InstanceId"] = instanceId 368 for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "lb-": "SlbInstance", "ngw-": "Nat"} { 369 if strings.HasPrefix(instanceId, prefix) { 370 params["InstanceType"] = instanceType 371 } 372 } 373 374 _, err := region.vpcRequest("UnassociateEipAddress", params) 375 if err != nil { 376 log.Errorf("UnassociateEipAddress fail %s", err) 377 } 378 return err 379 } 380 381 func (region *SRegion) UpdateEipBandwidth(eipId string, bw int) error { 382 params := make(map[string]string) 383 params["AllocationId"] = eipId 384 params["Bandwidth"] = fmt.Sprintf("%d", bw) 385 386 _, err := region.vpcRequest("ModifyEipAddressAttribute", params) 387 if err != nil { 388 log.Errorf("ModifyEipAddressAttribute fail %s", err) 389 } 390 return err 391 } 392 393 func (self *SEipAddress) GetProjectId() string { 394 return self.ResourceGroupId 395 }