2018-06-12 04:38:54 -04:00
|
|
|
package openstack
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2019-07-17 18:00:28 -04:00
|
|
|
"log"
|
2019-10-26 17:40:11 -04:00
|
|
|
"net"
|
2018-06-12 04:38:54 -04:00
|
|
|
|
2018-08-16 16:38:41 -04:00
|
|
|
"github.com/google/uuid"
|
2018-06-12 04:38:54 -04:00
|
|
|
"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"
|
2019-10-26 17:40:11 -04:00
|
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
2018-06-12 04:38:54 -04:00
|
|
|
"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.
|
2019-07-17 18:00:28 -04:00
|
|
|
func GetInstancePortID(client *gophercloud.ServiceClient, id string, instance_float_net string) (string, error) {
|
|
|
|
|
|
|
|
selected_interface := 0
|
|
|
|
|
2018-06-12 04:38:54 -04:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2019-07-17 18:00:28 -04:00
|
|
|
for i := 0; i < len(interfaces); i++ {
|
|
|
|
log.Printf("Instance interface: %v: %+v\n", i, interfaces[i])
|
|
|
|
if interfaces[i].NetID == instance_float_net {
|
2019-07-17 18:10:52 -04:00
|
|
|
log.Printf("Found preferred interface: %v\n", i)
|
|
|
|
selected_interface = i
|
|
|
|
log.Printf("Using interface value: %v", selected_interface)
|
2019-07-17 18:00:28 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return interfaces[selected_interface].PortID, nil
|
2018-06-12 04:38:54 -04:00
|
|
|
}
|
2018-08-16 16:38:41 -04:00
|
|
|
|
2018-08-16 17:49:06 -04:00
|
|
|
// CheckFloatingIPNetwork checks provided network reference and returns a valid
|
2018-08-16 16:38:41 -04:00
|
|
|
// Networking service ID.
|
2018-08-16 17:49:06 -04:00
|
|
|
func CheckFloatingIPNetwork(client *gophercloud.ServiceClient, networkRef string) (string, error) {
|
2018-08-16 16:38:41 -04:00
|
|
|
if _, err := uuid.Parse(networkRef); err != nil {
|
2018-08-16 17:49:06 -04:00
|
|
|
return GetFloatingIPNetworkIDByName(client, networkRef)
|
2018-08-16 16:38:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return networkRef, nil
|
|
|
|
}
|
|
|
|
|
2018-08-16 17:49:06 -04:00
|
|
|
// 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) {
|
2018-08-16 16:38:41 -04:00
|
|
|
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
|
|
|
|
}
|
2019-10-26 17:40:11 -04:00
|
|
|
|
|
|
|
// DiscoverProvisioningNetwork finds the first network whose subnet matches the given network ranges.
|
|
|
|
func DiscoverProvisioningNetwork(client *gophercloud.ServiceClient, cidrs []string) (string, error) {
|
|
|
|
allPages, err := subnets.List(client, subnets.ListOpts{}).AllPages()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
allSubnets, err := subnets.ExtractSubnets(allPages)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, subnet := range allSubnets {
|
|
|
|
_, tenantIPNet, err := net.ParseCIDR(subnet.CIDR)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, cidr := range cidrs {
|
|
|
|
_, candidateIPNet, err := net.ParseCIDR(cidr)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if containsNet(candidateIPNet, tenantIPNet) {
|
|
|
|
return subnet.NetworkID, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", fmt.Errorf("failed to discover a provisioning network")
|
|
|
|
}
|
|
|
|
|
|
|
|
// containsNet returns true whenever IPNet `a` contains IPNet `b`
|
|
|
|
func containsNet(a *net.IPNet, b *net.IPNet) bool {
|
|
|
|
aMask, _ := a.Mask.Size()
|
|
|
|
bMask, _ := b.Mask.Size()
|
|
|
|
return a.Contains(b.IP) && aMask <= bMask
|
|
|
|
}
|