github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/openstack/resource_openstack_compute_floatingip_associate_v2.go (about)

     1  package openstack
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/gophercloud/gophercloud"
     9  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
    10  	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
    11  	nfloatingips "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
    12  	"github.com/hashicorp/terraform/helper/schema"
    13  )
    14  
    15  func resourceComputeFloatingIPAssociateV2() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceComputeFloatingIPAssociateV2Create,
    18  		Read:   resourceComputeFloatingIPAssociateV2Read,
    19  		Delete: resourceComputeFloatingIPAssociateV2Delete,
    20  		Importer: &schema.ResourceImporter{
    21  			State: schema.ImportStatePassthrough,
    22  		},
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"region": &schema.Schema{
    26  				Type:        schema.TypeString,
    27  				Required:    true,
    28  				ForceNew:    true,
    29  				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
    30  			},
    31  			"floating_ip": &schema.Schema{
    32  				Type:     schema.TypeString,
    33  				Required: true,
    34  				ForceNew: true,
    35  			},
    36  			"instance_id": &schema.Schema{
    37  				Type:     schema.TypeString,
    38  				Required: true,
    39  				ForceNew: true,
    40  			},
    41  			"fixed_ip": &schema.Schema{
    42  				Type:     schema.TypeString,
    43  				Optional: true,
    44  				ForceNew: true,
    45  			},
    46  		},
    47  	}
    48  }
    49  
    50  func resourceComputeFloatingIPAssociateV2Create(d *schema.ResourceData, meta interface{}) error {
    51  	config := meta.(*Config)
    52  	computeClient, err := config.computeV2Client(GetRegion(d))
    53  	if err != nil {
    54  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
    55  	}
    56  
    57  	floatingIP := d.Get("floating_ip").(string)
    58  	fixedIP := d.Get("fixed_ip").(string)
    59  	instanceId := d.Get("instance_id").(string)
    60  
    61  	associateOpts := floatingips.AssociateOpts{
    62  		FloatingIP: floatingIP,
    63  		FixedIP:    fixedIP,
    64  	}
    65  	log.Printf("[DEBUG] Associate Options: %#v", associateOpts)
    66  
    67  	err = floatingips.AssociateInstance(computeClient, instanceId, associateOpts).ExtractErr()
    68  	if err != nil {
    69  		return fmt.Errorf("Error associating Floating IP: %s", err)
    70  	}
    71  
    72  	// There's an API call to get this information, but it has been
    73  	// deprecated. The Neutron API could be used, but I'm trying not
    74  	// to mix service APIs. Therefore, a faux ID will be used.
    75  	id := fmt.Sprintf("%s/%s/%s", floatingIP, instanceId, fixedIP)
    76  	d.SetId(id)
    77  
    78  	// This API call is synchronous, so Create won't return until the IP
    79  	// is attached. No need to wait for a state.
    80  
    81  	return resourceComputeFloatingIPAssociateV2Read(d, meta)
    82  }
    83  
    84  func resourceComputeFloatingIPAssociateV2Read(d *schema.ResourceData, meta interface{}) error {
    85  	config := meta.(*Config)
    86  	computeClient, err := config.computeV2Client(GetRegion(d))
    87  	if err != nil {
    88  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
    89  	}
    90  
    91  	// Obtain relevant info from parsing the ID
    92  	floatingIP, instanceId, fixedIP, err := parseComputeFloatingIPAssociateId(d.Id())
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	// Now check and see whether the floating IP still exists.
    98  	// First try to do this by querying the Network API.
    99  	networkEnabled := true
   100  	networkClient, err := config.networkingV2Client(GetRegion(d))
   101  	if err != nil {
   102  		networkEnabled = false
   103  	}
   104  
   105  	var exists bool
   106  	if networkEnabled {
   107  		log.Printf("[DEBUG] Checking for Floating IP existence via Network API")
   108  		exists, err = resourceComputeFloatingIPAssociateV2NetworkExists(networkClient, floatingIP)
   109  	} else {
   110  		log.Printf("[DEBUG] Checking for Floating IP existence via Compute API")
   111  		exists, err = resourceComputeFloatingIPAssociateV2ComputeExists(computeClient, floatingIP)
   112  	}
   113  
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	if !exists {
   119  		d.SetId("")
   120  	}
   121  
   122  	// Next, see if the instance still exists
   123  	instance, err := servers.Get(computeClient, instanceId).Extract()
   124  	if err != nil {
   125  		if CheckDeleted(d, err, "instance") == nil {
   126  			return nil
   127  		}
   128  	}
   129  
   130  	// Finally, check and see if the floating ip is still associated with the instance.
   131  	var associated bool
   132  	for _, networkAddresses := range instance.Addresses {
   133  		for _, element := range networkAddresses.([]interface{}) {
   134  			address := element.(map[string]interface{})
   135  			if address["OS-EXT-IPS:type"] == "floating" && address["addr"] == floatingIP {
   136  				associated = true
   137  			}
   138  		}
   139  	}
   140  
   141  	if !associated {
   142  		d.SetId("")
   143  	}
   144  
   145  	// Set the attributes pulled from the composed resource ID
   146  	d.Set("floating_ip", floatingIP)
   147  	d.Set("instance_id", instanceId)
   148  	d.Set("fixed_ip", fixedIP)
   149  	d.Set("region", GetRegion(d))
   150  
   151  	return nil
   152  }
   153  
   154  func resourceComputeFloatingIPAssociateV2Delete(d *schema.ResourceData, meta interface{}) error {
   155  	config := meta.(*Config)
   156  	computeClient, err := config.computeV2Client(GetRegion(d))
   157  	if err != nil {
   158  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
   159  	}
   160  
   161  	floatingIP := d.Get("floating_ip").(string)
   162  	instanceId := d.Get("instance_id").(string)
   163  
   164  	disassociateOpts := floatingips.DisassociateOpts{
   165  		FloatingIP: floatingIP,
   166  	}
   167  	log.Printf("[DEBUG] Disssociate Options: %#v", disassociateOpts)
   168  
   169  	err = floatingips.DisassociateInstance(computeClient, instanceId, disassociateOpts).ExtractErr()
   170  	if err != nil {
   171  		return CheckDeleted(d, err, "floating ip association")
   172  	}
   173  
   174  	return nil
   175  }
   176  
   177  func parseComputeFloatingIPAssociateId(id string) (string, string, string, error) {
   178  	idParts := strings.Split(id, "/")
   179  	if len(idParts) < 3 {
   180  		return "", "", "", fmt.Errorf("Unable to determine floating ip association ID")
   181  	}
   182  
   183  	floatingIP := idParts[0]
   184  	instanceId := idParts[1]
   185  	fixedIP := idParts[2]
   186  
   187  	return floatingIP, instanceId, fixedIP, nil
   188  }
   189  
   190  func resourceComputeFloatingIPAssociateV2NetworkExists(networkClient *gophercloud.ServiceClient, floatingIP string) (bool, error) {
   191  	listOpts := nfloatingips.ListOpts{
   192  		FloatingIP: floatingIP,
   193  	}
   194  	allPages, err := nfloatingips.List(networkClient, listOpts).AllPages()
   195  	if err != nil {
   196  		return false, err
   197  	}
   198  
   199  	allFips, err := nfloatingips.ExtractFloatingIPs(allPages)
   200  	if err != nil {
   201  		return false, err
   202  	}
   203  
   204  	if len(allFips) > 1 {
   205  		return false, fmt.Errorf("There was a problem retrieving the floating IP")
   206  	}
   207  
   208  	if len(allFips) == 0 {
   209  		return false, nil
   210  	}
   211  
   212  	return true, nil
   213  }
   214  
   215  func resourceComputeFloatingIPAssociateV2ComputeExists(computeClient *gophercloud.ServiceClient, floatingIP string) (bool, error) {
   216  	// If the Network API isn't available, fall back to the deprecated Compute API.
   217  	allPages, err := floatingips.List(computeClient).AllPages()
   218  	if err != nil {
   219  		return false, err
   220  	}
   221  
   222  	allFips, err := floatingips.ExtractFloatingIPs(allPages)
   223  	if err != nil {
   224  		return false, err
   225  	}
   226  
   227  	for _, f := range allFips {
   228  		if f.IP == floatingIP {
   229  			return true, nil
   230  		}
   231  	}
   232  
   233  	return false, nil
   234  }