github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/openstack/resource_openstack_compute_volume_attach_v2.go (about)

     1  package openstack
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/gophercloud/gophercloud"
    10  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
    11  
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  func resourceComputeVolumeAttachV2() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceComputeVolumeAttachV2Create,
    19  		Read:   resourceComputeVolumeAttachV2Read,
    20  		Delete: resourceComputeVolumeAttachV2Delete,
    21  		Importer: &schema.ResourceImporter{
    22  			State: schema.ImportStatePassthrough,
    23  		},
    24  
    25  		Schema: map[string]*schema.Schema{
    26  			"region": &schema.Schema{
    27  				Type:        schema.TypeString,
    28  				Required:    true,
    29  				ForceNew:    true,
    30  				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
    31  			},
    32  
    33  			"instance_id": &schema.Schema{
    34  				Type:     schema.TypeString,
    35  				Required: true,
    36  				ForceNew: true,
    37  			},
    38  
    39  			"volume_id": &schema.Schema{
    40  				Type:     schema.TypeString,
    41  				Required: true,
    42  				ForceNew: true,
    43  			},
    44  
    45  			"device": &schema.Schema{
    46  				Type:     schema.TypeString,
    47  				Computed: true,
    48  				Optional: true,
    49  			},
    50  		},
    51  	}
    52  }
    53  
    54  func resourceComputeVolumeAttachV2Create(d *schema.ResourceData, meta interface{}) error {
    55  	config := meta.(*Config)
    56  	computeClient, err := config.computeV2Client(GetRegion(d))
    57  	if err != nil {
    58  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
    59  	}
    60  
    61  	instanceId := d.Get("instance_id").(string)
    62  	volumeId := d.Get("volume_id").(string)
    63  
    64  	var device string
    65  	if v, ok := d.GetOk("device"); ok {
    66  		device = v.(string)
    67  	}
    68  
    69  	attachOpts := volumeattach.CreateOpts{
    70  		Device:   device,
    71  		VolumeID: volumeId,
    72  	}
    73  
    74  	log.Printf("[DEBUG] Creating volume attachment: %#v", attachOpts)
    75  
    76  	attachment, err := volumeattach.Create(computeClient, instanceId, attachOpts).Extract()
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	stateConf := &resource.StateChangeConf{
    82  		Pending:    []string{"ATTACHING"},
    83  		Target:     []string{"ATTACHED"},
    84  		Refresh:    resourceComputeVolumeAttachV2AttachFunc(computeClient, instanceId, attachment.ID),
    85  		Timeout:    10 * time.Minute,
    86  		Delay:      30 * time.Second,
    87  		MinTimeout: 15 * time.Second,
    88  	}
    89  
    90  	if _, err = stateConf.WaitForState(); err != nil {
    91  		return fmt.Errorf("Error attaching OpenStack volume: %s", err)
    92  	}
    93  
    94  	log.Printf("[DEBUG] Created volume attachment: %#v", attachment)
    95  
    96  	// Use the instance ID and attachment ID as the resource ID.
    97  	// This is because an attachment cannot be retrieved just by its ID alone.
    98  	id := fmt.Sprintf("%s/%s", instanceId, attachment.ID)
    99  
   100  	d.SetId(id)
   101  
   102  	return resourceComputeVolumeAttachV2Read(d, meta)
   103  }
   104  
   105  func resourceComputeVolumeAttachV2Read(d *schema.ResourceData, meta interface{}) error {
   106  	config := meta.(*Config)
   107  	computeClient, err := config.computeV2Client(GetRegion(d))
   108  	if err != nil {
   109  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
   110  	}
   111  
   112  	instanceId, attachmentId, err := parseComputeVolumeAttachmentId(d.Id())
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	attachment, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract()
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	log.Printf("[DEBUG] Retrieved volume attachment: %#v", attachment)
   123  
   124  	d.Set("instance_id", attachment.ServerID)
   125  	d.Set("volume_id", attachment.VolumeID)
   126  	d.Set("device", attachment.Device)
   127  	d.Set("region", GetRegion(d))
   128  
   129  	return nil
   130  }
   131  
   132  func resourceComputeVolumeAttachV2Delete(d *schema.ResourceData, meta interface{}) error {
   133  	config := meta.(*Config)
   134  	computeClient, err := config.computeV2Client(GetRegion(d))
   135  	if err != nil {
   136  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
   137  	}
   138  
   139  	instanceId, attachmentId, err := parseComputeVolumeAttachmentId(d.Id())
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	stateConf := &resource.StateChangeConf{
   145  		Pending:    []string{""},
   146  		Target:     []string{"DETACHED"},
   147  		Refresh:    resourceComputeVolumeAttachV2DetachFunc(computeClient, instanceId, attachmentId),
   148  		Timeout:    10 * time.Minute,
   149  		Delay:      15 * time.Second,
   150  		MinTimeout: 15 * time.Second,
   151  	}
   152  
   153  	if _, err = stateConf.WaitForState(); err != nil {
   154  		return fmt.Errorf("Error detaching OpenStack volume: %s", err)
   155  	}
   156  
   157  	return nil
   158  }
   159  
   160  func resourceComputeVolumeAttachV2AttachFunc(
   161  	computeClient *gophercloud.ServiceClient, instanceId, attachmentId string) resource.StateRefreshFunc {
   162  	return func() (interface{}, string, error) {
   163  		va, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract()
   164  		if err != nil {
   165  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   166  				return va, "ATTACHING", nil
   167  			}
   168  			return va, "", err
   169  		}
   170  
   171  		return va, "ATTACHED", nil
   172  	}
   173  }
   174  
   175  func resourceComputeVolumeAttachV2DetachFunc(
   176  	computeClient *gophercloud.ServiceClient, instanceId, attachmentId string) resource.StateRefreshFunc {
   177  	return func() (interface{}, string, error) {
   178  		log.Printf("[DEBUG] Attempting to detach OpenStack volume %s from instance %s",
   179  			attachmentId, instanceId)
   180  
   181  		va, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract()
   182  		if err != nil {
   183  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   184  				return va, "DETACHED", nil
   185  			}
   186  			return va, "", err
   187  		}
   188  
   189  		err = volumeattach.Delete(computeClient, instanceId, attachmentId).ExtractErr()
   190  		if err != nil {
   191  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   192  				return va, "DETACHED", nil
   193  			}
   194  
   195  			if _, ok := err.(gophercloud.ErrDefault400); ok {
   196  				return nil, "", nil
   197  			}
   198  
   199  			return nil, "", err
   200  		}
   201  
   202  		log.Printf("[DEBUG] OpenStack Volume Attachment (%s) is still active.", attachmentId)
   203  		return nil, "", nil
   204  	}
   205  }
   206  
   207  func parseComputeVolumeAttachmentId(id string) (string, string, error) {
   208  	idParts := strings.Split(id, "/")
   209  	if len(idParts) < 2 {
   210  		return "", "", fmt.Errorf("Unable to determine volume attachment ID")
   211  	}
   212  
   213  	instanceId := idParts[0]
   214  	attachmentId := idParts[1]
   215  
   216  	return instanceId, attachmentId, nil
   217  }