func detachVolumesFromInstance(computeClient *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, serverId string, vols []interface{}) error { for _, v := range vols { va := v.(map[string]interface{}) aId := va["id"].(string) log.Printf("[INFO] Attempting to detach volume %s", va["volume_id"]) if err := volumeattach.Delete(computeClient, serverId, aId).ExtractErr(); err != nil { return err } stateConf := &resource.StateChangeConf{ Pending: []string{"detaching", "in-use"}, Target: []string{"available"}, Refresh: VolumeV1StateRefreshFunc(blockClient, va["volume_id"].(string)), Timeout: 30 * time.Minute, Delay: 5 * time.Second, MinTimeout: 2 * time.Second, } if _, err := stateConf.WaitForState(); err != nil { return err } log.Printf("[INFO] Detached volume %s from instance %s", va["volume_id"], serverId) } return nil }
func volumeDetachRefreshFunc(computeClient *gophercloud.ServiceClient, instanceId, attachmentId string) resource.StateRefreshFunc { return func() (interface{}, string, error) { log.Printf("[DEBUG] Attempting to detach OpenStack volume %s from instance %s", attachmentId, instanceId) va, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return va, "DETACHED", nil } return va, "", err } err = volumeattach.Delete(computeClient, instanceId, attachmentId).ExtractErr() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return va, "DETACHED", nil } if _, ok := err.(gophercloud.ErrDefault400); ok { return nil, "", nil } return nil, "", err } log.Printf("[DEBUG] OpenStack Volume Attachment (%s) is still active.", attachmentId) return nil, "", nil } }
// DeleteVolumeAttachment will disconnect a volume from an instance. A fatal // error will occur if the volume failed to detach. This works best when used // as a deferred function. func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volumeAttachment *volumeattach.VolumeAttachment) { err := volumeattach.Delete(client, server.ID, volumeAttachment.VolumeID).ExtractErr() if err != nil { t.Fatalf("Unable to detach volume: %v", err) } if err := volumes.WaitForStatus(blockClient, volumeAttachment.ID, "available", 60); err != nil { t.Fatalf("Unable to wait for volume: %v", err) } t.Logf("Deleted volume: %s", volumeAttachment.VolumeID) }
func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) aID := "a26887c6-c47b-4654-abb5-dfadf7d3f804" serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0" err := volumeattach.Delete(client.ServiceClient(), serverID, aID).ExtractErr() th.AssertNoErr(t, err) }
func resourceBlockStorageVolumeV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) blockStorageClient, err := config.blockStorageV2Client(GetRegion(d)) if err != nil { return fmt.Errorf("Error creating OpenStack block storage client: %s", err) } v, err := volumes.Get(blockStorageClient, d.Id()).Extract() if err != nil { return CheckDeleted(d, err, "volume") } // make sure this volume is detached from all instances before deleting if len(v.Attachments) > 0 { log.Printf("[DEBUG] detaching volumes") if computeClient, err := config.computeV2Client(GetRegion(d)); err != nil { return err } else { for _, volumeAttachment := range v.Attachments { log.Printf("[DEBUG] Attachment: %v", volumeAttachment) if err := volumeattach.Delete(computeClient, volumeAttachment.ServerID, volumeAttachment.ID).ExtractErr(); err != nil { return err } } stateConf := &resource.StateChangeConf{ Pending: []string{"in-use", "attaching", "detaching"}, Target: []string{"available"}, Refresh: VolumeV2StateRefreshFunc(blockStorageClient, d.Id()), Timeout: 10 * time.Minute, Delay: 10 * time.Second, MinTimeout: 3 * time.Second, } _, err = stateConf.WaitForState() if err != nil { return fmt.Errorf( "Error waiting for volume (%s) to become available: %s", d.Id(), err) } } } // It's possible that this volume was used as a boot device and is currently // in a "deleting" state from when the instance was terminated. // If this is true, just move on. It'll eventually delete. if v.Status != "deleting" { if err := volumes.Delete(blockStorageClient, d.Id()).ExtractErr(); err != nil { return CheckDeleted(d, err, "volume") } } // Wait for the volume to delete before moving on. log.Printf("[DEBUG] Waiting for volume (%s) to delete", d.Id()) stateConf := &resource.StateChangeConf{ Pending: []string{"deleting", "downloading", "available"}, Target: []string{"deleted"}, Refresh: VolumeV2StateRefreshFunc(blockStorageClient, d.Id()), Timeout: 10 * time.Minute, Delay: 10 * time.Second, MinTimeout: 3 * time.Second, } _, err = stateConf.WaitForState() if err != nil { return fmt.Errorf( "Error waiting for volume (%s) to delete: %s", d.Id(), err) } d.SetId("") return nil }