Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Floating IP resource #1

Merged
merged 5 commits into from
Feb 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions builtin/providers/openstack/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func Provider() terraform.ResourceProvider {
"openstack_lb_vip_v1": resourceLBVipV1(),
"openstack_networking_network_v2": resourceNetworkingNetworkV2(),
"openstack_networking_subnet_v2": resourceNetworkingSubnetV2(),
"openstack_networking_floatingip_v2": resourceNetworkingFloatingIPV2(),
"openstack_objectstorage_container_v1": resourceObjectStorageContainerV1(),
},

Expand Down
7 changes: 7 additions & 0 deletions builtin/providers/openstack/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

var (
OS_REGION_NAME = ""
OS_POOL_NAME = ""
)

var testAccProviders map[string]terraform.ResourceProvider
Expand Down Expand Up @@ -49,6 +50,12 @@ func testAccPreCheck(t *testing.T) {
t.Fatal("OS_IMAGE_ID must be set for acceptance tests")
}

v = os.Getenv("OS_POOL_NAME")
if v == "" {
t.Fatal("OS_POOL_NAME must be set for acceptance tests")
}
OS_POOL_NAME = v

v = os.Getenv("OS_FLAVOR_ID")
if v == "" {
t.Fatal("OS_FLAVOR_ID must be set for acceptance tests")
Expand Down
150 changes: 149 additions & 1 deletion builtin/providers/openstack/resource_openstack_compute_instance_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import (
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
"github.com/rackspace/gophercloud/pagination"
)

Expand Down Expand Up @@ -48,6 +51,11 @@ func resourceComputeInstanceV2() *schema.Resource {
ForceNew: false,
DefaultFunc: envDefaultFunc("OS_FLAVOR_ID"),
},
"floating_ip": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"security_groups": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Expand Down Expand Up @@ -215,6 +223,22 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
"Error waiting for instance (%s) to become ready: %s",
server.ID, err)
}
floatingIP := d.Get("floating_ip").(string)
if floatingIP != "" {
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}

allFloatingIPs, err := getFloatingIPs(networkingClient)
if err != nil {
return fmt.Errorf("Error listing OpenStack floating IPs: %s", err)
}
err = assignFloatingIP(networkingClient, extractFloatingIPFromIP(allFloatingIPs, floatingIP), server.ID)
if err != nil {
fmt.Errorf("Error assigning floating IP to OpenStack compute instance: %s", err)
}
}

return resourceComputeInstanceV2Read(d, meta)
}
Expand Down Expand Up @@ -246,11 +270,25 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err
pa := paRaw.(map[string]interface{})
if pa["version"].(float64) == 4 {
host = pa["addr"].(string)
d.Set("access_ip_v4", host)
break
}
}
}
}

// If no host found, just get the first IP we find
if host == "" {
for _, networkAddresses := range server.Addresses {
for _, element := range networkAddresses.([]interface{}) {
address := element.(map[string]interface{})
if address["version"].(float64) == 4 {
host = address["addr"].(string)
break
}
}
}
}
d.Set("access_ip_v4", host)
d.Set("host", host)

log.Printf("host: %s", host)
Expand Down Expand Up @@ -361,6 +399,25 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e
}
}

if d.HasChange("floating_ip") {
floatingIP := d.Get("floating_ip").(string)
if floatingIP != "" {
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}

allFloatingIPs, err := getFloatingIPs(networkingClient)
if err != nil {
return fmt.Errorf("Error listing OpenStack floating IPs: %s", err)
}
err = assignFloatingIP(networkingClient, extractFloatingIPFromIP(allFloatingIPs, floatingIP), d.Id())
if err != nil {
fmt.Errorf("Error assigning floating IP to OpenStack compute instance: %s", err)
}
}
}

if d.HasChange("flavor_ref") {
resizeOpts := &servers.ResizeOpts{
FlavorRef: d.Get("flavor_ref").(string),
Expand Down Expand Up @@ -428,6 +485,7 @@ func resourceComputeInstanceV2Delete(d *schema.ResourceData, meta interface{}) e
log.Printf("[DEBUG] Waiting for instance (%s) to delete", d.Id())

stateConf := &resource.StateChangeConf{
Pending: []string{"ACTIVE"},
Target: "DELETED",
Refresh: ServerV2StateRefreshFunc(computeClient, d.Id()),
Timeout: 10 * time.Minute,
Expand Down Expand Up @@ -511,3 +569,93 @@ func resourceInstanceBlockDeviceV2(d *schema.ResourceData, bd map[string]interfa

return bfvOpts
}

func extractFloatingIPFromIP(ips []floatingips.FloatingIP, IP string) *floatingips.FloatingIP {
for _, floatingIP := range ips {
if floatingIP.FloatingIP == IP {
return &floatingIP
}
}
return nil
}

func assignFloatingIP(networkingClient *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, instanceID string) error {
networkID, err := getFirstNetworkID(networkingClient, instanceID)
if err != nil {
return err
}
portID, err := getInstancePortID(networkingClient, instanceID, networkID)
_, err = floatingips.Update(networkingClient, floatingIP.ID, floatingips.UpdateOpts{
PortID: portID,
}).Extract()
return err
}

func getFirstNetworkID(networkingClient *gophercloud.ServiceClient, instanceID string) (string, error) {
pager := networks.List(networkingClient, networks.ListOpts{})

var networkdID string
err := pager.EachPage(func(page pagination.Page) (bool, error) {
networkList, err := networks.ExtractNetworks(page)
if err != nil {
return false, err
}

if len(networkList) > 0 {
networkdID = networkList[0].ID
return false, nil
}
return false, fmt.Errorf("No network found for the instance %s", instanceID)
})
if err != nil {
return "", err
}
return networkdID, nil

}

func getInstancePortID(networkingClient *gophercloud.ServiceClient, instanceID, networkID string) (string, error) {
pager := ports.List(networkingClient, ports.ListOpts{
DeviceID: instanceID,
NetworkID: networkID,
})

var portID string
err := pager.EachPage(func(page pagination.Page) (bool, error) {
portList, err := ports.ExtractPorts(page)
if err != nil {
return false, err
}
for _, port := range portList {
portID = port.ID
return false, nil
}
return true, nil
})

if err != nil {
return "", err
}
return portID, nil
}

func getFloatingIPs(networkingClient *gophercloud.ServiceClient) ([]floatingips.FloatingIP, error) {
pager := floatingips.List(networkingClient, floatingips.ListOpts{})

ips := []floatingips.FloatingIP{}
err := pager.EachPage(func(page pagination.Page) (bool, error) {
floatingipList, err := floatingips.ExtractFloatingIPs(page)
if err != nil {
return false, err
}
for _, f := range floatingipList {
ips = append(ips, f)
}
return true, nil
})

if err != nil {
return nil, err
}
return ips, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package openstack

import (
"fmt"

"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/pagination"
)

func resourceNetworkingFloatingIPV2() *schema.Resource {
return &schema.Resource{
Create: resourceNetworkFloatingIPV2Create,
Read: resourceNetworkFloatingIPV2Read,
Delete: resourceNetworkFloatingIPV2Delete,

Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"address": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"pool": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}

func resourceNetworkFloatingIPV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack network client: %s", err)
}

poolID, err := getNetworkID(d, meta, d.Get("pool").(string))
if err != nil {
return fmt.Errorf("Error retrieving floating IP pool name: %s", err)
}
if len(poolID) == 0 {
return fmt.Errorf("No network found with name: %s", d.Get("pool").(string))
}
floatingIP, err := floatingips.Create(networkClient, floatingips.CreateOpts{
FloatingNetworkID: poolID,
}).Extract()
if err != nil {
return fmt.Errorf("Error allocating floating IP: %s", err)
}

d.SetId(floatingIP.ID)

return resourceNetworkFloatingIPV2Read(d, meta)
}

func resourceNetworkFloatingIPV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack network client: %s", err)
}

floatingIP, err := floatingips.Get(networkClient, d.Id()).Extract()
if err != nil {
return fmt.Errorf("Error retrieving floatingIP: %s", err)
}

d.Set("region", d.Get("region").(string))
d.Set("address", floatingIP.FloatingIP)
poolName, err := getNetworkName(d, meta, floatingIP.FloatingNetworkID)
if err != nil {
return fmt.Errorf("Error retrieving floating IP pool name: %s", err)
}
d.Set("pool", poolName)

return nil
}

func resourceNetworkFloatingIPV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack network client: %s", err)
}

err = floatingips.Delete(networkClient, d.Id()).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting floating IP: %s", err)
}
d.SetId("")
return nil
}

func getNetworkID(d *schema.ResourceData, meta interface{}, networkName string) (string, error) {
config := meta.(*Config)
networkClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return "", fmt.Errorf("Error creating OpenStack network client: %s", err)
}

opts := networks.ListOpts{Name: networkName}
pager := networks.List(networkClient, opts)
networkID := ""

err = pager.EachPage(func(page pagination.Page) (bool, error) {
networkList, err := networks.ExtractNetworks(page)
if err != nil {
return false, err
}

for _, n := range networkList {
if n.Name == networkName {
networkID = n.ID
return false, nil
}
}

return true, nil
})

return networkID, err
}

func getNetworkName(d *schema.ResourceData, meta interface{}, networkID string) (string, error) {
config := meta.(*Config)
networkClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return "", fmt.Errorf("Error creating OpenStack network client: %s", err)
}

opts := networks.ListOpts{ID: networkID}
pager := networks.List(networkClient, opts)
networkName := ""

err = pager.EachPage(func(page pagination.Page) (bool, error) {
networkList, err := networks.ExtractNetworks(page)
if err != nil {
return false, err
}

for _, n := range networkList {
if n.ID == networkID {
networkName = n.Name
return false, nil
}
}

return true, nil
})

return networkName, err
}
Loading