merge lbaasv2, portsbinding, volumes v2; remove 'rackspace' refs; update docs
diff --git a/acceptance/openstack/blockstorage/v2/extensions/pkg.go b/acceptance/openstack/blockstorage/v2/extensions/pkg.go
new file mode 100644
index 0000000..89d906d
--- /dev/null
+++ b/acceptance/openstack/blockstorage/v2/extensions/pkg.go
@@ -0,0 +1,3 @@
+// The extensions package contains acceptance tests for the Openstack Cinder V2 extensions service.
+
+package extensions
diff --git a/acceptance/openstack/blockstorage/v2/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/v2/extensions/volumeactions_test.go
new file mode 100644
index 0000000..20a4597
--- /dev/null
+++ b/acceptance/openstack/blockstorage/v2/extensions/volumeactions_test.go
@@ -0,0 +1,149 @@
+// +build acceptance blockstorage
+
+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/openstack/blockstorage/v2/volumes"
+	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 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")
+	}
+
+	t.Logf("Attaching volume")
+	err = volumeactions.Attach(client, cv.ID, &volumeactions.AttachOpts{
+		MountPoint:   "/mnt",
+		Mode:         "rw",
+		InstanceUUID: instanceID,
+	}).ExtractErr()
+	th.AssertNoErr(t, err)
+
+	err = volumes.WaitForStatus(client, cv.ID, "in-use", 60)
+	th.AssertNoErr(t, err)
+
+	t.Logf("Detaching volume")
+	err = volumeactions.Detach(client, cv.ID).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestVolumeReserve(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)
+
+	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)
+}
+
+func TestVolumeConns(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)
+
+	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("Terminating connection")
+	err = volumeactions.TerminateConnection(client, cv.ID, connOpts).ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/acceptance/openstack/blockstorage/v2/pkg.go b/acceptance/openstack/blockstorage/v2/pkg.go
new file mode 100644
index 0000000..31dd0ff
--- /dev/null
+++ b/acceptance/openstack/blockstorage/v2/pkg.go
@@ -0,0 +1,3 @@
+// The v2 package contains acceptance tests for the Openstack Cinder V2 service.
+
+package v2
diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/acceptance/openstack/blockstorage/v2/volumes_test.go
new file mode 100644
index 0000000..9edf31a
--- /dev/null
+++ b/acceptance/openstack/blockstorage/v2/volumes_test.go
@@ -0,0 +1,63 @@
+// +build acceptance blockstorage
+
+package v2
+
+import (
+	"os"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	"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)
+	}
+
+	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)
+}