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 }