Block Storage Acceptance Test Cleanup (#43)

diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go
index 6075fbc..822945f 100644
--- a/acceptance/clients/clients.go
+++ b/acceptance/clients/clients.go
@@ -96,6 +96,25 @@
 	})
 }
 
+// NewBlockStorageV2Client returns a *ServiceClient for making calls
+// to the OpenStack Block Storage v2 API. An error will be returned
+// if authentication or client creation was not possible.
+func NewBlockStorageV2Client() (*gophercloud.ServiceClient, error) {
+	ao, err := openstack.AuthOptionsFromEnv()
+	if err != nil {
+		return nil, err
+	}
+
+	client, err := openstack.AuthenticatedClient(ao)
+	if err != nil {
+		return nil, err
+	}
+
+	return openstack.NewBlockStorageV2(client, gophercloud.EndpointOpts{
+		Region: os.Getenv("OS_REGION_NAME"),
+	})
+}
+
 // NewComputeV2Client returns a *ServiceClient for making calls
 // to the OpenStack Compute v2 API. An error will be returned
 // if authentication or client creation was not possible.
diff --git a/acceptance/openstack/blockstorage/v1/.blockstorage.go.swp b/acceptance/openstack/blockstorage/v1/.blockstorage.go.swp
new file mode 100644
index 0000000..0b710e7
--- /dev/null
+++ b/acceptance/openstack/blockstorage/v1/.blockstorage.go.swp
Binary files differ
diff --git a/acceptance/openstack/blockstorage/v1/blockstorage.go b/acceptance/openstack/blockstorage/v1/blockstorage.go
new file mode 100644
index 0000000..3d4c9f1
--- /dev/null
+++ b/acceptance/openstack/blockstorage/v1/blockstorage.go
@@ -0,0 +1,183 @@
+// Package v1 contains common functions for creating block storage based
+// resources for use in acceptance tests. See the `*_test.go` files for
+// example usages.
+package v1
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes"
+)
+
+// CreateSnapshot will create a volume snapshot based off of a given volume and
+// with a random name. An error will be returned if the snapshot failed to be
+// created.
+func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) {
+	if testing.Short() {
+		t.Skip("Skipping test that requires snapshot creation in short mode.")
+	}
+
+	snapshotName := tools.RandomString("ACPTTEST", 16)
+	t.Logf("Attempting to create snapshot %s based on volume %s", snapshotName, volume.ID)
+
+	createOpts := snapshots.CreateOpts{
+		Name:     snapshotName,
+		VolumeID: volume.ID,
+	}
+
+	snapshot, err := snapshots.Create(client, createOpts).Extract()
+	if err != nil {
+		return snapshot, err
+	}
+
+	err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60)
+	if err != nil {
+		return snapshot, err
+	}
+
+	return snapshot, nil
+}
+
+// CreateVolume will create a volume with a random name and size of 1GB. An
+// error will be returned if the volume was unable to be created.
+func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) {
+	if testing.Short() {
+		t.Skip("Skipping test that requires volume creation in short mode.")
+	}
+
+	volumeName := tools.RandomString("ACPTTEST", 16)
+	t.Logf("Attempting to create volume: %s", volumeName)
+
+	createOpts := volumes.CreateOpts{
+		Size: 1,
+		Name: volumeName,
+	}
+
+	volume, err := volumes.Create(client, createOpts).Extract()
+	if err != nil {
+		return volume, err
+	}
+
+	err = volumes.WaitForStatus(client, volume.ID, "available", 60)
+	if err != nil {
+		return volume, err
+	}
+
+	return volume, nil
+}
+
+// CreateVolumeType will create a volume type with a random name. An error will
+// be returned if the volume type was unable to be created.
+func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) {
+	volumeTypeName := tools.RandomString("ACPTTEST", 16)
+	t.Logf("Attempting to create volume type: %s", volumeTypeName)
+
+	createOpts := volumetypes.CreateOpts{
+		Name: volumeTypeName,
+		ExtraSpecs: map[string]interface{}{
+			"capabilities": "ssd",
+			"priority":     3,
+		},
+	}
+
+	volumeType, err := volumetypes.Create(client, createOpts).Extract()
+	if err != nil {
+		return volumeType, err
+	}
+
+	return volumeType, nil
+}
+
+// DeleteSnapshot will delete a snapshot. A fatal error will occur if the
+// snapshot failed to be deleted. This works best when used as a deferred
+// function.
+func DeleteSnapshotshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) {
+	err := snapshots.Delete(client, snapshot.ID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete snapshot %s: %v", snapshot.ID, err)
+	}
+
+	// Volumes can't be deleted until their snapshots have been,
+	// so block up to 120 seconds for the snapshot to delete.
+	err = gophercloud.WaitFor(120, func() (bool, error) {
+		_, err := snapshots.Get(client, snapshot.ID).Extract()
+		if err != nil {
+			return true, nil
+		}
+
+		return false, nil
+	})
+	if err != nil {
+		t.Fatalf("Unable to wait for snapshot to delete: %v", err)
+	}
+
+	t.Logf("Deleted snapshot: %s", snapshot.ID)
+}
+
+// DeleteVolume will delete a volume. A fatal error will occur if the volume
+// failed to be deleted. This works best when used as a deferred function.
+func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) {
+	err := volumes.Delete(client, volume.ID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete volume %s: %v", volume.ID, err)
+	}
+
+	t.Logf("Deleted volume: %s", volume.ID)
+}
+
+// DeleteVolumeType will delete a volume type. A fatal error will occur if the
+// volume type failed to be deleted. This works best when used as a deferred
+// function.
+func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, volumeType *volumetypes.VolumeType) {
+	err := volumetypes.Delete(client, volumeType.ID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete volume type %s: %v", volumeType.ID, err)
+	}
+
+	t.Logf("Deleted volume type: %s", volumeType.ID)
+}
+
+// PrintVolume will print a volume and all of its attributes.
+func PrintVolume(t *testing.T, volume *volumes.Volume) {
+	t.Logf("ID: %s", volume.ID)
+	t.Logf("Status: %s", volume.Status)
+	t.Logf("Name: %s", volume.Name)
+	t.Logf("AvailabilityZone: %s", volume.AvailabilityZone)
+	t.Logf("Bootable: %s", volume.Bootable)
+	t.Logf("CreatedAt: %v", volume.CreatedAt)
+	t.Logf("Description: %s", volume.Description)
+	t.Logf("VolumeType: %s", volume.VolumeType)
+	t.Logf("SnapshotID: %s", volume.SnapshotID)
+	t.Logf("SourceVolID: %s", volume.SourceVolID)
+	t.Logf("Size: %d", volume.Size)
+	t.Logf("Metadata: %#v", volume.Metadata)
+	t.Logf("Attachments: %#v", volume.Attachments)
+}
+
+// PrintVolumeType will print a volume type and all of its attributes.
+func PrintVolumeType(t *testing.T, volumeType *volumetypes.VolumeType) {
+	t.Logf("ID: %s", volumeType.ID)
+	t.Logf("Name: %s", volumeType.Name)
+	t.Logf("ExtraSpecs: %#v", volumeType.ExtraSpecs)
+}
+
+// PrintSnapshot will print a snapshot and all of its attributes.
+func PrintSnapshot(t *testing.T, snapshot *snapshots.Snapshot) {
+	t.Logf("ID: %s", snapshot.ID)
+	t.Logf("Status: %s", snapshot.Status)
+	t.Logf("Name: %s", snapshot.Name)
+	t.Logf("AvailabilityZone: %s", snapshot.AvailabilityZone)
+	t.Logf("Bootable: %s", snapshot.Bootable)
+	t.Logf("CreatedAt: %v", snapshot.CreatedAt)
+	t.Logf("Description: %s", snapshot.Description)
+	t.Logf("VolumeType: %s", snapshot.VolumeType)
+	t.Logf("SnapshotID: %s", snapshot.SnapshotID)
+	t.Logf("VolumeID: %s", snapshot.VolumeID)
+	t.Logf("Size: %d", snapshot.Size)
+	t.Logf("Metadata: %#v", snapshot.Metadata)
+	t.Logf("Attachments: %#v", snapshot.Attachments)
+}
diff --git a/acceptance/openstack/blockstorage/v1/snapshots_test.go b/acceptance/openstack/blockstorage/v1/snapshots_test.go
index 2b737e7..2dc58db 100644
--- a/acceptance/openstack/blockstorage/v1/snapshots_test.go
+++ b/acceptance/openstack/blockstorage/v1/snapshots_test.go
@@ -1,70 +1,57 @@
-// +build acceptance
+// +build acceptance blockstorage
 
 package v1
 
 import (
 	"testing"
 
-	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
 	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots"
-	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
-	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
-func TestSnapshots(t *testing.T) {
+func TestSnapshotsList(t *testing.T) {
+	client, err := clients.NewBlockStorageV1Client()
+	if err != nil {
+		t.Fatalf("Unable to create a blockstorage client: %v", err)
+	}
 
-	client, err := newClient(t)
-	th.AssertNoErr(t, err)
+	allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to retrieve snapshots: %v", err)
+	}
 
-	v, err := volumes.Create(client, &volumes.CreateOpts{
-		Name: "gophercloud-test-volume",
-		Size: 1,
-	}).Extract()
-	th.AssertNoErr(t, err)
+	allSnapshots, err := snapshots.ExtractSnapshots(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract snapshots: %v", err)
+	}
 
-	err = volumes.WaitForStatus(client, v.ID, "available", 120)
-	th.AssertNoErr(t, err)
+	for _, snapshot := range allSnapshots {
+		PrintSnapshot(t, &snapshot)
+	}
+}
 
-	t.Logf("Created volume: %v\n", v)
+func TestSnapshotsCreateDelete(t *testing.T) {
+	client, err := clients.NewBlockStorageV1Client()
+	if err != nil {
+		t.Fatalf("Unable to create a blockstorage client: %v", err)
+	}
 
-	ss, err := snapshots.Create(client, &snapshots.CreateOpts{
-		Name:     "gophercloud-test-snapshot",
-		VolumeID: v.ID,
-	}).Extract()
-	th.AssertNoErr(t, err)
+	volume, err := CreateVolume(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create volume: %v", err)
+	}
+	defer DeleteVolume(t, client, volume)
 
-	err = snapshots.WaitForStatus(client, ss.ID, "available", 120)
-	th.AssertNoErr(t, err)
+	snapshot, err := CreateSnapshot(t, client, volume)
+	if err != nil {
+		t.Fatalf("Unable to create snapshot: %v", err)
+	}
+	defer DeleteSnapshotshot(t, client, snapshot)
 
-	t.Logf("Created snapshot: %+v\n", ss)
+	newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract()
+	if err != nil {
+		t.Errorf("Unable to retrieve snapshot: %v", err)
+	}
 
-	err = snapshots.Delete(client, ss.ID).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	err = gophercloud.WaitFor(120, func() (bool, error) {
-		_, err := snapshots.Get(client, ss.ID).Extract()
-		if err != nil {
-			return true, nil
-		}
-
-		return false, nil
-	})
-	th.AssertNoErr(t, err)
-
-	t.Log("Deleted snapshot\n")
-
-	err = volumes.Delete(client, v.ID).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	err = gophercloud.WaitFor(120, func() (bool, error) {
-		_, err := volumes.Get(client, v.ID).Extract()
-		if err != nil {
-			return true, nil
-		}
-
-		return false, nil
-	})
-	th.AssertNoErr(t, err)
-
-	t.Log("Deleted volume\n")
+	PrintSnapshot(t, newSnapshot)
 }
diff --git a/acceptance/openstack/blockstorage/v1/volumes_test.go b/acceptance/openstack/blockstorage/v1/volumes_test.go
index a7bf123..e375c59 100644
--- a/acceptance/openstack/blockstorage/v1/volumes_test.go
+++ b/acceptance/openstack/blockstorage/v1/volumes_test.go
@@ -3,61 +3,49 @@
 package v1
 
 import (
-	"os"
 	"testing"
 
-	"github.com/gophercloud/gophercloud"
-	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
 	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
-func newClient(t *testing.T) (*gophercloud.ServiceClient, error) {
-	ao, err := openstack.AuthOptionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	client, err := openstack.AuthenticatedClient(ao)
-	th.AssertNoErr(t, err)
-
-	return openstack.NewBlockStorageV1(client, gophercloud.EndpointOpts{
-		Region: os.Getenv("OS_REGION_NAME"),
-	})
-}
-
-func TestVolumes(t *testing.T) {
-	client, err := newClient(t)
-	th.AssertNoErr(t, err)
-
-	cv, err := volumes.Create(client, &volumes.CreateOpts{
-		Size: 1,
-		Name: "gophercloud-test-volume",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	defer func() {
-		err = volumes.WaitForStatus(client, cv.ID, "available", 60)
-		th.AssertNoErr(t, err)
-		err = volumes.Delete(client, cv.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-	}()
-
-	_, err = volumes.Update(client, cv.ID, &volumes.UpdateOpts{
-		Name: "gophercloud-updated-volume",
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	v, err := volumes.Get(client, cv.ID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Got volume: %+v\n", v)
-
-	if v.Name != "gophercloud-updated-volume" {
-		t.Errorf("Unable to update volume: Expected name: gophercloud-updated-volume\nActual name: %s", v.Name)
+func TestVolumesList(t *testing.T) {
+	client, err := clients.NewBlockStorageV1Client()
+	if err != nil {
+		t.Fatalf("Unable to create a blockstorage client: %v", err)
 	}
 
-	err = volumes.List(client, &volumes.ListOpts{Name: "gophercloud-updated-volume"}).EachPage(func(page pagination.Page) (bool, error) {
-		vols, err := volumes.ExtractVolumes(page)
-		th.CheckEquals(t, 1, len(vols))
-		return true, err
-	})
-	th.AssertNoErr(t, err)
+	allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to retrieve volumes: %v", err)
+	}
+
+	allVolumes, err := volumes.ExtractVolumes(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract volumes: %v", err)
+	}
+
+	for _, volume := range allVolumes {
+		PrintVolume(t, &volume)
+	}
+}
+
+func TestVolumesCreateDestroy(t *testing.T) {
+	client, err := clients.NewBlockStorageV1Client()
+	if err != nil {
+		t.Fatalf("Unable to create blockstorage client: %v", err)
+	}
+
+	volume, err := CreateVolume(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create volume: %v", err)
+	}
+	defer DeleteVolume(t, client, volume)
+
+	newVolume, err := volumes.Get(client, volume.ID).Extract()
+	if err != nil {
+		t.Errorf("Unable to retrieve volume: %v", err)
+	}
+
+	PrintVolume(t, newVolume)
 }
diff --git a/acceptance/openstack/blockstorage/v1/volumetypes_test.go b/acceptance/openstack/blockstorage/v1/volumetypes_test.go
index ebfa3de..843ea21 100644
--- a/acceptance/openstack/blockstorage/v1/volumetypes_test.go
+++ b/acceptance/openstack/blockstorage/v1/volumetypes_test.go
@@ -1,49 +1,46 @@
-// +build acceptance
+// +build acceptance blockstorage
 
 package v1
 
 import (
 	"testing"
-	"time"
 
+	"github.com/gophercloud/gophercloud/acceptance/clients"
 	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
-func TestVolumeTypes(t *testing.T) {
-	client, err := newClient(t)
-	th.AssertNoErr(t, err)
+func TestVolumeTypesList(t *testing.T) {
+	client, err := clients.NewBlockStorageV1Client()
+	if err != nil {
+		t.Fatalf("Unable to create a blockstorage client: %v", err)
+	}
 
-	vt, err := volumetypes.Create(client, &volumetypes.CreateOpts{
-		ExtraSpecs: map[string]interface{}{
-			"capabilities": "gpu",
-			"priority":     3,
-		},
-		Name: "gophercloud-test-volumeType",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	defer func() {
-		time.Sleep(10000 * time.Millisecond)
-		err = volumetypes.Delete(client, vt.ID).ExtractErr()
-		if err != nil {
-			t.Error(err)
-			return
-		}
-	}()
-	t.Logf("Created volume type: %+v\n", vt)
+	allPages, err := volumetypes.List(client).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to retrieve volume types: %v", err)
+	}
 
-	vt, err = volumetypes.Get(client, vt.ID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Got volume type: %+v\n", vt)
+	allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract volume types: %v", err)
+	}
 
-	err = volumetypes.List(client).EachPage(func(page pagination.Page) (bool, error) {
-		volTypes, err := volumetypes.ExtractVolumeTypes(page)
-		if len(volTypes) != 1 {
-			t.Errorf("Expected 1 volume type, got %d", len(volTypes))
-		}
-		t.Logf("Listing volume types: %+v\n", volTypes)
-		return true, err
-	})
-	th.AssertNoErr(t, err)
+	for _, volumeType := range allVolumeTypes {
+		PrintVolumeType(t, &volumeType)
+	}
+}
+
+func TestVolumeTypesCreateDestroy(t *testing.T) {
+	client, err := clients.NewBlockStorageV1Client()
+	if err != nil {
+		t.Fatalf("Unable to create a blockstorage client: %v", err)
+	}
+
+	volumeType, err := CreateVolumeType(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create volume type: %v", err)
+	}
+	defer DeleteVolumeType(t, client, volumeType)
+
+	PrintVolumeType(t, volumeType)
 }
diff --git a/acceptance/openstack/blockstorage/v2/blockstorage.go b/acceptance/openstack/blockstorage/v2/blockstorage.go
new file mode 100644
index 0000000..e007c2a
--- /dev/null
+++ b/acceptance/openstack/blockstorage/v2/blockstorage.go
@@ -0,0 +1,74 @@
+// Package v2 contains common functions for creating block storage based
+// resources for use in acceptance tests. See the `*_test.go` files for
+// example usages.
+package v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
+)
+
+// CreateVolume will create a volume with a random name and size of 1GB. An
+// error will be returned if the volume was unable to be created.
+func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) {
+	if testing.Short() {
+		t.Skip("Skipping test that requires volume creation in short mode.")
+	}
+
+	volumeName := tools.RandomString("ACPTTEST", 16)
+	t.Logf("Attempting to create volume: %s", volumeName)
+
+	createOpts := volumes.CreateOpts{
+		Size: 1,
+		Name: volumeName,
+	}
+
+	volume, err := volumes.Create(client, createOpts).Extract()
+	if err != nil {
+		return volume, err
+	}
+
+	err = volumes.WaitForStatus(client, volume.ID, "available", 60)
+	if err != nil {
+		return volume, err
+	}
+
+	return volume, nil
+}
+
+// DeleteVolume will delete a volume. A fatal error will occur if the volume
+// failed to be deleted. This works best when used as a deferred function.
+func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) {
+	err := volumes.Delete(client, volume.ID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete volume %s: %v", volume.ID, err)
+	}
+
+	t.Logf("Deleted volume: %s", volume.ID)
+}
+
+// PrintVolume will print a volume and all of its attributes.
+func PrintVolume(t *testing.T, volume *volumes.Volume) {
+	t.Logf("ID: %s", volume.ID)
+	t.Logf("Status: %s", volume.Status)
+	t.Logf("Size: %d", volume.Size)
+	t.Logf("AvailabilityZone: %s", volume.AvailabilityZone)
+	t.Logf("CreatedAt: %v", volume.CreatedAt)
+	t.Logf("UpdatedAt: %v", volume.CreatedAt)
+	t.Logf("Attachments: %#v", volume.Attachments)
+	t.Logf("Name: %s", volume.Name)
+	t.Logf("Description: %s", volume.Description)
+	t.Logf("VolumeType: %s", volume.VolumeType)
+	t.Logf("SnapshotID: %s", volume.SnapshotID)
+	t.Logf("SourceVolID: %s", volume.SourceVolID)
+	t.Logf("Metadata: %#v", volume.Metadata)
+	t.Logf("UserID: %s", volume.UserID)
+	t.Logf("Bootable: %s", volume.Bootable)
+	t.Logf("Encrypted: %s", volume.Encrypted)
+	t.Logf("ReplicationStatus: %s", volume.ReplicationStatus)
+	t.Logf("ConsistencyGroupID: %s", volume.ConsistencyGroupID)
+	t.Logf("Multiattach: %t", volume.Multiattach)
+}
diff --git a/acceptance/openstack/blockstorage/v2/extensions/extensions.go b/acceptance/openstack/blockstorage/v2/extensions/extensions.go
new file mode 100644
index 0000000..685f769
--- /dev/null
+++ b/acceptance/openstack/blockstorage/v2/extensions/extensions.go
@@ -0,0 +1,97 @@
+// Package extensions contains common functions for creating block storage
+// resources that are extensions of the block storage API. See the `*_test.go`
+// files for example usages.
+package extensions
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/extensions/volumeactions"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+)
+
+// CreateVolumeAttach will attach a volume to an instance. An error will be
+// returned if the attachment failed.
+func CreateVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, server *servers.Server) error {
+	if testing.Short() {
+		t.Skip("Skipping test that requires volume attachment in short mode.")
+	}
+
+	attachOpts := volumeactions.AttachOpts{
+		MountPoint:   "/mnt",
+		Mode:         "rw",
+		InstanceUUID: server.ID,
+	}
+
+	t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID)
+
+	if err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr(); err != nil {
+		return err
+	}
+
+	if err := volumes.WaitForStatus(client, volume.ID, "in-use", 60); err != nil {
+		return err
+	}
+
+	t.Logf("Attached volume %s to server %s", volume.ID, server.ID)
+
+	return nil
+}
+
+// CreateVolumeReserve creates a volume reservation. An error will be returned
+// if the reservation failed.
+func CreateVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error {
+	if testing.Short() {
+		t.Skip("Skipping test that requires volume reservation in short mode.")
+	}
+
+	t.Logf("Attempting to reserve volume %s", volume.ID)
+
+	if err := volumeactions.Reserve(client, volume.ID).ExtractErr(); err != nil {
+		return err
+	}
+
+	t.Logf("Reserved volume %s", volume.ID)
+
+	return nil
+}
+
+// DeleteVolumeAttach will detach a volume from an instance. A fatal error will
+// occur if the snapshot failed to be deleted. This works best when used as a
+// deferred function.
+func DeleteVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) {
+	t.Logf("Attepting to detach volume volume: %s", volume.ID)
+
+	detachOpts := volumeactions.DetachOpts{
+		AttachmentID: volume.Attachments[0].AttachmentID,
+	}
+
+	if err := volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr(); err != nil {
+		t.Fatalf("Unable to detach volume %s: %v", volume.ID, err)
+	}
+
+	if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil {
+		t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err)
+	}
+
+	t.Logf("Detached volume: %s", volume.ID)
+}
+
+// DeleteVolumeReserve deletes a volume reservation. A fatal error will occur
+// if the deletion request failed. This works best when used as a deferred
+// function.
+func DeleteVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) {
+	if testing.Short() {
+		t.Skip("Skipping test that requires volume reservation in short mode.")
+	}
+
+	t.Logf("Attempting to unreserve volume %s", volume.ID)
+
+	if err := volumeactions.Unreserve(client, volume.ID).ExtractErr(); err != nil {
+		t.Fatalf("Unable to unreserve volume %s: %v", volume.ID, err)
+	}
+
+	t.Logf("Unreserved volume %s", volume.ID)
+}
diff --git a/acceptance/openstack/blockstorage/v2/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/v2/extensions/volumeactions_test.go
index 20a4597..83d1571 100644
--- a/acceptance/openstack/blockstorage/v2/extensions/volumeactions_test.go
+++ b/acceptance/openstack/blockstorage/v2/extensions/volumeactions_test.go
@@ -3,147 +3,116 @@
 package extensions
 
 import (
-	"os"
 	"testing"
 
-	"github.com/gophercloud/gophercloud"
-	"github.com/gophercloud/gophercloud/openstack"
-	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/extensions/volumeactions"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
 	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
-	th "github.com/gophercloud/gophercloud/testhelper"
+
+	blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2"
+	compute "github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2"
 )
 
-func newClient(t *testing.T) (*gophercloud.ServiceClient, error) {
-	ao, err := openstack.AuthOptionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	client, err := openstack.AuthenticatedClient(ao)
-	th.AssertNoErr(t, err)
-
-	return openstack.NewBlockStorageV2(client, gophercloud.EndpointOpts{
-		Region: os.Getenv("OS_REGION_NAME"),
-	})
-}
-
-func TestVolumeAttach(t *testing.T) {
-	client, err := newClient(t)
-	th.AssertNoErr(t, err)
-
-	t.Logf("Creating volume")
-	cv, err := volumes.Create(client, &volumes.CreateOpts{
-		Size: 1,
-		Name: "blockv2-volume",
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	defer func() {
-		err = volumes.WaitForStatus(client, cv.ID, "available", 60)
-		th.AssertNoErr(t, err)
-
-		t.Logf("Deleting volume")
-		err = volumes.Delete(client, cv.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-	}()
-
-	err = volumes.WaitForStatus(client, cv.ID, "available", 60)
-	th.AssertNoErr(t, err)
-
-	instanceID := os.Getenv("OS_INSTANCE_ID")
-	if instanceID == "" {
-		t.Fatal("Environment variable OS_INSTANCE_ID is required")
+func TestVolumeActionsAttachCreateDestroy(t *testing.T) {
+	blockClient, err := clients.NewBlockStorageV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a blockstorage client: %v", err)
 	}
 
-	t.Logf("Attaching volume")
-	err = volumeactions.Attach(client, cv.ID, &volumeactions.AttachOpts{
-		MountPoint:   "/mnt",
-		Mode:         "rw",
-		InstanceUUID: instanceID,
-	}).ExtractErr()
-	th.AssertNoErr(t, err)
+	computeClient, err := clients.NewComputeV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a compute client: %v", err)
+	}
 
-	err = volumes.WaitForStatus(client, cv.ID, "in-use", 60)
-	th.AssertNoErr(t, err)
+	choices, err := clients.AcceptanceTestChoicesFromEnv()
+	if err != nil {
+		t.Fatal(err)
+	}
 
-	t.Logf("Detaching volume")
-	err = volumeactions.Detach(client, cv.ID).ExtractErr()
-	th.AssertNoErr(t, err)
+	server, err := compute.CreateServer(t, computeClient, choices)
+	if err != nil {
+		t.Fatalf("Unable to create server: %v", err)
+	}
+	defer compute.DeleteServer(t, computeClient, server)
+
+	volume, err := blockstorage.CreateVolume(t, blockClient)
+	if err != nil {
+		t.Fatalf("Unable to create volume: %v", err)
+	}
+	defer blockstorage.DeleteVolume(t, blockClient, volume)
+
+	err = CreateVolumeAttach(t, blockClient, volume, server)
+	if err != nil {
+		t.Fatalf("Unable to attach volume: %v", err)
+	}
+
+	newVolume, err := volumes.Get(blockClient, volume.ID).Extract()
+	if err != nil {
+		t.Fatal("Unable to get updated volume information: %v", err)
+	}
+
+	DeleteVolumeAttach(t, blockClient, newVolume)
 }
 
-func TestVolumeReserve(t *testing.T) {
-	client, err := newClient(t)
-	th.AssertNoErr(t, err)
+func TestVolumeActionsReserveUnreserve(t *testing.T) {
+	client, err := clients.NewBlockStorageV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create blockstorage client: %v", err)
+	}
 
-	t.Logf("Creating volume")
-	cv, err := volumes.Create(client, &volumes.CreateOpts{
-		Size: 1,
-		Name: "blockv2-volume",
-	}).Extract()
-	th.AssertNoErr(t, err)
+	volume, err := blockstorage.CreateVolume(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create volume: %v", err)
+	}
+	defer blockstorage.DeleteVolume(t, client, volume)
 
-	defer func() {
-		err = volumes.WaitForStatus(client, cv.ID, "available", 60)
-		th.AssertNoErr(t, err)
-
-		t.Logf("Deleting volume")
-		err = volumes.Delete(client, cv.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-	}()
-
-	err = volumes.WaitForStatus(client, cv.ID, "available", 60)
-	th.AssertNoErr(t, err)
-
-	t.Logf("Reserving volume")
-	err = volumeactions.Reserve(client, cv.ID).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	err = volumes.WaitForStatus(client, cv.ID, "attaching", 60)
-	th.AssertNoErr(t, err)
-
-	t.Logf("Unreserving volume")
-	err = volumeactions.Unreserve(client, cv.ID).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	err = volumes.WaitForStatus(client, cv.ID, "available", 60)
-	th.AssertNoErr(t, err)
+	err = CreateVolumeReserve(t, client, volume)
+	if err != nil {
+		t.Fatalf("Unable to create volume reserve: %v", err)
+	}
+	defer DeleteVolumeReserve(t, client, volume)
 }
 
+// Note(jtopjian): I plan to work on this at some point, but it requires
+// setting up a server with iscsi utils.
+/*
 func TestVolumeConns(t *testing.T) {
-	client, err := newClient(t)
-	th.AssertNoErr(t, err)
+    client, err := newClient()
+    th.AssertNoErr(t, err)
 
-	t.Logf("Creating volume")
-	cv, err := volumes.Create(client, &volumes.CreateOpts{
-		Size: 1,
-		Name: "blockv2-volume",
-	}).Extract()
-	th.AssertNoErr(t, err)
+    t.Logf("Creating volume")
+    cv, err := volumes.Create(client, &volumes.CreateOpts{
+        Size: 1,
+        Name: "blockv2-volume",
+    }).Extract()
+    th.AssertNoErr(t, err)
 
-	defer func() {
-		err = volumes.WaitForStatus(client, cv.ID, "available", 60)
-		th.AssertNoErr(t, err)
+    defer func() {
+        err = volumes.WaitForStatus(client, cv.ID, "available", 60)
+        th.AssertNoErr(t, err)
 
-		t.Logf("Deleting volume")
-		err = volumes.Delete(client, cv.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-	}()
+        t.Logf("Deleting volume")
+        err = volumes.Delete(client, cv.ID).ExtractErr()
+        th.AssertNoErr(t, err)
+    }()
 
-	err = volumes.WaitForStatus(client, cv.ID, "available", 60)
-	th.AssertNoErr(t, err)
+    err = volumes.WaitForStatus(client, cv.ID, "available", 60)
+    th.AssertNoErr(t, err)
 
-	connOpts := &volumeactions.ConnectorOpts{
-		IP:        "127.0.0.1",
-		Host:      "stack",
-		Initiator: "iqn.1994-05.com.redhat:17cf566367d2",
-		Multipath: false,
-		Platform:  "x86_64",
-		OSType:    "linux2",
-	}
+    connOpts := &volumeactions.ConnectorOpts{
+        IP:        "127.0.0.1",
+        Host:      "stack",
+        Initiator: "iqn.1994-05.com.redhat:17cf566367d2",
+        Multipath: false,
+        Platform:  "x86_64",
+        OSType:    "linux2",
+    }
 
-	t.Logf("Initializing connection")
-	_, err = volumeactions.InitializeConnection(client, cv.ID, connOpts).Extract()
-	th.AssertNoErr(t, err)
+    t.Logf("Initializing connection")
+    _, err = volumeactions.InitializeConnection(client, cv.ID, connOpts).Extract()
+    th.AssertNoErr(t, err)
 
-	t.Logf("Terminating connection")
-	err = volumeactions.TerminateConnection(client, cv.ID, connOpts).ExtractErr()
-	th.AssertNoErr(t, err)
+    t.Logf("Terminating connection")
+    err = volumeactions.TerminateConnection(client, cv.ID, connOpts).ExtractErr()
+    th.AssertNoErr(t, err)
 }
+*/
diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/acceptance/openstack/blockstorage/v2/volumes_test.go
index 9edf31a..3508bc5 100644
--- a/acceptance/openstack/blockstorage/v2/volumes_test.go
+++ b/acceptance/openstack/blockstorage/v2/volumes_test.go
@@ -3,61 +3,49 @@
 package v2
 
 import (
-	"os"
 	"testing"
 
-	"github.com/gophercloud/gophercloud"
-	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
 	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
-func newClient(t *testing.T) (*gophercloud.ServiceClient, error) {
-	ao, err := openstack.AuthOptionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	client, err := openstack.AuthenticatedClient(ao)
-	th.AssertNoErr(t, err)
-
-	return openstack.NewBlockStorageV2(client, gophercloud.EndpointOpts{
-		Region: os.Getenv("OS_REGION_NAME"),
-	})
-}
-
-func TestVolumes(t *testing.T) {
-	client, err := newClient(t)
-	th.AssertNoErr(t, err)
-
-	cv, err := volumes.Create(client, &volumes.CreateOpts{
-		Size: 1,
-		Name: "blockv2-volume",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	defer func() {
-		err = volumes.WaitForStatus(client, cv.ID, "available", 60)
-		th.AssertNoErr(t, err)
-		err = volumes.Delete(client, cv.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-	}()
-
-	_, err = volumes.Update(client, cv.ID, &volumes.UpdateOpts{
-		Name: "blockv2-updated-volume",
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	v, err := volumes.Get(client, cv.ID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Got volume: %+v\n", v)
-
-	if v.Name != "blockv2-updated-volume" {
-		t.Errorf("Unable to update volume: Expected name: blockv2-updated-volume\nActual name: %s", v.Name)
+func TestVolumesList(t *testing.T) {
+	client, err := clients.NewBlockStorageV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a blockstorage client: %v", err)
 	}
 
-	err = volumes.List(client, &volumes.ListOpts{Name: "blockv2-updated-volume"}).EachPage(func(page pagination.Page) (bool, error) {
-		vols, err := volumes.ExtractVolumes(page)
-		th.CheckEquals(t, 1, len(vols))
-		return true, err
-	})
-	th.AssertNoErr(t, err)
+	allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to retrieve volumes: %v", err)
+	}
+
+	allVolumes, err := volumes.ExtractVolumes(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract volumes: %v", err)
+	}
+
+	for _, volume := range allVolumes {
+		PrintVolume(t, &volume)
+	}
+}
+
+func TestVolumesCreateDestroy(t *testing.T) {
+	client, err := clients.NewBlockStorageV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create blockstorage client: %v", err)
+	}
+
+	volume, err := CreateVolume(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create volume: %v", err)
+	}
+	defer DeleteVolume(t, client, volume)
+
+	newVolume, err := volumes.Get(client, volume.ID).Extract()
+	if err != nil {
+		t.Errorf("Unable to retrieve volume: %v", err)
+	}
+
+	PrintVolume(t, newVolume)
 }