yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/huawei/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 huawei 16 17 import ( 18 "fmt" 19 "net/url" 20 "strings" 21 "time" 22 23 "yunion.io/x/jsonutils" 24 "yunion.io/x/log" 25 26 billing_api "yunion.io/x/cloudmux/pkg/apis/billing" 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("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 HuaweiTags 72 73 Alias string 74 ID string `json:"id"` 75 Status string `json:"status"` 76 Profile *SProfile `json:"profile,omitempty"` 77 Type string `json:"type"` 78 PublicIPAddress string `json:"public_ip_address"` 79 PrivateIPAddress string `json:"private_ip_address"` 80 TenantID string `json:"tenant_id"` 81 CreateTime time.Time `json:"create_time"` 82 BandwidthID string `json:"bandwidth_id"` 83 BandwidthShareType string `json:"bandwidth_share_type"` 84 BandwidthSize int64 `json:"bandwidth_size"` 85 BandwidthName string `json:"bandwidth_name"` 86 EnterpriseProjectID string `json:"enterprise_project_id"` 87 IPVersion int64 `json:"ip_version"` 88 PortId string `json:"port_id"` 89 EnterpriseProjectId string 90 } 91 92 func (self *SEipAddress) GetId() string { 93 return self.ID 94 } 95 96 func (self *SEipAddress) GetName() string { 97 if len(self.Alias) > 0 { 98 return self.Alias 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 if strings.HasPrefix(port.DeviceOwner, "compute") { 178 return api.EIP_ASSOCIATE_TYPE_SERVER 179 } 180 181 switch port.DeviceOwner { 182 case "neutron:LOADBALANCER", "neutron:LOADBALANCERV2": 183 return api.EIP_ASSOCIATE_TYPE_LOADBALANCER 184 case "network:nat_gateway": 185 return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY 186 default: 187 return port.DeviceOwner 188 } 189 } 190 191 func (self *SEipAddress) GetAssociationExternalId() string { 192 // network/0273a359d61847fc83405926c958c746/ext-floatingips?tenantId=0273a359d61847fc83405926c958c746&limit=2000 193 // 只能通过 port id 反查device id. 194 if len(self.PortId) > 0 { 195 port, _ := self.region.GetPort(self.PortId) 196 return port.DeviceID 197 } 198 return "" 199 } 200 201 func (self *SEipAddress) GetBandwidth() int { 202 return int(self.BandwidthSize) // Mb 203 } 204 205 func (self *SEipAddress) GetINetworkId() string { 206 return "" 207 } 208 209 func (self *SEipAddress) GetInternetChargeType() string { 210 // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090603.html 211 bandwidth, err := self.region.GetEipBandwidth(self.BandwidthID) 212 if err != nil { 213 return api.EIP_CHARGE_TYPE_BY_TRAFFIC 214 } 215 if bandwidth.ChargeMode == "traffic" { 216 return api.EIP_CHARGE_TYPE_BY_TRAFFIC 217 } 218 return api.EIP_CHARGE_TYPE_BY_BANDWIDTH 219 } 220 221 func (self *SEipAddress) GetBillingType() string { 222 if self.Profile == nil { 223 return billing_api.BILLING_TYPE_POSTPAID 224 } 225 return billing_api.BILLING_TYPE_PREPAID 226 } 227 228 func (self *SEipAddress) GetCreatedAt() time.Time { 229 return self.CreateTime 230 } 231 232 func (self *SEipAddress) GetExpiredAt() time.Time { 233 return time.Time{} 234 } 235 236 func (self *SEipAddress) Delete() error { 237 return self.region.DeallocateEIP(self.ID) 238 } 239 240 func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error { 241 portId, err := self.region.GetInstancePortId(conf.InstanceId) 242 if err != nil { 243 return err 244 } 245 246 if len(self.PortId) > 0 { 247 if self.PortId == portId { 248 return nil 249 } 250 251 return fmt.Errorf("eip %s aready associate with port %s", self.GetId(), self.PortId) 252 } 253 254 err = self.region.AssociateEipWithPortId(self.ID, portId) 255 if err != nil { 256 return err 257 } 258 259 err = cloudprovider.WaitStatusWithDelay(self, api.EIP_STATUS_READY, 10*time.Second, 10*time.Second, 180*time.Second) 260 return err 261 } 262 263 func (self *SEipAddress) Dissociate() error { 264 err := self.region.DissociateEip(self.ID) 265 if err != nil { 266 return err 267 } 268 return cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second) 269 } 270 271 func (self *SEipAddress) ChangeBandwidth(bw int) error { 272 return self.region.UpdateEipBandwidth(self.BandwidthID, bw) 273 } 274 275 func (self *SRegion) GetInstancePortId(instanceId string) (string, error) { 276 // 目前只绑定一个网卡 277 // todo: 还需要按照ports状态进行过滤 278 ports, err := self.GetPorts(instanceId) 279 if err != nil { 280 return "", err 281 } 282 283 if len(ports) == 0 { 284 return "", fmt.Errorf("AssociateEip instance %s port is empty", instanceId) 285 } 286 287 return ports[0].ID, nil 288 } 289 290 // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090596.html 291 func (self *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternetChargeType, bgpType string, projectId string) (*SEipAddress, error) { 292 params := map[string]interface{}{ 293 "bandwidth": map[string]interface{}{ 294 "name": name, 295 "size": bwMbps, 296 "share_type": "PER", 297 "charge_mode": chargeType, 298 }, 299 "publicip": map[string]interface{}{ 300 "type": bgpType, 301 "ip_version": 4, 302 "alias": name, 303 }, 304 } 305 if len(projectId) > 0 { 306 params["enterprise_project_id"] = projectId 307 } 308 resp, err := self.vpcCreate("publicips", params) 309 if err != nil { 310 return nil, err 311 } 312 eip := &SEipAddress{region: self} 313 return eip, resp.Unmarshal(eip, "publicip") 314 } 315 316 func (self *SRegion) GetEip(eipId string) (*SEipAddress, error) { 317 resp, err := self.vpcGet("publicips/" + eipId) 318 if err != nil { 319 return nil, err 320 } 321 eip := &SEipAddress{region: self} 322 return eip, resp.Unmarshal(eip, "publicip") 323 } 324 325 func (self *SRegion) DeallocateEIP(eipId string) error { 326 _, err := self.vpcDelete("publicips/" + eipId) 327 return err 328 } 329 330 func (self *SRegion) AssociateEip(eipId string, instanceId string) error { 331 portId, err := self.GetInstancePortId(instanceId) 332 if err != nil { 333 return err 334 } 335 return self.AssociateEipWithPortId(eipId, portId) 336 } 337 338 func (self *SRegion) AssociateEipWithPortId(eipId string, portId string) error { 339 params := map[string]interface{}{ 340 "publicip": map[string]interface{}{ 341 "port_id": portId, 342 }, 343 } 344 _, err := self.vpcUpdate("publicips/"+eipId, params) 345 return err 346 } 347 348 func (self *SRegion) DissociateEip(eipId string) error { 349 return self.AssociateEipWithPortId(eipId, "") 350 } 351 352 func (self *SRegion) UpdateEipBandwidth(bandwidthId string, bw int) error { 353 params := map[string]interface{}{ 354 "bandwidth": map[string]interface{}{ 355 "size": bw, 356 }, 357 } 358 _, err := self.vpcUpdate("bandwidths/"+bandwidthId, params) 359 return err 360 } 361 362 func (self *SRegion) GetEipBandwidth(id string) (*Bandwidth, error) { 363 resp, err := self.vpcGet("bandwidths/" + id) 364 if err != nil { 365 return nil, err 366 } 367 ret := &Bandwidth{} 368 return ret, resp.Unmarshal(ret, "bandwidth") 369 } 370 371 func (self *SEipAddress) GetProjectId() string { 372 return self.EnterpriseProjectId 373 } 374 375 func (self *SRegion) GetEips(portId string, addrs []string) ([]SEipAddress, error) { 376 query := url.Values{} 377 for _, addr := range addrs { 378 query.Add("public_ip_address", addr) 379 } 380 if len(portId) > 0 { 381 query.Set("port_id", portId) 382 } 383 resp, err := self.vpcList("publicips", query) 384 if err != nil { 385 return nil, err 386 } 387 eips := []SEipAddress{} 388 err = resp.Unmarshal(&eips, "publicips") 389 if err != nil { 390 return nil, err 391 } 392 for i := range eips { 393 eips[i].region = self 394 } 395 return eips, nil 396 }