yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcso/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 hcso 16 17 import ( 18 "fmt" 19 "time" 20 21 "yunion.io/x/jsonutils" 22 "yunion.io/x/log" 23 "yunion.io/x/pkg/errors" 24 25 billing_api "yunion.io/x/cloudmux/pkg/apis/billing" 26 api "yunion.io/x/cloudmux/pkg/apis/compute" 27 "yunion.io/x/cloudmux/pkg/cloudprovider" 28 "yunion.io/x/cloudmux/pkg/multicloud" 29 "yunion.io/x/cloudmux/pkg/multicloud/huawei" 30 ) 31 32 type TInternetChargeType string 33 34 const ( 35 InternetChargeByTraffic = TInternetChargeType("traffic") 36 InternetChargeByBandwidth = TInternetChargeType("bandwidth") 37 ) 38 39 type Bandwidth struct { 40 ID string `json:"id"` 41 Name string `json:"name"` 42 Size int64 `json:"size"` 43 ShareType string `json:"share_type"` 44 PublicipInfo []PublicipInfo `json:"publicip_info"` 45 TenantID string `json:"tenant_id"` 46 BandwidthType string `json:"bandwidth_type"` 47 ChargeMode string `json:"charge_mode"` 48 BillingInfo string `json:"billing_info"` 49 EnterpriseProjectID string `json:"enterprise_project_id"` 50 } 51 52 type PublicipInfo struct { 53 PublicipID string `json:"publicip_id"` 54 PublicipAddress string `json:"publicip_address"` 55 PublicipType string `json:"publicip_type"` 56 IPVersion int64 `json:"ip_version"` 57 } 58 59 type SProfile struct { 60 UserID string `json:"user_id"` 61 ProductID string `json:"product_id"` 62 RegionID string `json:"region_id"` 63 OrderID string `json:"order_id"` 64 } 65 66 // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090598.html 67 type SEipAddress struct { 68 region *SRegion 69 port *Port 70 multicloud.SEipBase 71 huawei.HuaweiTags 72 73 ID string `json:"id"` 74 Status string `json:"status"` 75 Profile *SProfile `json:"profile,omitempty"` 76 Type string `json:"type"` 77 PublicIPAddress string `json:"public_ip_address"` 78 PrivateIPAddress string `json:"private_ip_address"` 79 TenantID string `json:"tenant_id"` 80 CreateTime time.Time `json:"create_time"` 81 BandwidthID string `json:"bandwidth_id"` 82 BandwidthShareType string `json:"bandwidth_share_type"` 83 BandwidthSize int64 `json:"bandwidth_size"` 84 BandwidthName string `json:"bandwidth_name"` 85 EnterpriseProjectID string `json:"enterprise_project_id"` 86 IPVersion int64 `json:"ip_version"` 87 PortId string `json:"port_id"` 88 EnterpriseProjectId string 89 } 90 91 func (self *SEipAddress) GetId() string { 92 return self.ID 93 } 94 95 func (self *SEipAddress) GetName() string { 96 if len(self.BandwidthName) == 0 { 97 return self.BandwidthName 98 } 99 100 return self.PublicIPAddress 101 } 102 103 func (self *SEipAddress) GetGlobalId() string { 104 return self.ID 105 } 106 107 func (self *SEipAddress) GetStatus() string { 108 // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090598.html 109 switch self.Status { 110 case "ACTIVE", "DOWN", "ELB": 111 return api.EIP_STATUS_READY 112 case "PENDING_CREATE", "NOTIFYING": 113 return api.EIP_STATUS_ALLOCATE 114 case "BINDING": 115 return api.EIP_STATUS_ALLOCATE 116 case "BIND_ERROR": 117 return api.EIP_STATUS_ALLOCATE_FAIL 118 case "PENDING_DELETE", "NOTIFY_DELETE": 119 return api.EIP_STATUS_DEALLOCATE 120 default: 121 return api.EIP_STATUS_UNKNOWN 122 } 123 } 124 125 func (self *SEipAddress) Refresh() error { 126 if self.IsEmulated() { 127 return nil 128 } 129 new, err := self.region.GetEip(self.ID) 130 if err != nil { 131 return err 132 } 133 return jsonutils.Update(self, new) 134 } 135 136 func (self *SEipAddress) IsEmulated() bool { 137 return false 138 } 139 140 func (self *SEipAddress) GetIpAddr() string { 141 return self.PublicIPAddress 142 } 143 144 func (self *SEipAddress) GetMode() string { 145 return api.EIP_MODE_STANDALONE_EIP 146 } 147 148 func (self *SEipAddress) GetPort() *Port { 149 if len(self.PortId) == 0 { 150 return nil 151 } 152 153 if self.port != nil { 154 return self.port 155 } 156 157 port, err := self.region.GetPort(self.PortId) 158 if err != nil { 159 return nil 160 } else { 161 self.port = &port 162 } 163 164 return self.port 165 } 166 167 func (self *SEipAddress) GetAssociationType() string { 168 if len(self.PortId) == 0 { 169 return "" 170 } 171 port, err := self.region.GetPort(self.PortId) 172 if err != nil { 173 log.Errorf("Get eip %s port %s error: %v", self.ID, self.PortId, err) 174 return "" 175 } 176 177 switch port.DeviceOwner { 178 case "neutron:LOADBALANCER", "neutron:LOADBALANCERV2": 179 return api.EIP_ASSOCIATE_TYPE_LOADBALANCER 180 case "network:nat_gateway": 181 return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY 182 default: 183 return port.DeviceOwner 184 } 185 } 186 187 func (self *SEipAddress) GetAssociationExternalId() string { 188 // network/0273a359d61847fc83405926c958c746/ext-floatingips?tenantId=0273a359d61847fc83405926c958c746&limit=2000 189 // 只能通过 port id 反查device id. 190 if len(self.PortId) > 0 { 191 port, _ := self.region.GetPort(self.PortId) 192 return port.DeviceID 193 } 194 return "" 195 } 196 197 func (self *SEipAddress) GetBandwidth() int { 198 return int(self.BandwidthSize) // Mb 199 } 200 201 func (self *SEipAddress) GetINetworkId() string { 202 return "" 203 } 204 205 func (self *SEipAddress) GetInternetChargeType() string { 206 // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090603.html 207 bandwidth, err := self.region.GetEipBandwidth(self.BandwidthID) 208 if err != nil { 209 return api.EIP_CHARGE_TYPE_BY_TRAFFIC 210 } 211 212 if bandwidth.ChargeMode != "traffic" { 213 return api.EIP_CHARGE_TYPE_BY_BANDWIDTH 214 } else { 215 return api.EIP_CHARGE_TYPE_BY_TRAFFIC 216 } 217 } 218 219 func (self *SEipAddress) GetBillingType() string { 220 if self.Profile == nil { 221 return billing_api.BILLING_TYPE_POSTPAID 222 } else { 223 return billing_api.BILLING_TYPE_PREPAID 224 } 225 } 226 227 func (self *SEipAddress) GetCreatedAt() time.Time { 228 return self.CreateTime 229 } 230 231 func (self *SEipAddress) GetExpiredAt() time.Time { 232 return time.Time{} 233 } 234 235 func (self *SEipAddress) Delete() error { 236 return self.region.DeallocateEIP(self.ID) 237 } 238 239 func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error { 240 portId, err := self.region.GetInstancePortId(conf.InstanceId) 241 if err != nil { 242 return err 243 } 244 245 if len(self.PortId) > 0 { 246 if self.PortId == portId { 247 return nil 248 } 249 250 return fmt.Errorf("eip %s aready associate with port %s", self.GetId(), self.PortId) 251 } 252 253 err = self.region.AssociateEipWithPortId(self.ID, portId) 254 if err != nil { 255 return err 256 } 257 258 err = cloudprovider.WaitStatusWithDelay(self, api.EIP_STATUS_READY, 10*time.Second, 10*time.Second, 180*time.Second) 259 return err 260 } 261 262 func (self *SEipAddress) Dissociate() error { 263 if len(self.PortId) == 0 { 264 return nil 265 } 266 port, err := self.region.GetPort(self.PortId) 267 if err != nil { 268 return errors.Wrapf(err, "GetPort(%s)", self.PortId) 269 } 270 271 err = self.region.DissociateEip(self.ID, port.DeviceID) 272 if err != nil { 273 return errors.Wrapf(err, "DissociateEip") 274 } 275 err = cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second) 276 return err 277 } 278 279 func (self *SEipAddress) ChangeBandwidth(bw int) error { 280 return self.region.UpdateEipBandwidth(self.BandwidthID, bw) 281 } 282 283 func (self *SRegion) GetInstancePortId(instanceId string) (string, error) { 284 // 目前只绑定一个网卡 285 // todo: 还需要按照ports状态进行过滤 286 ports, err := self.GetPorts(instanceId) 287 if err != nil { 288 return "", err 289 } 290 291 if len(ports) == 0 { 292 return "", fmt.Errorf("AssociateEip instance %s port is empty", instanceId) 293 } 294 295 return ports[0].ID, nil 296 } 297 298 // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090596.html 299 func (self *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternetChargeType, bgpType string, projectId string) (*SEipAddress, error) { 300 paramsStr := ` 301 { 302 "publicip": { 303 "type": "%s", 304 "ip_version": 4 305 }, 306 "bandwidth": { 307 "name": "%s", 308 "size": %d, 309 "share_type": "PER", 310 "charge_mode": "%s" 311 } 312 } 313 ` 314 if len(bgpType) == 0 { 315 return nil, fmt.Errorf("AllocateEIP bgp type should not be empty") 316 } 317 paramsStr = fmt.Sprintf(paramsStr, bgpType, name, bwMbps, chargeType) 318 _params, _ := jsonutils.ParseString(paramsStr) 319 params := _params.(*jsonutils.JSONDict) 320 if len(projectId) > 0 { 321 params.Set("enterprise_project_id", jsonutils.NewString(projectId)) 322 } 323 eip := SEipAddress{} 324 err := DoCreate(self.ecsClient.Eips.Create, params, &eip) 325 return &eip, err 326 } 327 328 func (self *SRegion) GetEip(eipId string) (*SEipAddress, error) { 329 var eip SEipAddress 330 err := DoGet(self.ecsClient.Eips.Get, eipId, nil, &eip) 331 eip.region = self 332 return &eip, err 333 } 334 335 func (self *SRegion) DeallocateEIP(eipId string) error { 336 _, err := self.ecsClient.Eips.Delete(eipId, nil) 337 return err 338 } 339 340 func (self *SRegion) AssociateEip(eipId string, instanceId string) error { 341 portId, err := self.GetInstancePortId(instanceId) 342 if err != nil { 343 return err 344 } 345 return self.AssociateEipWithPortId(eipId, portId) 346 } 347 348 func (self *SRegion) AssociateEipWithPortId(eipId string, portId string) error { 349 params := jsonutils.NewDict() 350 publicIPObj := jsonutils.NewDict() 351 publicIPObj.Add(jsonutils.NewString(portId), "port_id") 352 params.Add(publicIPObj, "publicip") 353 354 _, err := self.ecsClient.Eips.Update(eipId, params) 355 return err 356 } 357 358 func (self *SRegion) DissociateEip(eipId string, instanceId string) error { 359 eip, err := self.GetEip(eipId) 360 if err != nil { 361 return err 362 } 363 364 // 已经是解绑状态 365 if eip.Status == "DOWN" { 366 return nil 367 } 368 369 remoteInstanceId := eip.GetAssociationExternalId() 370 if remoteInstanceId != instanceId { 371 return fmt.Errorf("eip %s associate with another instance %s", eipId, remoteInstanceId) 372 } 373 374 paramsStr := `{"publicip":{"port_id":null}}` 375 params, _ := jsonutils.ParseString(paramsStr) 376 _, err = self.ecsClient.Eips.Update(eipId, params) 377 return err 378 } 379 380 func (self *SRegion) UpdateEipBandwidth(bandwidthId string, bw int) error { 381 paramStr := `{ 382 "bandwidth": 383 { 384 "size": %d 385 } 386 }` 387 388 paramStr = fmt.Sprintf(paramStr, bw) 389 params, _ := jsonutils.ParseString(paramStr) 390 _, err := self.ecsClient.Bandwidths.Update(bandwidthId, params) 391 return err 392 } 393 394 func (self *SRegion) GetEipBandwidth(bandwidthId string) (Bandwidth, error) { 395 bandwidth := Bandwidth{} 396 err := DoGet(self.ecsClient.Bandwidths.Get, bandwidthId, nil, &bandwidth) 397 return bandwidth, err 398 } 399 400 func (self *SEipAddress) GetProjectId() string { 401 return self.EnterpriseProjectId 402 }