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