126 lines
3.8 KiB
Go
126 lines
3.8 KiB
Go
package openstack
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/gophercloud/gophercloud"
|
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
|
"github.com/gophercloud/gophercloud/pagination"
|
|
)
|
|
|
|
// CheckFloatingIP gets a floating IP by its ID and checks if it is already
|
|
// associated with any internal interface.
|
|
// It returns floating IP if it can be used.
|
|
func CheckFloatingIP(client *gophercloud.ServiceClient, id string) (*floatingips.FloatingIP, error) {
|
|
floatingIP, err := floatingips.Get(client, id).Extract()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if floatingIP.PortID != "" {
|
|
return nil, fmt.Errorf("provided floating IP '%s' is already associated with port '%s'",
|
|
id, floatingIP.PortID)
|
|
}
|
|
|
|
return floatingIP, nil
|
|
}
|
|
|
|
// FindFreeFloatingIP returns free unassociated floating IP.
|
|
// It will return first floating IP if there are many.
|
|
func FindFreeFloatingIP(client *gophercloud.ServiceClient) (*floatingips.FloatingIP, error) {
|
|
var freeFloatingIP *floatingips.FloatingIP
|
|
|
|
pager := floatingips.List(client, floatingips.ListOpts{
|
|
Status: "DOWN",
|
|
})
|
|
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
|
candidates, err := floatingips.ExtractFloatingIPs(page)
|
|
if err != nil {
|
|
return false, err // stop and throw error out
|
|
}
|
|
|
|
for _, candidate := range candidates {
|
|
if candidate.PortID != "" {
|
|
continue // this floating IP is associated with port, move to next in list
|
|
}
|
|
|
|
// Floating IP is able to be allocated.
|
|
freeFloatingIP = &candidate
|
|
return false, nil // stop iterating over pages
|
|
}
|
|
return true, nil // try the next page
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if freeFloatingIP == nil {
|
|
return nil, fmt.Errorf("no free floating IPs found")
|
|
}
|
|
|
|
return freeFloatingIP, nil
|
|
}
|
|
|
|
// GetInstancePortID returns internal port of the instance that can be used for
|
|
// the association of a floating IP.
|
|
// It will return an ID of a first port if there are many.
|
|
func GetInstancePortID(client *gophercloud.ServiceClient, id string) (string, error) {
|
|
interfacesPage, err := attachinterfaces.List(client, id).AllPages()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
interfaces, err := attachinterfaces.ExtractInterfaces(interfacesPage)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(interfaces) == 0 {
|
|
return "", fmt.Errorf("instance '%s' has no interfaces", id)
|
|
}
|
|
|
|
return interfaces[0].PortID, nil
|
|
}
|
|
|
|
// CheckFloatingIPNetwork checks provided network reference and returns a valid
|
|
// Networking service ID.
|
|
func CheckFloatingIPNetwork(client *gophercloud.ServiceClient, networkRef string) (string, error) {
|
|
if _, err := uuid.Parse(networkRef); err != nil {
|
|
return GetFloatingIPNetworkIDByName(client, networkRef)
|
|
}
|
|
|
|
return networkRef, nil
|
|
}
|
|
|
|
// ExternalNetwork is a network with external router.
|
|
type ExternalNetwork struct {
|
|
networks.Network
|
|
external.NetworkExternalExt
|
|
}
|
|
|
|
// GetFloatingIPNetworkIDByName searches for the external network ID by the provided name.
|
|
func GetFloatingIPNetworkIDByName(client *gophercloud.ServiceClient, networkName string) (string, error) {
|
|
var externalNetworks []ExternalNetwork
|
|
|
|
allPages, err := networks.List(client, networks.ListOpts{
|
|
Name: networkName,
|
|
}).AllPages()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if err := networks.ExtractNetworksInto(allPages, &externalNetworks); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if len(externalNetworks) == 0 {
|
|
return "", fmt.Errorf("can't find external network %s", networkName)
|
|
}
|
|
// Check and return the first external network.
|
|
if !externalNetworks[0].External {
|
|
return "", fmt.Errorf("network %s is not external", networkName)
|
|
}
|
|
|
|
return externalNetworks[0].ID, nil
|
|
}
|