github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/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  	log.Printf("[DEBUG] Created volume attachment: %#v", attachment)
    82  
    83  	// Use the instance ID and attachment ID as the resource ID.
    84  	// This is because an attachment cannot be retrieved just by its ID alone.
    85  	id := fmt.Sprintf("%s/%s", instanceId, attachment.ID)
    86  
    87  	d.SetId(id)
    88  
    89  	return resourceComputeVolumeAttachV2Read(d, meta)
    90  }
    91  
    92  func resourceComputeVolumeAttachV2Read(d *schema.ResourceData, meta interface{}) error {
    93  	config := meta.(*Config)
    94  	computeClient, err := config.computeV2Client(GetRegion(d))
    95  	if err != nil {
    96  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
    97  	}
    98  
    99  	instanceId, attachmentId, err := parseComputeVolumeAttachmentId(d.Id())
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	attachment, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract()
   105  	if err != nil {
   106  		return err
   107  	}
   108  
   109  	log.Printf("[DEBUG] Retrieved volume attachment: %#v", attachment)
   110  
   111  	d.Set("instance_id", attachment.ServerID)
   112  	d.Set("volume_id", attachment.VolumeID)
   113  	d.Set("device", attachment.Device)
   114  	d.Set("region", GetRegion(d))
   115  
   116  	return nil
   117  }
   118  
   119  func resourceComputeVolumeAttachV2Delete(d *schema.ResourceData, meta interface{}) error {
   120  	config := meta.(*Config)
   121  	computeClient, err := config.computeV2Client(GetRegion(d))
   122  	if err != nil {
   123  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
   124  	}
   125  
   126  	instanceId, attachmentId, err := parseComputeVolumeAttachmentId(d.Id())
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	stateConf := &resource.StateChangeConf{
   132  		Pending:    []string{""},
   133  		Target:     []string{"DETACHED"},
   134  		Refresh:    volumeDetachRefreshFunc(computeClient, instanceId, attachmentId),
   135  		Timeout:    10 * time.Minute,
   136  		Delay:      15 * time.Second,
   137  		MinTimeout: 15 * time.Second,
   138  	}
   139  
   140  	if _, err = stateConf.WaitForState(); err != nil {
   141  		return fmt.Errorf("Error detaching OpenStack volume: %s", err)
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  func volumeDetachRefreshFunc(computeClient *gophercloud.ServiceClient, instanceId, attachmentId string) resource.StateRefreshFunc {
   148  	return func() (interface{}, string, error) {
   149  		log.Printf("[DEBUG] Attempting to detach OpenStack volume %s from instance %s", attachmentId, instanceId)
   150  
   151  		va, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract()
   152  		if err != nil {
   153  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   154  				return va, "DETACHED", nil
   155  			}
   156  			return va, "", err
   157  		}
   158  
   159  		err = volumeattach.Delete(computeClient, instanceId, attachmentId).ExtractErr()
   160  		if err != nil {
   161  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   162  				return va, "DETACHED", nil
   163  			}
   164  
   165  			if _, ok := err.(gophercloud.ErrDefault400); ok {
   166  				return nil, "", nil
   167  			}
   168  
   169  			return nil, "", err
   170  		}
   171  
   172  		log.Printf("[DEBUG] OpenStack Volume Attachment (%s) is still active.", attachmentId)
   173  		return nil, "", nil
   174  	}
   175  }
   176  
   177  func parseComputeVolumeAttachmentId(id string) (string, string, error) {
   178  	idParts := strings.Split(id, "/")
   179  	if len(idParts) < 2 {
   180  		return "", "", fmt.Errorf("Unable to determine volume attachment ID")
   181  	}
   182  
   183  	instanceId := idParts[0]
   184  	attachmentId := idParts[1]
   185  
   186  	return instanceId, attachmentId, nil
   187  }