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