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  }