Merge pull request #4812 from jtopjian/openstack-per-network-floating-ip
provider/openstack: Per-network Floating IPs
This commit is contained in:
commit
1ccd0491ff
|
@ -136,10 +136,20 @@ func resourceComputeInstanceV2() *schema.Resource {
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
"floating_ip": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
"mac": &schema.Schema{
|
"mac": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
"access_network": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -320,11 +330,6 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
networkDetails, err := resourceInstanceNetworks(computeClient, d)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine if volume/block_device configuration is correct
|
// determine if volume/block_device configuration is correct
|
||||||
// this includes ensuring volume_ids are set
|
// this includes ensuring volume_ids are set
|
||||||
// and if only one block_device was specified.
|
// and if only one block_device was specified.
|
||||||
|
@ -332,6 +337,18 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if floating IP configuration is correct
|
||||||
|
if err := checkInstanceFloatingIPs(d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a list of networks with the information given upon creation.
|
||||||
|
// Error out if an invalid network configuration was used.
|
||||||
|
networkDetails, err := getInstanceNetworks(computeClient, d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
networks := make([]servers.Network, len(networkDetails))
|
networks := make([]servers.Network, len(networkDetails))
|
||||||
for i, net := range networkDetails {
|
for i, net := range networkDetails {
|
||||||
networks[i] = servers.Network{
|
networks[i] = servers.Network{
|
||||||
|
@ -424,11 +441,15 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
|
||||||
"Error waiting for instance (%s) to become ready: %s",
|
"Error waiting for instance (%s) to become ready: %s",
|
||||||
server.ID, err)
|
server.ID, err)
|
||||||
}
|
}
|
||||||
floatingIP := d.Get("floating_ip").(string)
|
|
||||||
if floatingIP != "" {
|
// Now that the instance has been created, we need to do an early read on the
|
||||||
if err := floatingip.Associate(computeClient, server.ID, floatingIP).ExtractErr(); err != nil {
|
// networks in order to associate floating IPs
|
||||||
return fmt.Errorf("Error associating floating IP: %s", err)
|
_, err = getInstanceNetworksAndAddresses(computeClient, d)
|
||||||
}
|
|
||||||
|
// If floating IPs were specified, associate them after the instance has launched.
|
||||||
|
err = associateFloatingIPsToInstance(computeClient, d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if volumes were specified, attach them after the instance has launched.
|
// if volumes were specified, attach them after the instance has launched.
|
||||||
|
@ -462,99 +483,35 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err
|
||||||
|
|
||||||
d.Set("name", server.Name)
|
d.Set("name", server.Name)
|
||||||
|
|
||||||
// begin reading the network configuration
|
// Get the instance network and address information
|
||||||
d.Set("access_ip_v4", server.AccessIPv4)
|
networks, err := getInstanceNetworksAndAddresses(computeClient, d)
|
||||||
d.Set("access_ip_v6", server.AccessIPv6)
|
|
||||||
hostv4 := server.AccessIPv4
|
|
||||||
hostv6 := server.AccessIPv6
|
|
||||||
|
|
||||||
networkDetails, err := resourceInstanceNetworks(computeClient, d)
|
|
||||||
addresses := resourceInstanceAddresses(server.Addresses)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there are no networkDetails, make networks at least a length of 1
|
// Determine the best IPv4 and IPv6 addresses to access the instance with
|
||||||
networkLength := 1
|
hostv4, hostv6 := getInstanceAccessAddresses(d, networks)
|
||||||
if len(networkDetails) > 0 {
|
|
||||||
networkLength = len(networkDetails)
|
|
||||||
}
|
|
||||||
networks := make([]map[string]interface{}, networkLength)
|
|
||||||
|
|
||||||
// Loop through all networks and addresses,
|
|
||||||
// merge relevant address details.
|
|
||||||
if len(networkDetails) == 0 {
|
|
||||||
for netName, n := range addresses {
|
|
||||||
if floatingIP, ok := n["floating_ip"]; ok {
|
|
||||||
hostv4 = floatingIP.(string)
|
|
||||||
} else {
|
|
||||||
if hostv4 == "" && n["fixed_ip_v4"] != nil {
|
|
||||||
hostv4 = n["fixed_ip_v4"].(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if hostv6 == "" && n["fixed_ip_v6"] != nil {
|
|
||||||
hostv6 = n["fixed_ip_v6"].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
networks[0] = map[string]interface{}{
|
|
||||||
"name": netName,
|
|
||||||
"fixed_ip_v4": n["fixed_ip_v4"],
|
|
||||||
"fixed_ip_v6": n["fixed_ip_v6"],
|
|
||||||
"mac": n["mac"],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i, net := range networkDetails {
|
|
||||||
n := addresses[net["name"].(string)]
|
|
||||||
|
|
||||||
if floatingIP, ok := n["floating_ip"]; ok {
|
|
||||||
hostv4 = floatingIP.(string)
|
|
||||||
} else {
|
|
||||||
if hostv4 == "" && n["fixed_ip_v4"] != nil {
|
|
||||||
hostv4 = n["fixed_ip_v4"].(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if hostv6 == "" && n["fixed_ip_v6"] != nil {
|
|
||||||
hostv6 = n["fixed_ip_v6"].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
networks[i] = map[string]interface{}{
|
|
||||||
"uuid": networkDetails[i]["uuid"],
|
|
||||||
"name": networkDetails[i]["name"],
|
|
||||||
"port": networkDetails[i]["port"],
|
|
||||||
"fixed_ip_v4": n["fixed_ip_v4"],
|
|
||||||
"fixed_ip_v6": n["fixed_ip_v6"],
|
|
||||||
"mac": n["mac"],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("[DEBUG] new networks: %+v", networks)
|
|
||||||
|
|
||||||
d.Set("network", networks)
|
d.Set("network", networks)
|
||||||
d.Set("access_ip_v4", hostv4)
|
d.Set("access_ip_v4", hostv4)
|
||||||
d.Set("access_ip_v6", hostv6)
|
d.Set("access_ip_v6", hostv6)
|
||||||
log.Printf("hostv4: %s", hostv4)
|
|
||||||
log.Printf("hostv6: %s", hostv6)
|
|
||||||
|
|
||||||
// prefer the v6 address if no v4 address exists.
|
// Determine the best IP address to use for SSH connectivity.
|
||||||
preferredv := ""
|
// Prefer IPv4 over IPv6.
|
||||||
|
preferredSSHAddress := ""
|
||||||
if hostv4 != "" {
|
if hostv4 != "" {
|
||||||
preferredv = hostv4
|
preferredSSHAddress = hostv4
|
||||||
} else if hostv6 != "" {
|
} else if hostv6 != "" {
|
||||||
preferredv = hostv6
|
preferredSSHAddress = hostv6
|
||||||
}
|
}
|
||||||
|
|
||||||
if preferredv != "" {
|
if preferredSSHAddress != "" {
|
||||||
// Initialize the connection info
|
// Initialize the connection info
|
||||||
d.SetConnInfo(map[string]string{
|
d.SetConnInfo(map[string]string{
|
||||||
"type": "ssh",
|
"type": "ssh",
|
||||||
"host": preferredv,
|
"host": preferredSSHAddress,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// end network configuration
|
|
||||||
|
|
||||||
d.Set("metadata", server.Metadata)
|
d.Set("metadata", server.Metadata)
|
||||||
|
|
||||||
|
@ -600,12 +557,6 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e
|
||||||
if d.HasChange("name") {
|
if d.HasChange("name") {
|
||||||
updateOpts.Name = d.Get("name").(string)
|
updateOpts.Name = d.Get("name").(string)
|
||||||
}
|
}
|
||||||
if d.HasChange("access_ip_v4") {
|
|
||||||
updateOpts.AccessIPv4 = d.Get("access_ip_v4").(string)
|
|
||||||
}
|
|
||||||
if d.HasChange("access_ip_v6") {
|
|
||||||
updateOpts.AccessIPv4 = d.Get("access_ip_v6").(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
if updateOpts != (servers.UpdateOpts{}) {
|
if updateOpts != (servers.UpdateOpts{}) {
|
||||||
_, err := servers.Update(computeClient, d.Id(), updateOpts).Extract()
|
_, err := servers.Update(computeClient, d.Id(), updateOpts).Extract()
|
||||||
|
@ -679,20 +630,48 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e
|
||||||
log.Printf("[DEBUG] Old Floating IP: %v", oldFIP)
|
log.Printf("[DEBUG] Old Floating IP: %v", oldFIP)
|
||||||
log.Printf("[DEBUG] New Floating IP: %v", newFIP)
|
log.Printf("[DEBUG] New Floating IP: %v", newFIP)
|
||||||
if oldFIP.(string) != "" {
|
if oldFIP.(string) != "" {
|
||||||
log.Printf("[DEBUG] Attemping to disassociate %s from %s", oldFIP, d.Id())
|
log.Printf("[DEBUG] Attempting to disassociate %s from %s", oldFIP, d.Id())
|
||||||
if err := floatingip.Disassociate(computeClient, d.Id(), oldFIP.(string)).ExtractErr(); err != nil {
|
if err := disassociateFloatingIPFromInstance(computeClient, oldFIP.(string), d.Id(), ""); err != nil {
|
||||||
return fmt.Errorf("Error disassociating Floating IP during update: %s", err)
|
return fmt.Errorf("Error disassociating Floating IP during update: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if newFIP.(string) != "" {
|
if newFIP.(string) != "" {
|
||||||
log.Printf("[DEBUG] Attemping to associate %s to %s", newFIP, d.Id())
|
log.Printf("[DEBUG] Attempting to associate %s to %s", newFIP, d.Id())
|
||||||
if err := floatingip.Associate(computeClient, d.Id(), newFIP.(string)).ExtractErr(); err != nil {
|
if err := associateFloatingIPToInstance(computeClient, newFIP.(string), d.Id(), ""); err != nil {
|
||||||
return fmt.Errorf("Error associating Floating IP during update: %s", err)
|
return fmt.Errorf("Error associating Floating IP during update: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.HasChange("network") {
|
||||||
|
oldNetworks, newNetworks := d.GetChange("network")
|
||||||
|
oldNetworkList := oldNetworks.([]interface{})
|
||||||
|
newNetworkList := newNetworks.([]interface{})
|
||||||
|
for i, oldNet := range oldNetworkList {
|
||||||
|
oldNetRaw := oldNet.(map[string]interface{})
|
||||||
|
oldFIP := oldNetRaw["floating_ip"].(string)
|
||||||
|
oldFixedIP := oldNetRaw["fixed_ip_v4"].(string)
|
||||||
|
|
||||||
|
newNetRaw := newNetworkList[i].(map[string]interface{})
|
||||||
|
newFIP := newNetRaw["floating_ip"].(string)
|
||||||
|
newFixedIP := newNetRaw["fixed_ip_v4"].(string)
|
||||||
|
|
||||||
|
// Only changes to the floating IP are supported
|
||||||
|
if oldFIP != newFIP {
|
||||||
|
log.Printf("[DEBUG] Attempting to disassociate %s from %s", oldFIP, d.Id())
|
||||||
|
if err := disassociateFloatingIPFromInstance(computeClient, oldFIP, d.Id(), oldFixedIP); err != nil {
|
||||||
|
return fmt.Errorf("Error disassociating Floating IP during update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Attempting to associate %s to %s", newFIP, d.Id())
|
||||||
|
if err := associateFloatingIPToInstance(computeClient, newFIP, d.Id(), newFixedIP); err != nil {
|
||||||
|
return fmt.Errorf("Error associating Floating IP during update: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if d.HasChange("volume") {
|
if d.HasChange("volume") {
|
||||||
// ensure the volume configuration is correct
|
// ensure the volume configuration is correct
|
||||||
if err := checkVolumeConfig(d); err != nil {
|
if err := checkVolumeConfig(d); err != nil {
|
||||||
|
@ -845,7 +824,62 @@ func resourceInstanceSecGroupsV2(d *schema.ResourceData) []string {
|
||||||
return secgroups
|
return secgroups
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceInstanceNetworks(computeClient *gophercloud.ServiceClient, d *schema.ResourceData) ([]map[string]interface{}, error) {
|
// getInstanceNetworks collects instance network information from different sources
|
||||||
|
// and aggregates it all together.
|
||||||
|
func getInstanceNetworksAndAddresses(computeClient *gophercloud.ServiceClient, d *schema.ResourceData) ([]map[string]interface{}, error) {
|
||||||
|
server, err := servers.Get(computeClient, d.Id()).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return nil, CheckDeleted(d, err, "server")
|
||||||
|
}
|
||||||
|
|
||||||
|
networkDetails, err := getInstanceNetworks(computeClient, d)
|
||||||
|
addresses := getInstanceAddresses(server.Addresses)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are no networkDetails, make networks at least a length of 1
|
||||||
|
networkLength := 1
|
||||||
|
if len(networkDetails) > 0 {
|
||||||
|
networkLength = len(networkDetails)
|
||||||
|
}
|
||||||
|
networks := make([]map[string]interface{}, networkLength)
|
||||||
|
|
||||||
|
// Loop through all networks and addresses,
|
||||||
|
// merge relevant address details.
|
||||||
|
if len(networkDetails) == 0 {
|
||||||
|
for netName, n := range addresses {
|
||||||
|
networks[0] = map[string]interface{}{
|
||||||
|
"name": netName,
|
||||||
|
"fixed_ip_v4": n["fixed_ip_v4"],
|
||||||
|
"fixed_ip_v6": n["fixed_ip_v6"],
|
||||||
|
"floating_ip": n["floating_ip"],
|
||||||
|
"mac": n["mac"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i, net := range networkDetails {
|
||||||
|
n := addresses[net["name"].(string)]
|
||||||
|
|
||||||
|
networks[i] = map[string]interface{}{
|
||||||
|
"uuid": networkDetails[i]["uuid"],
|
||||||
|
"name": networkDetails[i]["name"],
|
||||||
|
"port": networkDetails[i]["port"],
|
||||||
|
"fixed_ip_v4": n["fixed_ip_v4"],
|
||||||
|
"fixed_ip_v6": n["fixed_ip_v6"],
|
||||||
|
"floating_ip": n["floating_ip"],
|
||||||
|
"mac": n["mac"],
|
||||||
|
"access_network": networkDetails[i]["access_network"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] networks: %+v", networks)
|
||||||
|
|
||||||
|
return networks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInstanceNetworks(computeClient *gophercloud.ServiceClient, d *schema.ResourceData) ([]map[string]interface{}, error) {
|
||||||
rawNetworks := d.Get("network").([]interface{})
|
rawNetworks := d.Get("network").([]interface{})
|
||||||
newNetworks := make([]map[string]interface{}, 0, len(rawNetworks))
|
newNetworks := make([]map[string]interface{}, 0, len(rawNetworks))
|
||||||
var tenantnet tenantnetworks.Network
|
var tenantnet tenantnetworks.Network
|
||||||
|
@ -860,6 +894,7 @@ func resourceInstanceNetworks(computeClient *gophercloud.ServiceClient, d *schem
|
||||||
}
|
}
|
||||||
|
|
||||||
rawMap := raw.(map[string]interface{})
|
rawMap := raw.(map[string]interface{})
|
||||||
|
|
||||||
allPages, err := tenantnetworks.List(computeClient).AllPages()
|
allPages, err := tenantnetworks.List(computeClient).AllPages()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
||||||
|
@ -899,10 +934,11 @@ func resourceInstanceNetworks(computeClient *gophercloud.ServiceClient, d *schem
|
||||||
}
|
}
|
||||||
|
|
||||||
newNetworks = append(newNetworks, map[string]interface{}{
|
newNetworks = append(newNetworks, map[string]interface{}{
|
||||||
"uuid": networkID,
|
"uuid": networkID,
|
||||||
"name": networkName,
|
"name": networkName,
|
||||||
"port": rawMap["port"].(string),
|
"port": rawMap["port"].(string),
|
||||||
"fixed_ip_v4": rawMap["fixed_ip_v4"].(string),
|
"fixed_ip_v4": rawMap["fixed_ip_v4"].(string),
|
||||||
|
"access_network": rawMap["access_network"].(bool),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -910,8 +946,7 @@ func resourceInstanceNetworks(computeClient *gophercloud.ServiceClient, d *schem
|
||||||
return newNetworks, nil
|
return newNetworks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceInstanceAddresses(addresses map[string]interface{}) map[string]map[string]interface{} {
|
func getInstanceAddresses(addresses map[string]interface{}) map[string]map[string]interface{} {
|
||||||
|
|
||||||
addrs := make(map[string]map[string]interface{})
|
addrs := make(map[string]map[string]interface{})
|
||||||
for n, networkAddresses := range addresses {
|
for n, networkAddresses := range addresses {
|
||||||
addrs[n] = make(map[string]interface{})
|
addrs[n] = make(map[string]interface{})
|
||||||
|
@ -937,6 +972,117 @@ func resourceInstanceAddresses(addresses map[string]interface{}) map[string]map[
|
||||||
return addrs
|
return addrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getInstanceAccessAddresses(d *schema.ResourceData, networks []map[string]interface{}) (string, string) {
|
||||||
|
var hostv4, hostv6 string
|
||||||
|
|
||||||
|
// Start with a global floating IP
|
||||||
|
floatingIP := d.Get("floating_ip").(string)
|
||||||
|
if floatingIP != "" {
|
||||||
|
hostv4 = floatingIP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through all networks and check for the following:
|
||||||
|
// * If the network is set as an access network.
|
||||||
|
// * If the network has a floating IP.
|
||||||
|
// * If the network has a v4/v6 fixed IP.
|
||||||
|
for _, n := range networks {
|
||||||
|
if n["floating_ip"] != nil {
|
||||||
|
hostv4 = n["floating_ip"].(string)
|
||||||
|
} else {
|
||||||
|
if hostv4 == "" && n["fixed_ip_v4"] != nil {
|
||||||
|
hostv4 = n["fixed_ip_v4"].(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hostv6 == "" && n["fixed_ip_v6"] != nil {
|
||||||
|
hostv6 = n["fixed_ip_v6"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n["access_network"].(bool) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] OpenStack Instance Network Access Addresses: %s, %s", hostv4, hostv6)
|
||||||
|
|
||||||
|
return hostv4, hostv6
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkInstanceFloatingIPs(d *schema.ResourceData) error {
|
||||||
|
rawNetworks := d.Get("network").([]interface{})
|
||||||
|
floatingIP := d.Get("floating_ip").(string)
|
||||||
|
|
||||||
|
for _, raw := range rawNetworks {
|
||||||
|
if raw == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rawMap := raw.(map[string]interface{})
|
||||||
|
|
||||||
|
// Error if a floating IP was specified both globally and in the network block.
|
||||||
|
if floatingIP != "" && rawMap["floating_ip"] != "" {
|
||||||
|
return fmt.Errorf("Cannot specify a floating IP both globally and in a network block.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func associateFloatingIPsToInstance(computeClient *gophercloud.ServiceClient, d *schema.ResourceData) error {
|
||||||
|
floatingIP := d.Get("floating_ip").(string)
|
||||||
|
rawNetworks := d.Get("network").([]interface{})
|
||||||
|
instanceID := d.Id()
|
||||||
|
|
||||||
|
if floatingIP != "" {
|
||||||
|
if err := associateFloatingIPToInstance(computeClient, floatingIP, instanceID, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, raw := range rawNetworks {
|
||||||
|
if raw == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rawMap := raw.(map[string]interface{})
|
||||||
|
if rawMap["floating_ip"].(string) != "" {
|
||||||
|
floatingIP := rawMap["floating_ip"].(string)
|
||||||
|
fixedIP := rawMap["fixed_ip_v4"].(string)
|
||||||
|
if err := associateFloatingIPToInstance(computeClient, floatingIP, instanceID, fixedIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func associateFloatingIPToInstance(computeClient *gophercloud.ServiceClient, floatingIP string, instanceID string, fixedIP string) error {
|
||||||
|
associateOpts := floatingip.AssociateOpts{
|
||||||
|
ServerID: instanceID,
|
||||||
|
FloatingIP: floatingIP,
|
||||||
|
FixedIP: fixedIP,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := floatingip.AssociateInstance(computeClient, associateOpts).ExtractErr(); err != nil {
|
||||||
|
return fmt.Errorf("Error associating floating IP: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func disassociateFloatingIPFromInstance(computeClient *gophercloud.ServiceClient, floatingIP string, instanceID string, fixedIP string) error {
|
||||||
|
associateOpts := floatingip.AssociateOpts{
|
||||||
|
ServerID: instanceID,
|
||||||
|
FloatingIP: floatingIP,
|
||||||
|
FixedIP: fixedIP,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := floatingip.DisassociateInstance(computeClient, associateOpts).ExtractErr(); err != nil {
|
||||||
|
return fmt.Errorf("Error disassociating floating IP: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func resourceInstanceMetadataV2(d *schema.ResourceData) map[string]string {
|
func resourceInstanceMetadataV2(d *schema.ResourceData) map[string]string {
|
||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
for key, val := range d.Get("metadata").(map[string]interface{}) {
|
for key, val := range d.Get("metadata").(map[string]interface{}) {
|
||||||
|
|
|
@ -178,10 +178,10 @@ func TestAccComputeV2Instance_volumeDetachPostCreation(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccComputeV2Instance_floatingIPAttach(t *testing.T) {
|
func TestAccComputeV2Instance_floatingIPAttachGlobally(t *testing.T) {
|
||||||
var instance servers.Server
|
var instance servers.Server
|
||||||
var fip floatingip.FloatingIP
|
var fip floatingip.FloatingIP
|
||||||
var testAccComputeV2Instance_floatingIPAttach = fmt.Sprintf(`
|
var testAccComputeV2Instance_floatingIPAttachGlobally = fmt.Sprintf(`
|
||||||
resource "openstack_compute_floatingip_v2" "myip" {
|
resource "openstack_compute_floatingip_v2" "myip" {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ func TestAccComputeV2Instance_floatingIPAttach(t *testing.T) {
|
||||||
CheckDestroy: testAccCheckComputeV2InstanceDestroy,
|
CheckDestroy: testAccCheckComputeV2InstanceDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccComputeV2Instance_floatingIPAttach,
|
Config: testAccComputeV2Instance_floatingIPAttachGlobally,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckComputeV2FloatingIPExists(t, "openstack_compute_floatingip_v2.myip", &fip),
|
testAccCheckComputeV2FloatingIPExists(t, "openstack_compute_floatingip_v2.myip", &fip),
|
||||||
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance),
|
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance),
|
||||||
|
@ -213,6 +213,108 @@ func TestAccComputeV2Instance_floatingIPAttach(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccComputeV2Instance_floatingIPAttachToNetwork(t *testing.T) {
|
||||||
|
var instance servers.Server
|
||||||
|
var fip floatingip.FloatingIP
|
||||||
|
var testAccComputeV2Instance_floatingIPAttachToNetwork = fmt.Sprintf(`
|
||||||
|
resource "openstack_compute_floatingip_v2" "myip" {
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "openstack_compute_instance_v2" "foo" {
|
||||||
|
name = "terraform-test"
|
||||||
|
security_groups = ["default"]
|
||||||
|
|
||||||
|
network {
|
||||||
|
uuid = "%s"
|
||||||
|
floating_ip = "${openstack_compute_floatingip_v2.myip.address}"
|
||||||
|
access_network = true
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
os.Getenv("OS_NETWORK_ID"))
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeV2InstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeV2Instance_floatingIPAttachToNetwork,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeV2FloatingIPExists(t, "openstack_compute_floatingip_v2.myip", &fip),
|
||||||
|
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance),
|
||||||
|
testAccCheckComputeV2InstanceFloatingIPAttach(&instance, &fip),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccComputeV2Instance_floatingIPAttachAndChange(t *testing.T) {
|
||||||
|
var instance servers.Server
|
||||||
|
var fip floatingip.FloatingIP
|
||||||
|
var testAccComputeV2Instance_floatingIPAttachToNetwork_1 = fmt.Sprintf(`
|
||||||
|
resource "openstack_compute_floatingip_v2" "myip_1" {
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "openstack_compute_floatingip_v2" "myip_2" {
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "openstack_compute_instance_v2" "foo" {
|
||||||
|
name = "terraform-test"
|
||||||
|
security_groups = ["default"]
|
||||||
|
|
||||||
|
network {
|
||||||
|
uuid = "%s"
|
||||||
|
floating_ip = "${openstack_compute_floatingip_v2.myip_1.address}"
|
||||||
|
access_network = true
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
os.Getenv("OS_NETWORK_ID"))
|
||||||
|
|
||||||
|
var testAccComputeV2Instance_floatingIPAttachToNetwork_2 = fmt.Sprintf(`
|
||||||
|
resource "openstack_compute_floatingip_v2" "myip_1" {
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "openstack_compute_floatingip_v2" "myip_2" {
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "openstack_compute_instance_v2" "foo" {
|
||||||
|
name = "terraform-test"
|
||||||
|
security_groups = ["default"]
|
||||||
|
|
||||||
|
network {
|
||||||
|
uuid = "%s"
|
||||||
|
floating_ip = "${openstack_compute_floatingip_v2.myip_2.address}"
|
||||||
|
access_network = true
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
os.Getenv("OS_NETWORK_ID"))
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeV2InstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeV2Instance_floatingIPAttachToNetwork_1,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeV2FloatingIPExists(t, "openstack_compute_floatingip_v2.myip_1", &fip),
|
||||||
|
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance),
|
||||||
|
testAccCheckComputeV2InstanceFloatingIPAttach(&instance, &fip),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeV2Instance_floatingIPAttachToNetwork_2,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeV2FloatingIPExists(t, "openstack_compute_floatingip_v2.myip_2", &fip),
|
||||||
|
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance),
|
||||||
|
testAccCheckComputeV2InstanceFloatingIPAttach(&instance, &fip),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccComputeV2Instance_multi_secgroups(t *testing.T) {
|
func TestAccComputeV2Instance_multi_secgroups(t *testing.T) {
|
||||||
var instance servers.Server
|
var instance servers.Server
|
||||||
var secgroup secgroups.SecurityGroup
|
var secgroup secgroups.SecurityGroup
|
||||||
|
|
|
@ -50,7 +50,8 @@ The following arguments are supported:
|
||||||
desired flavor for the server. Changing this resizes the existing server.
|
desired flavor for the server. Changing this resizes the existing server.
|
||||||
|
|
||||||
* `floating_ip` - (Optional) A *Compute* Floating IP that will be associated
|
* `floating_ip` - (Optional) A *Compute* Floating IP that will be associated
|
||||||
with the Instance. The Floating IP must be provisioned already.
|
with the Instance. The Floating IP must be provisioned already. See *Notes*
|
||||||
|
for more information about Floating IPs.
|
||||||
|
|
||||||
* `user_data` - (Optional) The user data to provide when launching the instance.
|
* `user_data` - (Optional) The user data to provide when launching the instance.
|
||||||
Changing this creates a new server.
|
Changing this creates a new server.
|
||||||
|
@ -106,6 +107,13 @@ The `network` block supports:
|
||||||
* `fixed_ip_v4` - (Optional) Specifies a fixed IPv4 address to be used on this
|
* `fixed_ip_v4` - (Optional) Specifies a fixed IPv4 address to be used on this
|
||||||
network.
|
network.
|
||||||
|
|
||||||
|
* `floating_ip` - (Optional) Specifies a floating IP address to be associated
|
||||||
|
with this network. Cannot be combined with a top-level floating IP. See
|
||||||
|
*Notes* for more information about Floating IPs.
|
||||||
|
|
||||||
|
* `access_network` - (Optional) Specifies if this network should be used for
|
||||||
|
provisioning access. Accepts true or false. Defaults to false.
|
||||||
|
|
||||||
The `block_device` block supports:
|
The `block_device` block supports:
|
||||||
|
|
||||||
* `uuid` - (Required) The UUID of the image, volume, or snapshot.
|
* `uuid` - (Required) The UUID of the image, volume, or snapshot.
|
||||||
|
@ -173,11 +181,21 @@ The following attributes are exported:
|
||||||
network.
|
network.
|
||||||
* `network/fixed_ip_v6` - The Fixed IPv6 address of the Instance on that
|
* `network/fixed_ip_v6` - The Fixed IPv6 address of the Instance on that
|
||||||
network.
|
network.
|
||||||
|
* `network/floating_ip` - The Floating IP address of the Instance on that
|
||||||
|
network.
|
||||||
* `network/mac` - The MAC address of the NIC on that network.
|
* `network/mac` - The MAC address of the NIC on that network.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
If you configure the instance to have multiple networks, be aware that only
|
Floating IPs can be associated in one of two ways:
|
||||||
the first network can be associated with a Floating IP. So the first network
|
|
||||||
in the instance resource _must_ be the network that you have configured to
|
* You can specify a Floating IP address by using the top-level `floating_ip`
|
||||||
communicate with your floating IP / public network via a Neutron Router.
|
attribute. This floating IP will be associated with either the network defined
|
||||||
|
in the first `network` block or the default network if no `network` blocks are
|
||||||
|
defined.
|
||||||
|
|
||||||
|
* You can specify a Floating IP address by using the `floating_ip` attribute
|
||||||
|
defined in the `network` block. Each `network` block can have its own floating
|
||||||
|
IP address.
|
||||||
|
|
||||||
|
Only one of the above methods can be used.
|
||||||
|
|
Loading…
Reference in New Issue