yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/openstack/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 openstack 16 17 import ( 18 "fmt" 19 "net/url" 20 "time" 21 22 "yunion.io/x/jsonutils" 23 "yunion.io/x/log" 24 "yunion.io/x/pkg/errors" 25 26 api "yunion.io/x/cloudmux/pkg/apis/compute" 27 "yunion.io/x/cloudmux/pkg/cloudprovider" 28 "yunion.io/x/cloudmux/pkg/multicloud" 29 ) 30 31 type SPortDetail struct { 32 Status string `json:"status"` 33 Name string `json:"name"` 34 AdminStateUp bool `json:"admin_state_up"` 35 NetworkId string `json:"network_id"` 36 DeviceOwner string `json:"device_owner"` 37 MacAddress string `json:"mac_address"` 38 DeviceId string `json:"device_id"` 39 } 40 41 type SEipAddress struct { 42 region *SRegion 43 multicloud.SEipBase 44 OpenStackTags 45 46 RouterId string `json:"router_id"` 47 Status string `json:"status"` 48 Description string `json:"description"` 49 Tags []string `json:"tags"` 50 TenantId string `json:"tenant_id"` 51 CreatedAt time.Time `json:"created_at"` 52 UpdatedAt time.Time `json:"updated_at"` 53 FloatingNetworkId string `json:"floating_network_id"` 54 PortDetails SPortDetail `json:"port_details"` 55 FixedIPAddress string `json:"fixed_ip_address"` 56 FloatingIPAddress string `json:"floating_ip_address"` 57 RevisionNumber int `json:"revision_number"` 58 ProjectId string `json:"project_id"` 59 PortId string `json:"port_id"` 60 Id string `json:"id"` 61 QosPolicyId string `json:"qos_policy_id"` 62 } 63 64 func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) { 65 resource := fmt.Sprintf("/v2.0/floatingips/%s", eipId) 66 resp, err := region.vpcGet(resource) 67 if err != nil { 68 return nil, err 69 } 70 eip := &SEipAddress{region: region} 71 err = resp.Unmarshal(eip, "floatingip") 72 if err != nil { 73 return nil, errors.Wrap(err, "resp.Unmarshal") 74 } 75 return eip, nil 76 } 77 78 func (region *SRegion) GetEipByIp(ip string) (*SEipAddress, error) { 79 eips, err := region.GetEips(ip) 80 if err != nil { 81 return nil, errors.Wrap(err, "GetEips") 82 } 83 if len(eips) == 1 { 84 eips[0].region = region 85 return &eips[0], nil 86 } 87 if len(eips) == 0 { 88 return nil, cloudprovider.ErrNotFound 89 } 90 return nil, cloudprovider.ErrDuplicateId 91 } 92 93 func (region *SRegion) GetEips(ip string) ([]SEipAddress, error) { 94 resource := "/v2.0/floatingips" 95 eips := []SEipAddress{} 96 query := url.Values{} 97 if len(ip) > 0 { 98 query.Set("floating_ip_address", ip) 99 } 100 for { 101 part := struct { 102 Floatingips []SEipAddress 103 FloatingipsLinks SNextLinks 104 }{} 105 resp, err := region.vpcList(resource, query) 106 if err != nil { 107 return nil, errors.Wrap(err, "vpcList") 108 } 109 err = resp.Unmarshal(&part) 110 if err != nil { 111 return nil, errors.Wrap(err, "resp.Unmarshal") 112 } 113 eips = append(eips, part.Floatingips...) 114 marker := part.FloatingipsLinks.GetNextMark() 115 if len(marker) == 0 { 116 break 117 } 118 query.Set("marker", marker) 119 } 120 return eips, nil 121 } 122 123 func (eip *SEipAddress) GetId() string { 124 return eip.Id 125 } 126 127 func (eip *SEipAddress) GetName() string { 128 return eip.FloatingIPAddress 129 } 130 131 func (eip *SEipAddress) GetGlobalId() string { 132 return eip.Id 133 } 134 135 func (eip *SEipAddress) GetStatus() string { 136 switch eip.Status { 137 case "ACTIVE": 138 return api.EIP_STATUS_READY 139 case "DOWN": //实际是未绑定在机器上 140 return api.EIP_STATUS_READY 141 case "ERROR": 142 return api.EIP_STATUS_UNKNOWN 143 default: 144 log.Errorf("Unknown eip %s status %s", eip.Id, eip.Status) 145 return api.EIP_STATUS_UNKNOWN 146 } 147 } 148 149 func (eip *SEipAddress) Refresh() error { 150 _eip, err := eip.region.GetEip(eip.Id) 151 if err != nil { 152 return err 153 } 154 return jsonutils.Update(eip, _eip) 155 } 156 157 func (eip *SEipAddress) IsEmulated() bool { 158 return false 159 } 160 161 func (eip *SEipAddress) GetIpAddr() string { 162 return eip.FloatingIPAddress 163 } 164 165 func (eip *SEipAddress) GetMode() string { 166 return api.EIP_MODE_STANDALONE_EIP 167 } 168 169 func (eip *SEipAddress) GetAssociationType() string { 170 if len(eip.GetAssociationExternalId()) > 0 { 171 return api.EIP_ASSOCIATE_TYPE_SERVER 172 } 173 return "" 174 } 175 176 func (eip *SEipAddress) GetAssociationExternalId() string { 177 if len(eip.PortDetails.DeviceId) > 0 { 178 return eip.PortDetails.DeviceId 179 } 180 if len(eip.PortId) > 0 { 181 port, err := eip.region.GetPort(eip.PortId) 182 if err != nil { 183 log.Errorf("failed to get eip port %s info", eip.PortId) 184 return "" 185 } 186 return port.DeviceID 187 } 188 return "" 189 } 190 191 func (eip *SEipAddress) GetBillingType() string { 192 return "" 193 } 194 195 func (eip *SEipAddress) GetCreatedAt() time.Time { 196 return eip.CreatedAt 197 } 198 199 func (eip *SEipAddress) GetExpiredAt() time.Time { 200 return time.Time{} 201 } 202 203 func (eip *SEipAddress) Delete() error { 204 return eip.region.DeleteEip(eip.Id) 205 } 206 207 func (eip *SEipAddress) GetBandwidth() int { 208 return 0 209 } 210 211 func (eip *SEipAddress) GetINetworkId() string { 212 networks, err := eip.region.GetNetworks(eip.FloatingNetworkId) 213 if err != nil { 214 log.Errorf("failed to find vpc id for eip %s(%s), error: %v", eip.FloatingIPAddress, eip.FloatingNetworkId, err) 215 return "" 216 } 217 for _, network := range networks { 218 for _, pool := range network.AllocationPools { 219 if pool.Contains(eip.FloatingIPAddress) { 220 network.AllocationPools = []AllocationPool{pool} 221 return network.GetGlobalId() 222 } 223 } 224 } 225 log.Errorf("failed to find eip %s(%s) networkId", eip.FloatingIPAddress, eip.FloatingNetworkId) 226 return "" 227 } 228 229 func (eip *SEipAddress) GetInternetChargeType() string { 230 return api.EIP_CHARGE_TYPE_BY_TRAFFIC 231 } 232 233 func (eip *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error { 234 return eip.region.AssociateEip(conf.InstanceId, eip.Id) 235 } 236 237 func (eip *SEipAddress) Dissociate() error { 238 return eip.region.DisassociateEip(eip.Id) 239 } 240 241 func (eip *SEipAddress) ChangeBandwidth(bw int) error { 242 return cloudprovider.ErrNotSupported 243 } 244 245 func (eip *SEipAddress) GetProjectId() string { 246 return eip.ProjectId 247 } 248 249 func (region *SRegion) CreateEip(vpcId, networkId, ip string, projectId string) (*SEipAddress, error) { 250 _, networkId = getNetworkId(networkId) 251 params := map[string]map[string]string{ 252 "floatingip": map[string]string{ 253 "floating_network_id": vpcId, 254 "subnet_id": networkId, 255 }, 256 } 257 if len(projectId) > 0 { 258 params["floatingip"]["tenant_id"] = projectId 259 } 260 if len(ip) > 0 { 261 params["floatingip"]["floating_ip_address"] = ip 262 } 263 resource := "/v2.0/floatingips" 264 resp, err := region.vpcPost(resource, params) 265 if err != nil { 266 return nil, errors.Wrap(err, "vpcPost") 267 } 268 eip := &SEipAddress{region: region} 269 err = resp.Unmarshal(eip, "floatingip") 270 if err != nil { 271 return nil, errors.Wrap(err, "resp.Unmarshal") 272 } 273 return eip, nil 274 } 275 276 func (region *SRegion) AssociateEip(instanceId, eipId string) error { 277 instance, err := region.GetInstance(instanceId) 278 if err != nil { 279 return err 280 } 281 for networkName, address := range instance.Addresses { 282 for i := 0; i < len(address); i++ { 283 if instance.Addresses[networkName][i].Type == "fixed" { 284 ports, err := region.GetPorts(instance.Addresses[networkName][i].MacAddr, "") 285 if err != nil { 286 return err 287 } 288 289 if len(ports) == 1 { 290 params := map[string]map[string]string{ 291 "floatingip": { 292 "port_id": ports[0].ID, 293 }, 294 } 295 resource := "/v2.0/floatingips/" + eipId 296 _, err = region.vpcUpdate(resource, params) 297 return err 298 } 299 300 if len(ports) == 0 { 301 log.Errorf("failed to found port for instance nic %s(%s)", instance.Addresses[networkName][i].Addr, instance.Addresses[networkName][i].MacAddr) 302 return cloudprovider.ErrNotFound 303 } 304 return cloudprovider.ErrDuplicateId 305 } 306 } 307 } 308 return fmt.Errorf("failed to found instnace %s nics for binding eip", instanceId) 309 } 310 311 func (region *SRegion) AssociateEipWithPortId(portid, eipId string) error { 312 params := map[string]map[string]string{ 313 "floatingip": { 314 "port_id": portid, 315 }, 316 } 317 _, err := region.vpcUpdate("/v2.0/floatingips/"+eipId, jsonutils.Marshal(params)) 318 return err 319 } 320 321 func (region *SRegion) DisassociateEip(eipId string) error { 322 params, _ := jsonutils.Parse([]byte(`{ 323 "floatingip": { 324 "port_id": null, 325 }, 326 }`)) 327 _, err := region.vpcUpdate("/v2.0/floatingips/"+eipId, params) 328 return err 329 } 330 331 func (region *SRegion) DeleteEip(eipId string) error { 332 resource := "/v2.0/floatingips/" + eipId 333 _, err := region.vpcDelete(resource) 334 return err 335 }