github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/builtin/providers/openstack/resource_openstack_blockstorage_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/openstack/blockstorage/extensions/volumeactions"
    10  	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
    11  	"github.com/hashicorp/terraform/helper/resource"
    12  	"github.com/hashicorp/terraform/helper/schema"
    13  )
    14  
    15  func resourceBlockStorageVolumeAttachV2() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceBlockStorageVolumeAttachV2Create,
    18  		Read:   resourceBlockStorageVolumeAttachV2Read,
    19  		Delete: resourceBlockStorageVolumeAttachV2Delete,
    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  
    32  			"volume_id": &schema.Schema{
    33  				Type:     schema.TypeString,
    34  				Required: true,
    35  				ForceNew: true,
    36  			},
    37  
    38  			"instance_id": &schema.Schema{
    39  				Type:          schema.TypeString,
    40  				Optional:      true,
    41  				ForceNew:      true,
    42  				ConflictsWith: []string{"host_name"},
    43  			},
    44  
    45  			"host_name": &schema.Schema{
    46  				Type:          schema.TypeString,
    47  				Optional:      true,
    48  				ForceNew:      true,
    49  				ConflictsWith: []string{"instance_id"},
    50  			},
    51  
    52  			"device": &schema.Schema{
    53  				Type:     schema.TypeString,
    54  				Optional: true,
    55  				Computed: true,
    56  			},
    57  
    58  			"attach_mode": &schema.Schema{
    59  				Type:     schema.TypeString,
    60  				Optional: true,
    61  				ForceNew: true,
    62  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    63  					value := v.(string)
    64  					if value != "ro" && value != "rw" {
    65  						errors = append(errors, fmt.Errorf(
    66  							"Only 'ro' and 'rw' are supported values for 'attach_mode'"))
    67  					}
    68  					return
    69  				},
    70  			},
    71  		},
    72  	}
    73  }
    74  
    75  func resourceBlockStorageVolumeAttachV2Create(d *schema.ResourceData, meta interface{}) error {
    76  	config := meta.(*Config)
    77  	client, err := config.blockStorageV2Client(GetRegion(d))
    78  	if err != nil {
    79  		return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
    80  	}
    81  
    82  	// Check if either instance_id or host_name was set.
    83  	instanceId := d.Get("instance_id").(string)
    84  	hostName := d.Get("host_name").(string)
    85  	if instanceId == "" && hostName == "" {
    86  		return fmt.Errorf("One of 'instance_id' or 'host_name' must be set.")
    87  	}
    88  
    89  	volumeId := d.Get("volume_id").(string)
    90  
    91  	attachMode, err := blockStorageVolumeAttachV2AttachMode(d.Get("attach_mode").(string))
    92  	if err != nil {
    93  		return nil
    94  	}
    95  
    96  	attachOpts := &volumeactions.AttachOpts{
    97  		InstanceUUID: d.Get("instance_id").(string),
    98  		HostName:     d.Get("host_name").(string),
    99  		MountPoint:   d.Get("device").(string),
   100  		Mode:         attachMode,
   101  	}
   102  
   103  	log.Printf("[DEBUG] Attachment Options: %#v", attachOpts)
   104  
   105  	if err := volumeactions.Attach(client, volumeId, attachOpts).ExtractErr(); err != nil {
   106  		return err
   107  	}
   108  
   109  	// Wait for the volume to become available.
   110  	log.Printf("[DEBUG] Waiting for volume (%s) to become available", volumeId)
   111  
   112  	stateConf := &resource.StateChangeConf{
   113  		Pending:    []string{"available", "attaching"},
   114  		Target:     []string{"in-use"},
   115  		Refresh:    VolumeV2StateRefreshFunc(client, volumeId),
   116  		Timeout:    10 * time.Minute,
   117  		Delay:      10 * time.Second,
   118  		MinTimeout: 3 * time.Second,
   119  	}
   120  
   121  	_, err = stateConf.WaitForState()
   122  	if err != nil {
   123  		return fmt.Errorf("Error waiting for volume (%s) to become ready: %s", volumeId, err)
   124  	}
   125  
   126  	volume, err := volumes.Get(client, volumeId).Extract()
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	var attachmentId string
   132  	for _, attachment := range volume.Attachments {
   133  		if instanceId != "" && instanceId == attachment.ServerID {
   134  			attachmentId = attachment.AttachmentID
   135  		}
   136  
   137  		if hostName != "" && hostName == attachment.HostName {
   138  			attachmentId = attachment.AttachmentID
   139  		}
   140  	}
   141  
   142  	if attachmentId == "" {
   143  		return fmt.Errorf("Unable to determine attachment ID.")
   144  	}
   145  
   146  	// The ID must be a combination of the volume and attachment ID
   147  	// in order to import attachments.
   148  	id := fmt.Sprintf("%s/%s", volumeId, attachmentId)
   149  	d.SetId(id)
   150  
   151  	return resourceBlockStorageVolumeAttachV2Read(d, meta)
   152  }
   153  
   154  func resourceBlockStorageVolumeAttachV2Read(d *schema.ResourceData, meta interface{}) error {
   155  	config := meta.(*Config)
   156  	client, err := config.blockStorageV2Client(GetRegion(d))
   157  	if err != nil {
   158  		return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
   159  	}
   160  
   161  	volumeId, attachmentId, err := blockStorageVolumeAttachV2ParseId(d.Id())
   162  	if err != nil {
   163  		return err
   164  	}
   165  
   166  	volume, err := volumes.Get(client, volumeId).Extract()
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	log.Printf("[DEBUG] Retrieved volume %s: %#v", d.Id(), volume)
   172  
   173  	var attachment volumes.Attachment
   174  	for _, v := range volume.Attachments {
   175  		if attachmentId == v.AttachmentID {
   176  			attachment = v
   177  		}
   178  	}
   179  
   180  	log.Printf("[DEBUG] Retrieved volume attachment: %#v", attachment)
   181  
   182  	d.Set("volume_id", volumeId)
   183  	d.Set("attachment_id", attachmentId)
   184  	d.Set("device", attachment.Device)
   185  	d.Set("instance_id", attachment.ServerID)
   186  	d.Set("host_name", attachment.HostName)
   187  	d.Set("region", GetRegion(d))
   188  
   189  	return nil
   190  }
   191  
   192  func resourceBlockStorageVolumeAttachV2Delete(d *schema.ResourceData, meta interface{}) error {
   193  	config := meta.(*Config)
   194  	client, err := config.blockStorageV2Client(GetRegion(d))
   195  	if err != nil {
   196  		return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
   197  	}
   198  
   199  	volumeId, attachmentId, err := blockStorageVolumeAttachV2ParseId(d.Id())
   200  	if err != nil {
   201  		return err
   202  	}
   203  
   204  	detachOpts := volumeactions.DetachOpts{
   205  		AttachmentID: attachmentId,
   206  	}
   207  
   208  	log.Printf("[DEBUG] Detachment Options: %#v", detachOpts)
   209  
   210  	if err := volumeactions.Detach(client, volumeId, detachOpts).ExtractErr(); err != nil {
   211  		return err
   212  	}
   213  
   214  	stateConf := &resource.StateChangeConf{
   215  		Pending:    []string{"in-use", "attaching", "detaching"},
   216  		Target:     []string{"available"},
   217  		Refresh:    VolumeV2StateRefreshFunc(client, volumeId),
   218  		Timeout:    10 * time.Minute,
   219  		Delay:      10 * time.Second,
   220  		MinTimeout: 3 * time.Second,
   221  	}
   222  
   223  	_, err = stateConf.WaitForState()
   224  	if err != nil {
   225  		return fmt.Errorf("Error waiting for volume (%s) to become available: %s", volumeId, err)
   226  	}
   227  
   228  	return nil
   229  }
   230  
   231  func blockStorageVolumeAttachV2AttachMode(v string) (volumeactions.AttachMode, error) {
   232  	var attachMode volumeactions.AttachMode
   233  	var attachError error
   234  	switch v {
   235  	case "":
   236  		attachMode = ""
   237  	case "ro":
   238  		attachMode = volumeactions.ReadOnly
   239  	case "rw":
   240  		attachMode = volumeactions.ReadWrite
   241  	default:
   242  		attachError = fmt.Errorf("Invalid attach_mode specified")
   243  	}
   244  
   245  	return attachMode, attachError
   246  }
   247  
   248  func blockStorageVolumeAttachV2ParseId(id string) (string, string, error) {
   249  	parts := strings.Split(id, "/")
   250  	if len(parts) < 2 {
   251  		return "", "", fmt.Errorf("Unable to determine attachment ID")
   252  	}
   253  
   254  	return parts[0], parts[1], nil
   255  }