1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
package openstack
import (
"fmt"
"log"
"net"
"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/openstack/networking/v2/subnets"
"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, instance_float_net string) (string, error) {
selected_interface := 0
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)
}
for i := 0; i < len(interfaces); i++ {
log.Printf("Instance interface: %v: %+v\n", i, interfaces[i])
if interfaces[i].NetID == instance_float_net {
log.Printf("Found preferred interface: %v\n", i)
selected_interface = i
log.Printf("Using interface value: %v", selected_interface)
}
}
return interfaces[selected_interface].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
}
// 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
}
|