Moving calls to client helper while I'm at it
diff --git a/_site/acceptance/README.md b/_site/acceptance/README.md
new file mode 100644
index 0000000..95c174d
--- /dev/null
+++ b/_site/acceptance/README.md
@@ -0,0 +1,56 @@
+# Gophercloud Acceptance tests
+
+The purpose of these acceptance tests is to validate that SDK features meet
+the requirements of a contract - to consumers, other parts of the library, and
+to a remote API.
+
+> **Note:** Because every test will be run against a real API endpoint, you
+> may incur bandwidth and service charges for all the resource usage. These
+> tests *should* remove their remote products automatically. However, there may
+> be certain cases where this does not happen; always double-check to make sure
+> you have no stragglers left behind.
+
+### Step 1. Set environment variables
+
+A lot of tests rely on environment variables for configuration - so you will need
+to set them before running the suite. If you're testing against pure OpenStack APIs,
+you can download a file that contains all of these variables for you: just visit
+the `project/access_and_security` page in your control panel and click the "Download
+OpenStack RC File" button at the top right. For all other providers, you will need
+to set them manually.
+
+#### Authentication
+
+|Name|Description|
+|---|---|
+|`OS_USERNAME`|Your API username|
+|`OS_PASSWORD`|Your API password|
+|`OS_AUTH_URL`|The identity URL you need to authenticate|
+|`OS_TENANT_NAME`|Your API tenant name|
+|`OS_TENANT_ID`|Your API tenant ID|
+
+#### General
+
+|Name|Description|
+|---|---|
+|`OS_REGION_NAME`|The region you want your resources to reside in|
+
+#### Compute
+
+|Name|Description|
+|---|---|
+|`OS_IMAGE_ID`|The ID of the image your want your server to be based on|
+|`OS_FLAVOR_ID`|The ID of the flavor you want your server to be based on|
+|`OS_FLAVOR_ID_RESIZE`|The ID of the flavor you want your server to be resized to|
+
+### 2. Run the test suite
+
+From any directory, run:
+
+```
+go test -v -tags acceptance github.com/rackspace/gophercloud/...
+```
+
+Alternatively, you can execute the above from your nested git folder (i.e. the
+ workspace visible when browsing the Github repository) by replacing
+ `github.com/rackspace/gophercloud/...` with `./...`
diff --git a/_site/acceptance/openstack/blockstorage/v1/snapshots_test.go b/_site/acceptance/openstack/blockstorage/v1/snapshots_test.go
new file mode 100644
index 0000000..5835048
--- /dev/null
+++ b/_site/acceptance/openstack/blockstorage/v1/snapshots_test.go
@@ -0,0 +1,87 @@
+// +build acceptance
+
+package v1
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots"
+ "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
+)
+
+func TestSnapshots(t *testing.T) {
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Failed to create Block Storage v1 client: %v", err)
+ }
+
+ v, err := volumes.Create(client, &volumes.CreateOpts{
+ Name: "gophercloud-test-volume",
+ Size: 1,
+ }).Extract()
+ if err != nil {
+ t.Fatalf("Failed to create volume: %v\n", err)
+ }
+
+ err = volumes.WaitForStatus(client, v.ID, "available", 120)
+ if err != nil {
+ t.Fatalf("Failed to create volume: %v\n", err)
+ }
+
+ t.Logf("Created volume: %v\n", v)
+
+ ss, err := snapshots.Create(client, &snapshots.CreateOpts{
+ Name: "gophercloud-test-snapshot",
+ VolumeID: v.ID,
+ }).Extract()
+ if err != nil {
+ t.Fatalf("Failed to create snapshot: %v\n", err)
+ }
+
+ err = snapshots.WaitForStatus(client, ss.ID, "available", 120)
+ if err != nil {
+ t.Fatalf("Failed to create snapshot: %v\n", err)
+ }
+
+ t.Logf("Created snapshot: %+v\n", ss)
+
+ err = snapshots.Delete(client, ss.ID)
+ if err != nil {
+ t.Fatalf("Failed to delete snapshot: %v", err)
+ }
+
+ err = gophercloud.WaitFor(120, func() (bool, error) {
+ _, err := snapshots.Get(client, ss.ID).Extract()
+ if err != nil {
+ return true, nil
+ }
+
+ return false, nil
+ })
+ if err != nil {
+ t.Fatalf("Failed to delete snapshot: %v", err)
+ }
+
+ t.Log("Deleted snapshot\n")
+
+ err = volumes.Delete(client, v.ID)
+ if err != nil {
+ t.Errorf("Failed to delete volume: %v", err)
+ }
+
+ err = gophercloud.WaitFor(120, func() (bool, error) {
+ _, err := volumes.Get(client, v.ID).Extract()
+ if err != nil {
+ return true, nil
+ }
+
+ return false, nil
+ })
+ if err != nil {
+ t.Errorf("Failed to delete volume: %v", err)
+ }
+
+ t.Log("Deleted volume\n")
+}
diff --git a/_site/acceptance/openstack/blockstorage/v1/volumes_test.go b/_site/acceptance/openstack/blockstorage/v1/volumes_test.go
new file mode 100644
index 0000000..21a47ac
--- /dev/null
+++ b/_site/acceptance/openstack/blockstorage/v1/volumes_test.go
@@ -0,0 +1,88 @@
+// +build acceptance blockstorage
+
+package v1
+
+import (
+ "fmt"
+ "os"
+ "testing"
+
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/openstack"
+ "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
+ "github.com/rackspace/gophercloud/openstack/utils"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+func newClient() (*gophercloud.ServiceClient, error) {
+ ao, err := utils.AuthOptions()
+ if err != nil {
+ return nil, err
+ }
+
+ client, err := openstack.AuthenticatedClient(ao)
+ if err != nil {
+ return nil, err
+ }
+
+ return openstack.NewBlockStorageV1(client, gophercloud.EndpointOpts{
+ Region: os.Getenv("OS_REGION_NAME"),
+ })
+}
+
+func TestVolumes(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Failed to create Block Storage v1 client: %v", err)
+ }
+
+ cv, err := volumes.Create(client, &volumes.CreateOpts{
+ Size: 1,
+ Name: "gophercloud-test-volume",
+ }).Extract()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer func() {
+ err = volumes.WaitForStatus(client, cv.ID, "available", 60)
+ if err != nil {
+ t.Error(err)
+ }
+ err = volumes.Delete(client, cv.ID)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ }()
+
+ _, err = volumes.Update(client, cv.ID, &volumes.UpdateOpts{
+ Name: "gophercloud-updated-volume",
+ }).Extract()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ v, err := volumes.Get(client, cv.ID).Extract()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ fmt.Printf("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)
+ }
+
+ err = volumes.List(client, &volumes.ListOpts{Name: "gophercloud-updated-volume"}).EachPage(func(page pagination.Page) (bool, error) {
+ vols, err := volumes.ExtractVolumes(page)
+ if len(vols) != 1 {
+ t.Errorf("Expected 1 volume, got %d", len(vols))
+ }
+ return true, err
+ })
+ if err != nil {
+ t.Errorf("Error listing volumes: %v", err)
+ }
+}
diff --git a/_site/acceptance/openstack/blockstorage/v1/volumetypes_test.go b/_site/acceptance/openstack/blockstorage/v1/volumetypes_test.go
new file mode 100644
index 0000000..416e341
--- /dev/null
+++ b/_site/acceptance/openstack/blockstorage/v1/volumetypes_test.go
@@ -0,0 +1,58 @@
+// +build acceptance
+
+package v1
+
+import (
+ "testing"
+ "time"
+
+ "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumetypes"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+func TestVolumeTypes(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Failed to create Block Storage v1 client: %v", err)
+ }
+
+ vt, err := volumetypes.Create(client, &volumetypes.CreateOpts{
+ ExtraSpecs: map[string]interface{}{
+ "capabilities": "gpu",
+ "priority": 3,
+ },
+ Name: "gophercloud-test-volumeType",
+ }).Extract()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer func() {
+ time.Sleep(10000 * time.Millisecond)
+ err = volumetypes.Delete(client, vt.ID)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ }()
+ t.Logf("Created volume type: %+v\n", vt)
+
+ vt, err = volumetypes.Get(client, vt.ID).Extract()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ t.Logf("Got volume type: %+v\n", vt)
+
+ 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
+ })
+ if err != nil {
+ t.Errorf("Error trying to list volume types: %v", err)
+ }
+}
diff --git a/_site/acceptance/openstack/client_test.go b/_site/acceptance/openstack/client_test.go
new file mode 100644
index 0000000..52c0cf5
--- /dev/null
+++ b/_site/acceptance/openstack/client_test.go
@@ -0,0 +1,41 @@
+// +build acceptance
+
+package openstack
+
+import (
+ "os"
+ "testing"
+
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/openstack"
+ "github.com/rackspace/gophercloud/openstack/utils"
+)
+
+func TestAuthenticatedClient(t *testing.T) {
+ // Obtain credentials from the environment.
+ ao, err := utils.AuthOptions()
+ if err != nil {
+ t.Fatalf("Unable to acquire credentials: %v", err)
+ }
+
+ client, err := openstack.AuthenticatedClient(ao)
+ if err != nil {
+ t.Fatalf("Unable to authenticate: %v", err)
+ }
+
+ if client.TokenID == "" {
+ t.Errorf("No token ID assigned to the client")
+ }
+
+ t.Logf("Client successfully acquired a token: %v", client.TokenID)
+
+ // Find the storage service in the service catalog.
+ storage, err := openstack.NewStorageV1(client, gophercloud.EndpointOpts{
+ Region: os.Getenv("OS_REGION_NAME"),
+ })
+ if err != nil {
+ t.Errorf("Unable to locate a storage service: %v", err)
+ } else {
+ t.Logf("Located a storage service at endpoint: [%s]", storage.Endpoint)
+ }
+}
diff --git a/_site/acceptance/openstack/compute/v2/compute_test.go b/_site/acceptance/openstack/compute/v2/compute_test.go
new file mode 100644
index 0000000..15b5163
--- /dev/null
+++ b/_site/acceptance/openstack/compute/v2/compute_test.go
@@ -0,0 +1,98 @@
+// +build acceptance
+
+package v2
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/acceptance/tools"
+ "github.com/rackspace/gophercloud/openstack"
+ "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+ "github.com/rackspace/gophercloud/openstack/utils"
+)
+
+func newClient() (*gophercloud.ServiceClient, error) {
+ ao, err := utils.AuthOptions()
+ if err != nil {
+ return nil, err
+ }
+
+ client, err := openstack.AuthenticatedClient(ao)
+ if err != nil {
+ return nil, err
+ }
+
+ return openstack.NewComputeV2(client, gophercloud.EndpointOpts{
+ Region: os.Getenv("OS_REGION_NAME"),
+ })
+}
+
+func waitForStatus(client *gophercloud.ServiceClient, server *servers.Server, status string) error {
+ return tools.WaitFor(func() (bool, error) {
+ latest, err := servers.Get(client, server.ID).Extract()
+ if err != nil {
+ return false, err
+ }
+
+ if latest.Status == status {
+ // Success!
+ return true, nil
+ }
+
+ return false, nil
+ })
+}
+
+// ComputeChoices contains image and flavor selections for use by the acceptance tests.
+type ComputeChoices struct {
+ // ImageID contains the ID of a valid image.
+ ImageID string
+
+ // FlavorID contains the ID of a valid flavor.
+ FlavorID string
+
+ // FlavorIDResize contains the ID of a different flavor available on the same OpenStack installation, that is distinct
+ // from FlavorID.
+ FlavorIDResize string
+}
+
+// ComputeChoicesFromEnv populates a ComputeChoices struct from environment variables.
+// If any required state is missing, an `error` will be returned that enumerates the missing properties.
+func ComputeChoicesFromEnv() (*ComputeChoices, error) {
+ imageID := os.Getenv("OS_IMAGE_ID")
+ flavorID := os.Getenv("OS_FLAVOR_ID")
+ flavorIDResize := os.Getenv("OS_FLAVOR_ID_RESIZE")
+
+ missing := make([]string, 0, 3)
+ if imageID == "" {
+ missing = append(missing, "OS_IMAGE_ID")
+ }
+ if flavorID == "" {
+ missing = append(missing, "OS_FLAVOR_ID")
+ }
+ if flavorIDResize == "" {
+ missing = append(missing, "OS_FLAVOR_ID_RESIZE")
+ }
+
+ notDistinct := ""
+ if flavorID == flavorIDResize {
+ notDistinct = "OS_FLAVOR_ID and OS_FLAVOR_ID_RESIZE must be distinct."
+ }
+
+ if len(missing) > 0 || notDistinct != "" {
+ text := "You're missing some important setup:\n"
+ if len(missing) > 0 {
+ text += " * These environment variables must be provided: " + strings.Join(missing, ", ") + "\n"
+ }
+ if notDistinct != "" {
+ text += " * " + notDistinct + "\n"
+ }
+
+ return nil, fmt.Errorf(text)
+ }
+
+ return &ComputeChoices{ImageID: imageID, FlavorID: flavorID, FlavorIDResize: flavorIDResize}, nil
+}
diff --git a/_site/acceptance/openstack/compute/v2/flavors_test.go b/_site/acceptance/openstack/compute/v2/flavors_test.go
new file mode 100644
index 0000000..9c8e322
--- /dev/null
+++ b/_site/acceptance/openstack/compute/v2/flavors_test.go
@@ -0,0 +1,57 @@
+// +build acceptance
+
+package v2
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+func TestListFlavors(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ t.Logf("ID\tRegion\tName\tStatus\tCreated")
+
+ pager := flavors.List(client, nil)
+ count, pages := 0, 0
+ pager.EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("---")
+ pages++
+ flavors, err := flavors.ExtractFlavors(page)
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range flavors {
+ t.Logf("%s\t%s\t%d\t%d\t%d", f.ID, f.Name, f.RAM, f.Disk, f.VCPUs)
+ }
+
+ return true, nil
+ })
+
+ t.Logf("--------\n%d flavors listed on %d pages.", count, pages)
+}
+
+func TestGetFlavor(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ flavor, err := flavors.Get(client, choices.FlavorID).Extract()
+ if err != nil {
+ t.Fatalf("Unable to get flavor information: %v", err)
+ }
+
+ t.Logf("Flavor: %#v", flavor)
+}
diff --git a/_site/acceptance/openstack/compute/v2/images_test.go b/_site/acceptance/openstack/compute/v2/images_test.go
new file mode 100644
index 0000000..6166fc8
--- /dev/null
+++ b/_site/acceptance/openstack/compute/v2/images_test.go
@@ -0,0 +1,37 @@
+// +build acceptance
+
+package v2
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud/openstack/compute/v2/images"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+func TestListImages(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute: client: %v", err)
+ }
+
+ t.Logf("ID\tRegion\tName\tStatus\tCreated")
+
+ pager := images.ListDetail(client, nil)
+ count, pages := 0, 0
+ pager.EachPage(func(page pagination.Page) (bool, error) {
+ pages++
+ images, err := images.ExtractImages(page)
+ if err != nil {
+ return false, err
+ }
+
+ for _, i := range images {
+ t.Logf("%s\t%s\t%s\t%s", i.ID, i.Name, i.Status, i.Created)
+ }
+
+ return true, nil
+ })
+
+ t.Logf("--------\n%d images listed on %d pages.", count, pages)
+}
diff --git a/_site/acceptance/openstack/compute/v2/pkg.go b/_site/acceptance/openstack/compute/v2/pkg.go
new file mode 100644
index 0000000..bb158c3
--- /dev/null
+++ b/_site/acceptance/openstack/compute/v2/pkg.go
@@ -0,0 +1,3 @@
+// The v2 package contains acceptance tests for the Openstack Compute V2 service.
+
+package v2
diff --git a/_site/acceptance/openstack/compute/v2/servers_test.go b/_site/acceptance/openstack/compute/v2/servers_test.go
new file mode 100644
index 0000000..5193e4e
--- /dev/null
+++ b/_site/acceptance/openstack/compute/v2/servers_test.go
@@ -0,0 +1,342 @@
+// +build acceptance
+
+package v2
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/acceptance/tools"
+ "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+func TestListServers(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ t.Logf("ID\tRegion\tName\tStatus\tIPv4\tIPv6")
+
+ pager := servers.List(client, servers.ListOpts{})
+ count, pages := 0, 0
+ pager.EachPage(func(page pagination.Page) (bool, error) {
+ pages++
+ t.Logf("---")
+
+ servers, err := servers.ExtractServers(page)
+ if err != nil {
+ return false, err
+ }
+
+ for _, s := range servers {
+ t.Logf("%s\t%s\t%s\t%s\t%s\t\n", s.ID, s.Name, s.Status, s.AccessIPv4, s.AccessIPv6)
+ count++
+ }
+
+ return true, nil
+ })
+
+ fmt.Printf("--------\n%d servers listed on %d pages.\n", count, pages)
+}
+
+func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
+ name := tools.RandomString("ACPTTEST", 16)
+ t.Logf("Attempting to create server: %s\n", name)
+
+ server, err := servers.Create(client, servers.CreateOpts{
+ Name: name,
+ FlavorRef: choices.FlavorID,
+ ImageRef: choices.ImageID,
+ }).Extract()
+ if err != nil {
+ t.Fatalf("Unable to create server: %v", err)
+ }
+
+ return server, err
+}
+
+func TestCreateDestroyServer(t *testing.T) {
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ name := tools.RandomString("ACPTTEST", 16)
+ t.Logf("Attempting to create server: %s\n", name)
+
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatalf("Unable to create server: %v", err)
+ }
+ defer func() {
+ servers.Delete(client, server.ID)
+ t.Logf("Server deleted.")
+ }()
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatalf("Unable to wait for server: %v", err)
+ }
+}
+
+func TestUpdateServer(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer servers.Delete(client, server.ID)
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+
+ alternateName := tools.RandomString("ACPTTEST", 16)
+ for alternateName == server.Name {
+ alternateName = tools.RandomString("ACPTTEST", 16)
+ }
+
+ t.Logf("Attempting to rename the server to %s.", alternateName)
+
+ updated, err := servers.Update(client, server.ID, servers.UpdateOpts{Name: alternateName}).Extract()
+ if err != nil {
+ t.Fatalf("Unable to rename server: %v", err)
+ }
+
+ if updated.ID != server.ID {
+ t.Errorf("Updated server ID [%s] didn't match original server ID [%s]!", updated.ID, server.ID)
+ }
+
+ err = tools.WaitFor(func() (bool, error) {
+ latest, err := servers.Get(client, updated.ID).Extract()
+ if err != nil {
+ return false, err
+ }
+
+ return latest.Name == alternateName, nil
+ })
+}
+
+func TestActionChangeAdminPassword(t *testing.T) {
+ t.Parallel()
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer servers.Delete(client, server.ID)
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+
+ randomPassword := tools.MakeNewPassword(server.AdminPass)
+ err = servers.ChangeAdminPassword(client, server.ID, randomPassword)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err = waitForStatus(client, server, "PASSWORD"); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestActionReboot(t *testing.T) {
+ t.Parallel()
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer servers.Delete(client, server.ID)
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+
+ err = servers.Reboot(client, server.ID, "aldhjflaskhjf")
+ if err == nil {
+ t.Fatal("Expected the SDK to provide an ArgumentError here")
+ }
+
+ t.Logf("Attempting reboot of server %s", server.ID)
+ err = servers.Reboot(client, server.ID, servers.OSReboot)
+ if err != nil {
+ t.Fatalf("Unable to reboot server: %v", err)
+ }
+
+ if err = waitForStatus(client, server, "REBOOT"); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestActionRebuild(t *testing.T) {
+ t.Parallel()
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer servers.Delete(client, server.ID)
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+
+ t.Logf("Attempting to rebuild server %s", server.ID)
+
+ rebuildOpts := servers.RebuildOpts{
+ Name: tools.RandomString("ACPTTEST", 16),
+ AdminPass: tools.MakeNewPassword(server.AdminPass),
+ ImageID: choices.ImageID,
+ }
+
+ rebuilt, err := servers.Rebuild(client, server.ID, rebuildOpts).Extract()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if rebuilt.ID != server.ID {
+ t.Errorf("Expected rebuilt server ID of [%s]; got [%s]", server.ID, rebuilt.ID)
+ }
+
+ if err = waitForStatus(client, rebuilt, "REBUILD"); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = waitForStatus(client, rebuilt, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func resizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server, choices *ComputeChoices) {
+ if err := waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+
+ t.Logf("Attempting to resize server [%s]", server.ID)
+
+ if err := servers.Resize(client, server.ID, choices.FlavorIDResize); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := waitForStatus(client, server, "VERIFY_RESIZE"); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestActionResizeConfirm(t *testing.T) {
+ t.Parallel()
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer servers.Delete(client, server.ID)
+ resizeServer(t, client, server, choices)
+
+ t.Logf("Attempting to confirm resize for server %s", server.ID)
+
+ if err = servers.ConfirmResize(client, server.ID); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestActionResizeRevert(t *testing.T) {
+ t.Parallel()
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer servers.Delete(client, server.ID)
+ resizeServer(t, client, server, choices)
+
+ t.Logf("Attempting to revert resize for server %s", server.ID)
+
+ if err := servers.RevertResize(client, server.ID); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/_site/acceptance/openstack/identity/v2/extension_test.go b/_site/acceptance/openstack/identity/v2/extension_test.go
new file mode 100644
index 0000000..2b4e062
--- /dev/null
+++ b/_site/acceptance/openstack/identity/v2/extension_test.go
@@ -0,0 +1,46 @@
+// +build acceptance
+
+package v2
+
+import (
+ "testing"
+
+ extensions2 "github.com/rackspace/gophercloud/openstack/identity/v2/extensions"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestEnumerateExtensions(t *testing.T) {
+ service := authenticatedClient(t)
+
+ t.Logf("Extensions available on this identity endpoint:")
+ count := 0
+ err := extensions2.List(service).EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("--- Page %02d ---", count)
+
+ extensions, err := extensions2.ExtractExtensions(page)
+ th.AssertNoErr(t, err)
+
+ for i, ext := range extensions {
+ t.Logf("[%02d] name=[%s] namespace=[%s]", i, ext.Name, ext.Namespace)
+ t.Logf(" alias=[%s] updated=[%s]", ext.Alias, ext.Updated)
+ t.Logf(" description=[%s]", ext.Description)
+ }
+
+ count++
+ return true, nil
+ })
+ th.AssertNoErr(t, err)
+}
+
+func TestGetExtension(t *testing.T) {
+ service := authenticatedClient(t)
+
+ ext, err := extensions2.Get(service, "OS-KSCRUD").Extract()
+ th.AssertNoErr(t, err)
+
+ th.CheckEquals(t, "OpenStack Keystone User CRUD", ext.Name)
+ th.CheckEquals(t, "http://docs.openstack.org/identity/api/ext/OS-KSCRUD/v1.0", ext.Namespace)
+ th.CheckEquals(t, "OS-KSCRUD", ext.Alias)
+ th.CheckEquals(t, "OpenStack extensions to Keystone v2.0 API enabling User Operations.", ext.Description)
+}
diff --git a/_site/acceptance/openstack/identity/v2/identity_test.go b/_site/acceptance/openstack/identity/v2/identity_test.go
new file mode 100644
index 0000000..2ecd3ca
--- /dev/null
+++ b/_site/acceptance/openstack/identity/v2/identity_test.go
@@ -0,0 +1,48 @@
+// +build acceptance
+
+package v2
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/openstack"
+ "github.com/rackspace/gophercloud/openstack/utils"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func v2AuthOptions(t *testing.T) gophercloud.AuthOptions {
+ // Obtain credentials from the environment.
+ ao, err := utils.AuthOptions()
+ th.AssertNoErr(t, err)
+
+ // Trim out unused fields. Prefer authentication by API key to password.
+ ao.UserID, ao.DomainID, ao.DomainName = "", "", ""
+ if ao.APIKey != "" {
+ ao.Password = ""
+ }
+
+ return ao
+}
+
+func createClient(t *testing.T, auth bool) *gophercloud.ServiceClient {
+ ao := v2AuthOptions(t)
+
+ provider, err := openstack.NewClient(ao.IdentityEndpoint)
+ th.AssertNoErr(t, err)
+
+ if auth {
+ err = openstack.AuthenticateV2(provider, ao)
+ th.AssertNoErr(t, err)
+ }
+
+ return openstack.NewIdentityV2(provider)
+}
+
+func unauthenticatedClient(t *testing.T) *gophercloud.ServiceClient {
+ return createClient(t, false)
+}
+
+func authenticatedClient(t *testing.T) *gophercloud.ServiceClient {
+ return createClient(t, true)
+}
diff --git a/_site/acceptance/openstack/identity/v2/pkg.go b/_site/acceptance/openstack/identity/v2/pkg.go
new file mode 100644
index 0000000..5ec3cc8
--- /dev/null
+++ b/_site/acceptance/openstack/identity/v2/pkg.go
@@ -0,0 +1 @@
+package v2
diff --git a/_site/acceptance/openstack/identity/v2/tenant_test.go b/_site/acceptance/openstack/identity/v2/tenant_test.go
new file mode 100644
index 0000000..2054598
--- /dev/null
+++ b/_site/acceptance/openstack/identity/v2/tenant_test.go
@@ -0,0 +1,32 @@
+// +build acceptance
+
+package v2
+
+import (
+ "testing"
+
+ tenants2 "github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestEnumerateTenants(t *testing.T) {
+ service := authenticatedClient(t)
+
+ t.Logf("Tenants to which your current token grants access:")
+ count := 0
+ err := tenants2.List(service, nil).EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("--- Page %02d ---", count)
+
+ tenants, err := tenants2.ExtractTenants(page)
+ th.AssertNoErr(t, err)
+ for i, tenant := range tenants {
+ t.Logf("[%02d] name=[%s] id=[%s] description=[%s] enabled=[%v]",
+ i, tenant.Name, tenant.ID, tenant.Description, tenant.Enabled)
+ }
+
+ count++
+ return true, nil
+ })
+ th.AssertNoErr(t, err)
+}
diff --git a/_site/acceptance/openstack/identity/v2/token_test.go b/_site/acceptance/openstack/identity/v2/token_test.go
new file mode 100644
index 0000000..47381a2
--- /dev/null
+++ b/_site/acceptance/openstack/identity/v2/token_test.go
@@ -0,0 +1,38 @@
+// +build acceptance
+
+package v2
+
+import (
+ "testing"
+
+ tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestAuthenticate(t *testing.T) {
+ ao := v2AuthOptions(t)
+ service := unauthenticatedClient(t)
+
+ // Authenticated!
+ result := tokens2.Create(service, ao)
+
+ // Extract and print the token.
+ token, err := result.ExtractToken()
+ th.AssertNoErr(t, err)
+
+ t.Logf("Acquired token: [%s]", token.ID)
+ t.Logf("The token will expire at: [%s]", token.ExpiresAt.String())
+ t.Logf("The token is valid for tenant: [%#v]", token.Tenant)
+
+ // Extract and print the service catalog.
+ catalog, err := result.ExtractServiceCatalog()
+ th.AssertNoErr(t, err)
+
+ t.Logf("Acquired service catalog listing [%d] services", len(catalog.Entries))
+ for i, entry := range catalog.Entries {
+ t.Logf("[%02d]: name=[%s], type=[%s]", i, entry.Name, entry.Type)
+ for _, endpoint := range entry.Endpoints {
+ t.Logf(" - region=[%s] publicURL=[%s]", endpoint.Region, endpoint.PublicURL)
+ }
+ }
+}
diff --git a/_site/acceptance/openstack/identity/v3/endpoint_test.go b/_site/acceptance/openstack/identity/v3/endpoint_test.go
new file mode 100644
index 0000000..9032ec3
--- /dev/null
+++ b/_site/acceptance/openstack/identity/v3/endpoint_test.go
@@ -0,0 +1,108 @@
+// +build acceptance
+
+package v3
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud"
+ endpoints3 "github.com/rackspace/gophercloud/openstack/identity/v3/endpoints"
+ services3 "github.com/rackspace/gophercloud/openstack/identity/v3/services"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+func TestListEndpoints(t *testing.T) {
+ // Create a service client.
+ serviceClient := createAuthenticatedClient(t)
+ if serviceClient == nil {
+ return
+ }
+
+ // Use the service to list all available endpoints.
+ pager := endpoints3.List(serviceClient, endpoints3.ListOpts{})
+ err := pager.EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("--- Page ---")
+
+ endpoints, err := endpoints3.ExtractEndpoints(page)
+ if err != nil {
+ t.Fatalf("Error extracting endpoings: %v", err)
+ }
+
+ for _, endpoint := range endpoints {
+ t.Logf("Endpoint: %8s %10s %9s %s",
+ endpoint.ID,
+ endpoint.Availability,
+ endpoint.Name,
+ endpoint.URL)
+ }
+
+ return true, nil
+ })
+ if err != nil {
+ t.Errorf("Unexpected error while iterating endpoint pages: %v", err)
+ }
+}
+
+func TestNavigateCatalog(t *testing.T) {
+ // Create a service client.
+ client := createAuthenticatedClient(t)
+
+ var compute *services3.Service
+ var endpoint *endpoints3.Endpoint
+
+ // Discover the service we're interested in.
+ servicePager := services3.List(client, services3.ListOpts{ServiceType: "compute"})
+ err := servicePager.EachPage(func(page pagination.Page) (bool, error) {
+ part, err := services3.ExtractServices(page)
+ if err != nil {
+ return false, err
+ }
+ if compute != nil {
+ t.Fatalf("Expected one service, got more than one page")
+ return false, nil
+ }
+ if len(part) != 1 {
+ t.Fatalf("Expected one service, got %d", len(part))
+ return false, nil
+ }
+
+ compute = &part[0]
+ return true, nil
+ })
+ if err != nil {
+ t.Fatalf("Unexpected error iterating pages: %v", err)
+ }
+
+ if compute == nil {
+ t.Fatalf("No compute service found.")
+ }
+
+ // Enumerate the endpoints available for this service.
+ computePager := endpoints3.List(client, endpoints3.ListOpts{
+ Availability: gophercloud.AvailabilityPublic,
+ ServiceID: compute.ID,
+ })
+ err = computePager.EachPage(func(page pagination.Page) (bool, error) {
+ part, err := endpoints3.ExtractEndpoints(page)
+ if err != nil {
+ return false, err
+ }
+ if endpoint != nil {
+ t.Fatalf("Expected one endpoint, got more than one page")
+ return false, nil
+ }
+ if len(part) != 1 {
+ t.Fatalf("Expected one endpoint, got %d", len(part))
+ return false, nil
+ }
+
+ endpoint = &part[0]
+ return true, nil
+ })
+
+ if endpoint == nil {
+ t.Fatalf("No endpoint found.")
+ }
+
+ t.Logf("Success. The compute endpoint is at %s.", endpoint.URL)
+}
diff --git a/_site/acceptance/openstack/identity/v3/identity_test.go b/_site/acceptance/openstack/identity/v3/identity_test.go
new file mode 100644
index 0000000..e0503e2
--- /dev/null
+++ b/_site/acceptance/openstack/identity/v3/identity_test.go
@@ -0,0 +1,41 @@
+// +build acceptance
+
+package v3
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/openstack"
+ "github.com/rackspace/gophercloud/openstack/utils"
+)
+
+func createAuthenticatedClient(t *testing.T) *gophercloud.ServiceClient {
+ // Obtain credentials from the environment.
+ ao, err := utils.AuthOptions()
+ if err != nil {
+ t.Fatalf("Unable to acquire credentials: %v", err)
+ }
+
+ // Trim out unused fields.
+ ao.Username, ao.TenantID, ao.TenantName = "", "", ""
+
+ if ao.UserID == "" {
+ t.Logf("Skipping identity v3 tests because no OS_USERID is present.")
+ return nil
+ }
+
+ // Create a client and manually authenticate against v3.
+ providerClient, err := openstack.NewClient(ao.IdentityEndpoint)
+ if err != nil {
+ t.Fatalf("Unable to instantiate client: %v", err)
+ }
+
+ err = openstack.AuthenticateV3(providerClient, ao)
+ if err != nil {
+ t.Fatalf("Unable to authenticate against identity v3: %v", err)
+ }
+
+ // Create a service client.
+ return openstack.NewIdentityV3(providerClient)
+}
diff --git a/_site/acceptance/openstack/identity/v3/pkg.go b/_site/acceptance/openstack/identity/v3/pkg.go
new file mode 100644
index 0000000..d3b5573
--- /dev/null
+++ b/_site/acceptance/openstack/identity/v3/pkg.go
@@ -0,0 +1,2 @@
+// Package v3 contains acceptance tests for identity v3 resources.
+package v3
diff --git a/_site/acceptance/openstack/identity/v3/service_test.go b/_site/acceptance/openstack/identity/v3/service_test.go
new file mode 100644
index 0000000..082bd11
--- /dev/null
+++ b/_site/acceptance/openstack/identity/v3/service_test.go
@@ -0,0 +1,36 @@
+// +build acceptance
+
+package v3
+
+import (
+ "testing"
+
+ services3 "github.com/rackspace/gophercloud/openstack/identity/v3/services"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+func TestListServices(t *testing.T) {
+ // Create a service client.
+ serviceClient := createAuthenticatedClient(t)
+ if serviceClient == nil {
+ return
+ }
+
+ // Use the client to list all available services.
+ pager := services3.List(serviceClient, services3.ListOpts{})
+ err := pager.EachPage(func(page pagination.Page) (bool, error) {
+ parts, err := services3.ExtractServices(page)
+ if err != nil {
+ return false, err
+ }
+
+ t.Logf("--- Page ---")
+ for _, service := range parts {
+ t.Logf("Service: %32s %15s %10s %s", service.ID, service.Type, service.Name, *service.Description)
+ }
+ return true, nil
+ })
+ if err != nil {
+ t.Errorf("Unexpected error traversing pages: %v", err)
+ }
+}
diff --git a/_site/acceptance/openstack/identity/v3/token_test.go b/_site/acceptance/openstack/identity/v3/token_test.go
new file mode 100644
index 0000000..341acb7
--- /dev/null
+++ b/_site/acceptance/openstack/identity/v3/token_test.go
@@ -0,0 +1,43 @@
+// +build acceptance
+
+package v3
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud/openstack"
+ tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
+ "github.com/rackspace/gophercloud/openstack/utils"
+)
+
+func TestGetToken(t *testing.T) {
+ // Obtain credentials from the environment.
+ ao, err := utils.AuthOptions()
+ if err != nil {
+ t.Fatalf("Unable to acquire credentials: %v", err)
+ }
+
+ // Trim out unused fields. Skip if we don't have a UserID.
+ ao.Username, ao.TenantID, ao.TenantName = "", "", ""
+ if ao.UserID == "" {
+ t.Logf("Skipping identity v3 tests because no OS_USERID is present.")
+ return
+ }
+
+ // Create an unauthenticated client.
+ provider, err := openstack.NewClient(ao.IdentityEndpoint)
+ if err != nil {
+ t.Fatalf("Unable to instantiate client: %v", err)
+ }
+
+ // Create a service client.
+ service := openstack.NewIdentityV3(provider)
+
+ // Use the service to create a token.
+ token, err := tokens3.Create(service, ao, nil).Extract()
+ if err != nil {
+ t.Fatalf("Unable to get token: %v", err)
+ }
+
+ t.Logf("Acquired token: %s", token.ID)
+}
diff --git a/_site/acceptance/openstack/networking/v2/apiversion_test.go b/_site/acceptance/openstack/networking/v2/apiversion_test.go
new file mode 100644
index 0000000..99e1d01
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/apiversion_test.go
@@ -0,0 +1,51 @@
+// +build acceptance networking
+
+package v2
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud/openstack/networking/v2/apiversions"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestListAPIVersions(t *testing.T) {
+ Setup(t)
+ defer Teardown()
+
+ pager := apiversions.ListVersions(Client)
+ err := pager.EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("--- Page ---")
+
+ versions, err := apiversions.ExtractAPIVersions(page)
+ th.AssertNoErr(t, err)
+
+ for _, v := range versions {
+ t.Logf("API Version: ID [%s] Status [%s]", v.ID, v.Status)
+ }
+
+ return true, nil
+ })
+ th.CheckNoErr(t, err)
+}
+
+func TestListAPIResources(t *testing.T) {
+ Setup(t)
+ defer Teardown()
+
+ pager := apiversions.ListVersionResources(Client, "v2.0")
+ err := pager.EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("--- Page ---")
+
+ vrs, err := apiversions.ExtractVersionResources(page)
+ th.AssertNoErr(t, err)
+
+ for _, vr := range vrs {
+ t.Logf("Network: Name [%s] Collection [%s]", vr.Name, vr.Collection)
+ }
+
+ return true, nil
+ })
+ th.CheckNoErr(t, err)
+}
diff --git a/_site/acceptance/openstack/networking/v2/common.go b/_site/acceptance/openstack/networking/v2/common.go
new file mode 100644
index 0000000..6dd58af
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/common.go
@@ -0,0 +1,40 @@
+package v2
+
+import (
+ "os"
+ "testing"
+
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/openstack"
+ "github.com/rackspace/gophercloud/openstack/utils"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+var Client *gophercloud.ServiceClient
+
+func NewClient() (*gophercloud.ServiceClient, error) {
+ opts, err := utils.AuthOptions()
+ if err != nil {
+ return nil, err
+ }
+
+ provider, err := openstack.AuthenticatedClient(opts)
+ if err != nil {
+ return nil, err
+ }
+
+ return openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
+ Name: "neutron",
+ Region: os.Getenv("OS_REGION_NAME"),
+ })
+}
+
+func Setup(t *testing.T) {
+ client, err := NewClient()
+ th.AssertNoErr(t, err)
+ Client = client
+}
+
+func Teardown() {
+ Client = nil
+}
diff --git a/_site/acceptance/openstack/networking/v2/extension_test.go b/_site/acceptance/openstack/networking/v2/extension_test.go
new file mode 100644
index 0000000..edcbba4
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/extension_test.go
@@ -0,0 +1,45 @@
+// +build acceptance networking
+
+package v2
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestListExts(t *testing.T) {
+ Setup(t)
+ defer Teardown()
+
+ pager := extensions.List(Client)
+ err := pager.EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("--- Page ---")
+
+ exts, err := extensions.ExtractExtensions(page)
+ th.AssertNoErr(t, err)
+
+ for _, ext := range exts {
+ t.Logf("Extension: Name [%s] Description [%s]", ext.Name, ext.Description)
+ }
+
+ return true, nil
+ })
+ th.CheckNoErr(t, err)
+}
+
+func TestGetExt(t *testing.T) {
+ Setup(t)
+ defer Teardown()
+
+ ext, err := extensions.Get(Client, "service-type").Extract()
+ th.AssertNoErr(t, err)
+
+ th.AssertEquals(t, ext.Updated, "2013-01-20T00:00:00-00:00")
+ th.AssertEquals(t, ext.Name, "Neutron Service Type Management")
+ th.AssertEquals(t, ext.Namespace, "http://docs.openstack.org/ext/neutron/service-type/api/v1.0")
+ th.AssertEquals(t, ext.Alias, "service-type")
+ th.AssertEquals(t, ext.Description, "API for retrieving service providers for Neutron advanced services")
+}
diff --git a/_site/acceptance/openstack/networking/v2/extensions/layer3_test.go b/_site/acceptance/openstack/networking/v2/extensions/layer3_test.go
new file mode 100644
index 0000000..63e0be3
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/extensions/layer3_test.go
@@ -0,0 +1,300 @@
+// +build acceptance networking layer3ext
+
+package extensions
+
+import (
+ "testing"
+
+ base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/ports"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+const (
+ cidr1 = "10.0.0.1/24"
+ cidr2 = "20.0.0.1/24"
+)
+
+func TestAll(t *testing.T) {
+ base.Setup(t)
+ defer base.Teardown()
+
+ testRouter(t)
+ testFloatingIP(t)
+}
+
+func testRouter(t *testing.T) {
+ // Setup: Create network
+ networkID := createNetwork(t)
+
+ // Create router
+ routerID := createRouter(t, networkID)
+
+ // Lists routers
+ listRouters(t)
+
+ // Update router
+ updateRouter(t, routerID)
+
+ // Get router
+ getRouter(t, routerID)
+
+ // Create new subnet. Note: this subnet will be deleted when networkID is deleted
+ subnetID := createSubnet(t, networkID, cidr2)
+
+ // Add interface
+ addInterface(t, routerID, subnetID)
+
+ // Remove interface
+ removeInterface(t, routerID, subnetID)
+
+ // Delete router
+ deleteRouter(t, routerID)
+
+ // Cleanup
+ deleteNetwork(t, networkID)
+}
+
+func testFloatingIP(t *testing.T) {
+ // Setup external network
+ extNetworkID := createNetwork(t)
+
+ // Setup internal network, subnet and port
+ intNetworkID, subnetID, portID := createInternalTopology(t)
+
+ // Now the important part: we need to allow the external network to talk to
+ // the internal subnet. For this we need a router that has an interface to
+ // the internal subnet.
+ routerID := bridgeIntSubnetWithExtNetwork(t, extNetworkID, subnetID)
+
+ // Create floating IP
+ ipID := createFloatingIP(t, extNetworkID, portID)
+
+ // Get floating IP
+ getFloatingIP(t, ipID)
+
+ // Update floating IP
+ updateFloatingIP(t, ipID, portID)
+
+ // Delete floating IP
+ deleteFloatingIP(t, ipID)
+
+ // Remove the internal subnet interface
+ removeInterface(t, routerID, subnetID)
+
+ // Delete router and external network
+ deleteRouter(t, routerID)
+ deleteNetwork(t, extNetworkID)
+
+ // Delete internal port and network
+ deletePort(t, portID)
+ deleteNetwork(t, intNetworkID)
+}
+
+func createNetwork(t *testing.T) string {
+ t.Logf("Creating a network")
+
+ asu := true
+ opts := external.CreateOpts{
+ Parent: networks.CreateOpts{Name: "sample_network", AdminStateUp: &asu},
+ External: true,
+ }
+ n, err := networks.Create(base.Client, opts).Extract()
+
+ th.AssertNoErr(t, err)
+
+ if n.ID == "" {
+ t.Fatalf("No ID returned when creating a network")
+ }
+
+ createSubnet(t, n.ID, cidr1)
+
+ t.Logf("Network created: ID [%s]", n.ID)
+
+ return n.ID
+}
+
+func deleteNetwork(t *testing.T, networkID string) {
+ t.Logf("Deleting network %s", networkID)
+ networks.Delete(base.Client, networkID)
+}
+
+func deletePort(t *testing.T, portID string) {
+ t.Logf("Deleting port %s", portID)
+ ports.Delete(base.Client, portID)
+}
+
+func createInternalTopology(t *testing.T) (string, string, string) {
+ t.Logf("Creating an internal network (for port)")
+ opts := networks.CreateOpts{Name: "internal_network"}
+ n, err := networks.Create(base.Client, opts).Extract()
+ th.AssertNoErr(t, err)
+
+ // A subnet is also needed
+ subnetID := createSubnet(t, n.ID, cidr2)
+
+ t.Logf("Creating an internal port on network %s", n.ID)
+ p, err := ports.Create(base.Client, ports.CreateOpts{
+ NetworkID: n.ID,
+ Name: "fixed_internal_port",
+ }).Extract()
+ th.AssertNoErr(t, err)
+
+ return n.ID, subnetID, p.ID
+}
+
+func bridgeIntSubnetWithExtNetwork(t *testing.T, networkID, subnetID string) string {
+ // Create router with external gateway info
+ routerID := createRouter(t, networkID)
+
+ // Add interface for internal subnet
+ addInterface(t, routerID, subnetID)
+
+ return routerID
+}
+
+func createSubnet(t *testing.T, networkID, cidr string) string {
+ t.Logf("Creating a subnet for network %s", networkID)
+
+ iFalse := false
+ s, err := subnets.Create(base.Client, subnets.CreateOpts{
+ NetworkID: networkID,
+ CIDR: cidr,
+ IPVersion: subnets.IPv4,
+ Name: "my_subnet",
+ EnableDHCP: &iFalse,
+ }).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Subnet created: ID [%s]", s.ID)
+
+ return s.ID
+}
+
+func createRouter(t *testing.T, networkID string) string {
+ t.Logf("Creating a router for network %s", networkID)
+
+ asu := false
+ gwi := routers.GatewayInfo{NetworkID: networkID}
+ r, err := routers.Create(base.Client, routers.CreateOpts{
+ Name: "foo_router",
+ AdminStateUp: &asu,
+ GatewayInfo: &gwi,
+ }).Extract()
+
+ th.AssertNoErr(t, err)
+
+ if r.ID == "" {
+ t.Fatalf("No ID returned when creating a router")
+ }
+
+ t.Logf("Router created: ID [%s]", r.ID)
+
+ return r.ID
+}
+
+func listRouters(t *testing.T) {
+ pager := routers.List(base.Client, routers.ListOpts{})
+
+ err := pager.EachPage(func(page pagination.Page) (bool, error) {
+ routerList, err := routers.ExtractRouters(page)
+ th.AssertNoErr(t, err)
+
+ for _, r := range routerList {
+ t.Logf("Listing router: ID [%s] Name [%s] Status [%s] GatewayInfo [%#v]",
+ r.ID, r.Name, r.Status, r.GatewayInfo)
+ }
+
+ return true, nil
+ })
+
+ th.AssertNoErr(t, err)
+}
+
+func updateRouter(t *testing.T, routerID string) {
+ _, err := routers.Update(base.Client, routerID, routers.UpdateOpts{
+ Name: "another_name",
+ }).Extract()
+
+ th.AssertNoErr(t, err)
+}
+
+func getRouter(t *testing.T, routerID string) {
+ r, err := routers.Get(base.Client, routerID).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Getting router: ID [%s] Name [%s] Status [%s]", r.ID, r.Name, r.Status)
+}
+
+func addInterface(t *testing.T, routerID, subnetID string) {
+ ir, err := routers.AddInterface(base.Client, routerID, routers.InterfaceOpts{SubnetID: subnetID}).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Interface added to router %s: SubnetID [%s] PortID [%s]", routerID, ir.SubnetID, ir.PortID)
+}
+
+func removeInterface(t *testing.T, routerID, subnetID string) {
+ ir, err := routers.RemoveInterface(base.Client, routerID, routers.InterfaceOpts{SubnetID: subnetID}).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Interface %s removed from %s", ir.ID, routerID)
+}
+
+func deleteRouter(t *testing.T, routerID string) {
+ t.Logf("Deleting router %s", routerID)
+
+ res := routers.Delete(base.Client, routerID)
+
+ th.AssertNoErr(t, res.Err)
+}
+
+func createFloatingIP(t *testing.T, networkID, portID string) string {
+ t.Logf("Creating floating IP on network [%s] with port [%s]", networkID, portID)
+
+ opts := floatingips.CreateOpts{
+ FloatingNetworkID: networkID,
+ PortID: portID,
+ }
+
+ ip, err := floatingips.Create(base.Client, opts).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Floating IP created: ID [%s] Status [%s] Fixed (internal) IP: [%s] Floating (external) IP: [%s]",
+ ip.ID, ip.Status, ip.FixedIP, ip.FloatingIP)
+
+ return ip.ID
+}
+
+func getFloatingIP(t *testing.T, ipID string) {
+ ip, err := floatingips.Get(base.Client, ipID).Extract()
+ th.AssertNoErr(t, err)
+
+ t.Logf("Getting floating IP: ID [%s] Status [%s]", ip.ID, ip.Status)
+}
+
+func updateFloatingIP(t *testing.T, ipID, portID string) {
+ t.Logf("Disassociate all ports from IP %s", ipID)
+ _, err := floatingips.Update(base.Client, ipID, floatingips.UpdateOpts{PortID: ""}).Extract()
+ th.AssertNoErr(t, err)
+
+ t.Logf("Re-associate the port %s", portID)
+ _, err = floatingips.Update(base.Client, ipID, floatingips.UpdateOpts{PortID: portID}).Extract()
+ th.AssertNoErr(t, err)
+}
+
+func deleteFloatingIP(t *testing.T, ipID string) {
+ t.Logf("Deleting IP %s", ipID)
+ res := floatingips.Delete(base.Client, ipID)
+ th.AssertNoErr(t, res.Err)
+}
diff --git a/_site/acceptance/openstack/networking/v2/extensions/lbaas/common.go b/_site/acceptance/openstack/networking/v2/extensions/lbaas/common.go
new file mode 100644
index 0000000..a9db1af
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/extensions/lbaas/common.go
@@ -0,0 +1,78 @@
+package lbaas
+
+import (
+ "testing"
+
+ base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func SetupTopology(t *testing.T) (string, string) {
+ // create network
+ n, err := networks.Create(base.Client, networks.CreateOpts{Name: "tmp_network"}).Extract()
+ th.AssertNoErr(t, err)
+
+ t.Logf("Created network %s", n.ID)
+
+ // create subnet
+ s, err := subnets.Create(base.Client, subnets.CreateOpts{
+ NetworkID: n.ID,
+ CIDR: "192.168.199.0/24",
+ IPVersion: subnets.IPv4,
+ Name: "tmp_subnet",
+ }).Extract()
+ th.AssertNoErr(t, err)
+
+ t.Logf("Created subnet %s", s.ID)
+
+ return n.ID, s.ID
+}
+
+func DeleteTopology(t *testing.T, networkID string) {
+ res := networks.Delete(base.Client, networkID)
+ th.AssertNoErr(t, res.Err)
+ t.Logf("Deleted network %s", networkID)
+}
+
+func CreatePool(t *testing.T, subnetID string) string {
+ p, err := pools.Create(base.Client, pools.CreateOpts{
+ LBMethod: pools.LBMethodRoundRobin,
+ Protocol: "HTTP",
+ Name: "tmp_pool",
+ SubnetID: subnetID,
+ }).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Created pool %s", p.ID)
+
+ return p.ID
+}
+
+func DeletePool(t *testing.T, poolID string) {
+ res := pools.Delete(base.Client, poolID)
+ th.AssertNoErr(t, res.Err)
+ t.Logf("Deleted pool %s", poolID)
+}
+
+func CreateMonitor(t *testing.T) string {
+ m, err := monitors.Create(base.Client, monitors.CreateOpts{
+ Delay: 5,
+ Timeout: 10,
+ MaxRetries: 3,
+ Type: monitors.TypeHTTP,
+ ExpectedCodes: "200",
+ URLPath: "/login",
+ HTTPMethod: "GET",
+ }).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Created monitor ID [%s]", m.ID)
+
+ return m.ID
+}
diff --git a/_site/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go b/_site/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go
new file mode 100644
index 0000000..9b60582
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go
@@ -0,0 +1,95 @@
+// +build acceptance networking lbaas lbaasmember
+
+package lbaas
+
+import (
+ "testing"
+
+ base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestMembers(t *testing.T) {
+ base.Setup(t)
+ defer base.Teardown()
+
+ // setup
+ networkID, subnetID := SetupTopology(t)
+ poolID := CreatePool(t, subnetID)
+
+ // create member
+ memberID := createMember(t, poolID)
+
+ // list members
+ listMembers(t)
+
+ // update member
+ updateMember(t, memberID)
+
+ // get member
+ getMember(t, memberID)
+
+ // delete member
+ deleteMember(t, memberID)
+
+ // teardown
+ DeletePool(t, poolID)
+ DeleteTopology(t, networkID)
+}
+
+func createMember(t *testing.T, poolID string) string {
+ m, err := members.Create(base.Client, members.CreateOpts{
+ Address: "192.168.199.1",
+ ProtocolPort: 8080,
+ PoolID: poolID,
+ }).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Created member: ID [%s] Status [%s] Weight [%d] Address [%s] Port [%d]",
+ m.ID, m.Status, m.Weight, m.Address, m.ProtocolPort)
+
+ return m.ID
+}
+
+func listMembers(t *testing.T) {
+ err := members.List(base.Client, members.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+ memberList, err := members.ExtractMembers(page)
+ if err != nil {
+ t.Errorf("Failed to extract members: %v", err)
+ return false, err
+ }
+
+ for _, m := range memberList {
+ t.Logf("Listing member: ID [%s] Status [%s]", m.ID, m.Status)
+ }
+
+ return true, nil
+ })
+
+ th.AssertNoErr(t, err)
+}
+
+func updateMember(t *testing.T, memberID string) {
+ m, err := members.Update(base.Client, memberID, members.UpdateOpts{AdminStateUp: true}).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Updated member ID [%s]", m.ID)
+}
+
+func getMember(t *testing.T, memberID string) {
+ m, err := members.Get(base.Client, memberID).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Getting member ID [%s]", m.ID)
+}
+
+func deleteMember(t *testing.T, memberID string) {
+ res := members.Delete(base.Client, memberID)
+ th.AssertNoErr(t, res.Err)
+ t.Logf("Deleted member %s", memberID)
+}
diff --git a/_site/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go b/_site/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go
new file mode 100644
index 0000000..57e860c
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go
@@ -0,0 +1,77 @@
+// +build acceptance networking lbaas lbaasmonitor
+
+package lbaas
+
+import (
+ "testing"
+
+ base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestMonitors(t *testing.T) {
+ base.Setup(t)
+ defer base.Teardown()
+
+ // create monitor
+ monitorID := CreateMonitor(t)
+
+ // list monitors
+ listMonitors(t)
+
+ // update monitor
+ updateMonitor(t, monitorID)
+
+ // get monitor
+ getMonitor(t, monitorID)
+
+ // delete monitor
+ deleteMonitor(t, monitorID)
+}
+
+func listMonitors(t *testing.T) {
+ err := monitors.List(base.Client, monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+ monitorList, err := monitors.ExtractMonitors(page)
+ if err != nil {
+ t.Errorf("Failed to extract monitors: %v", err)
+ return false, err
+ }
+
+ for _, m := range monitorList {
+ t.Logf("Listing monitor: ID [%s] Type [%s] Delay [%ds] Timeout [%d] Retries [%d] Status [%s]",
+ m.ID, m.Type, m.Delay, m.Timeout, m.MaxRetries, m.Status)
+ }
+
+ return true, nil
+ })
+
+ th.AssertNoErr(t, err)
+}
+
+func updateMonitor(t *testing.T, monitorID string) {
+ opts := monitors.UpdateOpts{Delay: 5, Timeout: 10, MaxRetries: 3}
+ m, err := monitors.Update(base.Client, monitorID, opts).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Updated monitor ID [%s]", m.ID)
+}
+
+func getMonitor(t *testing.T, monitorID string) {
+ m, err := monitors.Get(base.Client, monitorID).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Getting monitor ID [%s]: URL path [%s] HTTP Method [%s] Accepted codes [%s]",
+ m.ID, m.URLPath, m.HTTPMethod, m.ExpectedCodes)
+}
+
+func deleteMonitor(t *testing.T, monitorID string) {
+ res := monitors.Delete(base.Client, monitorID)
+
+ th.AssertNoErr(t, res.Err)
+
+ t.Logf("Deleted monitor %s", monitorID)
+}
diff --git a/_site/acceptance/openstack/networking/v2/extensions/lbaas/pkg.go b/_site/acceptance/openstack/networking/v2/extensions/lbaas/pkg.go
new file mode 100644
index 0000000..f5a7df7
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/extensions/lbaas/pkg.go
@@ -0,0 +1 @@
+package lbaas
diff --git a/_site/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go b/_site/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go
new file mode 100644
index 0000000..8194064
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go
@@ -0,0 +1,98 @@
+// +build acceptance networking lbaas lbaaspool
+
+package lbaas
+
+import (
+ "testing"
+
+ base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestPools(t *testing.T) {
+ base.Setup(t)
+ defer base.Teardown()
+
+ // setup
+ networkID, subnetID := SetupTopology(t)
+
+ // create pool
+ poolID := CreatePool(t, subnetID)
+
+ // list pools
+ listPools(t)
+
+ // update pool
+ updatePool(t, poolID)
+
+ // get pool
+ getPool(t, poolID)
+
+ // create monitor
+ monitorID := CreateMonitor(t)
+
+ // associate health monitor
+ associateMonitor(t, poolID, monitorID)
+
+ // disassociate health monitor
+ disassociateMonitor(t, poolID, monitorID)
+
+ // delete pool
+ DeletePool(t, poolID)
+
+ // teardown
+ DeleteTopology(t, networkID)
+}
+
+func listPools(t *testing.T) {
+ err := pools.List(base.Client, pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+ poolList, err := pools.ExtractPools(page)
+ if err != nil {
+ t.Errorf("Failed to extract pools: %v", err)
+ return false, err
+ }
+
+ for _, p := range poolList {
+ t.Logf("Listing pool: ID [%s] Name [%s] Status [%s] LB algorithm [%s]", p.ID, p.Name, p.Status, p.LBMethod)
+ }
+
+ return true, nil
+ })
+
+ th.AssertNoErr(t, err)
+}
+
+func updatePool(t *testing.T, poolID string) {
+ opts := pools.UpdateOpts{Name: "SuperPool", LBMethod: pools.LBMethodLeastConnections}
+ p, err := pools.Update(base.Client, poolID, opts).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Updated pool ID [%s]", p.ID)
+}
+
+func getPool(t *testing.T, poolID string) {
+ p, err := pools.Get(base.Client, poolID).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Getting pool ID [%s]", p.ID)
+}
+
+func associateMonitor(t *testing.T, poolID, monitorID string) {
+ res := pools.AssociateMonitor(base.Client, poolID, monitorID)
+
+ th.AssertNoErr(t, res.Err)
+
+ t.Logf("Associated pool %s with monitor %s", poolID, monitorID)
+}
+
+func disassociateMonitor(t *testing.T, poolID, monitorID string) {
+ res := pools.DisassociateMonitor(base.Client, poolID, monitorID)
+
+ th.AssertNoErr(t, res.Err)
+
+ t.Logf("Disassociated pool %s with monitor %s", poolID, monitorID)
+}
diff --git a/_site/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go b/_site/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go
new file mode 100644
index 0000000..c8dff2d
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go
@@ -0,0 +1,101 @@
+// +build acceptance networking lbaas lbaasvip
+
+package lbaas
+
+import (
+ "testing"
+
+ base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestVIPs(t *testing.T) {
+ base.Setup(t)
+ defer base.Teardown()
+
+ // setup
+ networkID, subnetID := SetupTopology(t)
+ poolID := CreatePool(t, subnetID)
+
+ // create VIP
+ VIPID := createVIP(t, subnetID, poolID)
+
+ // list VIPs
+ listVIPs(t)
+
+ // update VIP
+ updateVIP(t, VIPID)
+
+ // get VIP
+ getVIP(t, VIPID)
+
+ // delete VIP
+ deleteVIP(t, VIPID)
+
+ // teardown
+ DeletePool(t, poolID)
+ DeleteTopology(t, networkID)
+}
+
+func createVIP(t *testing.T, subnetID, poolID string) string {
+ p, err := vips.Create(base.Client, vips.CreateOpts{
+ Protocol: "HTTP",
+ Name: "New_VIP",
+ AdminStateUp: vips.Up,
+ SubnetID: subnetID,
+ PoolID: poolID,
+ ProtocolPort: 80,
+ }).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Created pool %s", p.ID)
+
+ return p.ID
+}
+
+func listVIPs(t *testing.T) {
+ err := vips.List(base.Client, vips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+ vipList, err := vips.ExtractVIPs(page)
+ if err != nil {
+ t.Errorf("Failed to extract VIPs: %v", err)
+ return false, err
+ }
+
+ for _, vip := range vipList {
+ t.Logf("Listing VIP: ID [%s] Name [%s] Address [%s] Port [%s] Connection Limit [%d]",
+ vip.ID, vip.Name, vip.Address, vip.ProtocolPort, vip.ConnLimit)
+ }
+
+ return true, nil
+ })
+
+ th.AssertNoErr(t, err)
+}
+
+func updateVIP(t *testing.T, VIPID string) {
+ i1000 := 1000
+ _, err := vips.Update(base.Client, VIPID, vips.UpdateOpts{ConnLimit: &i1000}).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Updated VIP ID [%s]", VIPID)
+}
+
+func getVIP(t *testing.T, VIPID string) {
+ vip, err := vips.Get(base.Client, VIPID).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Getting VIP ID [%s]: Status [%s]", vip.ID, vip.Status)
+}
+
+func deleteVIP(t *testing.T, VIPID string) {
+ res := vips.Delete(base.Client, VIPID)
+
+ th.AssertNoErr(t, res.Err)
+
+ t.Logf("Deleted VIP %s", VIPID)
+}
diff --git a/_site/acceptance/openstack/networking/v2/extensions/pkg.go b/_site/acceptance/openstack/networking/v2/extensions/pkg.go
new file mode 100644
index 0000000..aeec0fa
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/extensions/pkg.go
@@ -0,0 +1 @@
+package extensions
diff --git a/_site/acceptance/openstack/networking/v2/extensions/provider_test.go b/_site/acceptance/openstack/networking/v2/extensions/provider_test.go
new file mode 100644
index 0000000..f10c9d9
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/extensions/provider_test.go
@@ -0,0 +1,68 @@
+// +build acceptance networking
+
+package extensions
+
+import (
+ "strconv"
+ "testing"
+
+ base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestNetworkCRUDOperations(t *testing.T) {
+ base.Setup(t)
+ defer base.Teardown()
+
+ // Create a network
+ n, err := networks.Create(base.Client, networks.CreateOpts{Name: "sample_network", AdminStateUp: networks.Up}).Extract()
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, n.Name, "sample_network")
+ th.AssertEquals(t, n.AdminStateUp, true)
+ networkID := n.ID
+
+ // List networks
+ pager := networks.List(base.Client, networks.ListOpts{Limit: 2})
+ err = pager.EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("--- Page ---")
+
+ networkList, err := networks.ExtractNetworks(page)
+ th.AssertNoErr(t, err)
+
+ for _, n := range networkList {
+ t.Logf("Network: ID [%s] Name [%s] Status [%s] Is shared? [%s]",
+ n.ID, n.Name, n.Status, strconv.FormatBool(n.Shared))
+ }
+
+ return true, nil
+ })
+ th.CheckNoErr(t, err)
+
+ // Get a network
+ if networkID == "" {
+ t.Fatalf("In order to retrieve a network, the NetworkID must be set")
+ }
+ n, err = networks.Get(base.Client, networkID).Extract()
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, n.Status, "ACTIVE")
+ th.AssertDeepEquals(t, n.Subnets, []string{})
+ th.AssertEquals(t, n.Name, "sample_network")
+ th.AssertEquals(t, n.AdminStateUp, true)
+ th.AssertEquals(t, n.Shared, false)
+ th.AssertEquals(t, n.ID, networkID)
+
+ // Update network
+ n, err = networks.Update(base.Client, networkID, networks.UpdateOpts{Name: "new_network_name"}).Extract()
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, n.Name, "new_network_name")
+
+ // Delete network
+ res := networks.Delete(base.Client, networkID)
+ th.AssertNoErr(t, res.Err)
+}
+
+func TestCreateMultipleNetworks(t *testing.T) {
+ //networks.CreateMany()
+}
diff --git a/_site/acceptance/openstack/networking/v2/extensions/security_test.go b/_site/acceptance/openstack/networking/v2/extensions/security_test.go
new file mode 100644
index 0000000..16ecca4
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/extensions/security_test.go
@@ -0,0 +1,169 @@
+// +build acceptance networking security
+
+package extensions
+
+import (
+ "testing"
+
+ base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/ports"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestSecurityGroups(t *testing.T) {
+ base.Setup(t)
+ defer base.Teardown()
+
+ // create security group
+ groupID := createSecGroup(t)
+
+ // list security group
+ listSecGroups(t)
+
+ // get security group
+ getSecGroup(t, groupID)
+
+ // create port with security group
+ networkID, portID := createPort(t, groupID)
+
+ // delete port
+ deletePort(t, portID)
+
+ // delete security group
+ deleteSecGroup(t, groupID)
+
+ // teardown
+ deleteNetwork(t, networkID)
+}
+
+func TestSecurityGroupRules(t *testing.T) {
+ base.Setup(t)
+ defer base.Teardown()
+
+ // create security group
+ groupID := createSecGroup(t)
+
+ // create security group rule
+ ruleID := createSecRule(t, groupID)
+
+ // list security group rule
+ listSecRules(t)
+
+ // get security group rule
+ getSecRule(t, ruleID)
+
+ // delete security group rule
+ deleteSecRule(t, ruleID)
+}
+
+func createSecGroup(t *testing.T) string {
+ sg, err := groups.Create(base.Client, groups.CreateOpts{
+ Name: "new-webservers",
+ Description: "security group for webservers",
+ }).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Created security group %s", sg.ID)
+
+ return sg.ID
+}
+
+func listSecGroups(t *testing.T) {
+ err := groups.List(base.Client, groups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+ list, err := groups.ExtractGroups(page)
+ if err != nil {
+ t.Errorf("Failed to extract secgroups: %v", err)
+ return false, err
+ }
+
+ for _, sg := range list {
+ t.Logf("Listing security group: ID [%s] Name [%s]", sg.ID, sg.Name)
+ }
+
+ return true, nil
+ })
+
+ th.AssertNoErr(t, err)
+}
+
+func getSecGroup(t *testing.T, id string) {
+ sg, err := groups.Get(base.Client, id).Extract()
+ th.AssertNoErr(t, err)
+ t.Logf("Getting security group: ID [%s] Name [%s] Description [%s]", sg.ID, sg.Name, sg.Description)
+}
+
+func createPort(t *testing.T, groupID string) (string, string) {
+ n, err := networks.Create(base.Client, networks.CreateOpts{Name: "tmp_network"}).Extract()
+ th.AssertNoErr(t, err)
+ t.Logf("Created network %s", n.ID)
+
+ opts := ports.CreateOpts{
+ NetworkID: n.ID,
+ Name: "my_port",
+ SecurityGroups: []string{groupID},
+ }
+ p, err := ports.Create(base.Client, opts).Extract()
+ th.AssertNoErr(t, err)
+ t.Logf("Created port %s with security group %s", p.ID, groupID)
+
+ return n.ID, p.ID
+}
+
+func deleteSecGroup(t *testing.T, groupID string) {
+ res := groups.Delete(base.Client, groupID)
+ th.AssertNoErr(t, res.Err)
+ t.Logf("Deleted security group %s", groupID)
+}
+
+func createSecRule(t *testing.T, groupID string) string {
+ r, err := rules.Create(base.Client, rules.CreateOpts{
+ Direction: "ingress",
+ PortRangeMin: 80,
+ EtherType: "IPv4",
+ PortRangeMax: 80,
+ Protocol: "tcp",
+ SecGroupID: groupID,
+ }).Extract()
+
+ th.AssertNoErr(t, err)
+
+ t.Logf("Created security group rule %s", r.ID)
+
+ return r.ID
+}
+
+func listSecRules(t *testing.T) {
+ err := rules.List(base.Client, rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+ list, err := rules.ExtractRules(page)
+ if err != nil {
+ t.Errorf("Failed to extract sec rules: %v", err)
+ return false, err
+ }
+
+ for _, r := range list {
+ t.Logf("Listing security rule: ID [%s]", r.ID)
+ }
+
+ return true, nil
+ })
+
+ th.AssertNoErr(t, err)
+}
+
+func getSecRule(t *testing.T, id string) {
+ r, err := rules.Get(base.Client, id).Extract()
+ th.AssertNoErr(t, err)
+ t.Logf("Getting security rule: ID [%s] Direction [%s] EtherType [%s] Protocol [%s]",
+ r.ID, r.Direction, r.EtherType, r.Protocol)
+}
+
+func deleteSecRule(t *testing.T, id string) {
+ res := rules.Delete(base.Client, id)
+ th.AssertNoErr(t, res.Err)
+ t.Logf("Deleted security rule %s", id)
+}
diff --git a/_site/acceptance/openstack/networking/v2/network_test.go b/_site/acceptance/openstack/networking/v2/network_test.go
new file mode 100644
index 0000000..be8a3a1
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/network_test.go
@@ -0,0 +1,68 @@
+// +build acceptance networking
+
+package v2
+
+import (
+ "strconv"
+ "testing"
+
+ "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestNetworkCRUDOperations(t *testing.T) {
+ Setup(t)
+ defer Teardown()
+
+ // Create a network
+ n, err := networks.Create(Client, networks.CreateOpts{Name: "sample_network", AdminStateUp: networks.Up}).Extract()
+ th.AssertNoErr(t, err)
+ defer networks.Delete(Client, n.ID)
+ th.AssertEquals(t, n.Name, "sample_network")
+ th.AssertEquals(t, n.AdminStateUp, true)
+ networkID := n.ID
+
+ // List networks
+ pager := networks.List(Client, networks.ListOpts{Limit: 2})
+ err = pager.EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("--- Page ---")
+
+ networkList, err := networks.ExtractNetworks(page)
+ th.AssertNoErr(t, err)
+
+ for _, n := range networkList {
+ t.Logf("Network: ID [%s] Name [%s] Status [%s] Is shared? [%s]",
+ n.ID, n.Name, n.Status, strconv.FormatBool(n.Shared))
+ }
+
+ return true, nil
+ })
+ th.CheckNoErr(t, err)
+
+ // Get a network
+ if networkID == "" {
+ t.Fatalf("In order to retrieve a network, the NetworkID must be set")
+ }
+ n, err = networks.Get(Client, networkID).Extract()
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, n.Status, "ACTIVE")
+ th.AssertDeepEquals(t, n.Subnets, []string{})
+ th.AssertEquals(t, n.Name, "sample_network")
+ th.AssertEquals(t, n.AdminStateUp, true)
+ th.AssertEquals(t, n.Shared, false)
+ th.AssertEquals(t, n.ID, networkID)
+
+ // Update network
+ n, err = networks.Update(Client, networkID, networks.UpdateOpts{Name: "new_network_name"}).Extract()
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, n.Name, "new_network_name")
+
+ // Delete network
+ res := networks.Delete(Client, networkID)
+ th.AssertNoErr(t, res.Err)
+}
+
+func TestCreateMultipleNetworks(t *testing.T) {
+ //networks.CreateMany()
+}
diff --git a/_site/acceptance/openstack/networking/v2/pkg.go b/_site/acceptance/openstack/networking/v2/pkg.go
new file mode 100644
index 0000000..5ec3cc8
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/pkg.go
@@ -0,0 +1 @@
+package v2
diff --git a/_site/acceptance/openstack/networking/v2/port_test.go b/_site/acceptance/openstack/networking/v2/port_test.go
new file mode 100644
index 0000000..7f22dbd
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/port_test.go
@@ -0,0 +1,117 @@
+// +build acceptance networking
+
+package v2
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/ports"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestPortCRUD(t *testing.T) {
+ Setup(t)
+ defer Teardown()
+
+ // Setup network
+ t.Log("Setting up network")
+ networkID, err := createNetwork()
+ th.AssertNoErr(t, err)
+ defer networks.Delete(Client, networkID)
+
+ // Setup subnet
+ t.Logf("Setting up subnet on network %s", networkID)
+ subnetID, err := createSubnet(networkID)
+ th.AssertNoErr(t, err)
+ defer subnets.Delete(Client, subnetID)
+
+ // Create port
+ t.Logf("Create port based on subnet %s", subnetID)
+ portID := createPort(t, networkID, subnetID)
+
+ // List ports
+ t.Logf("Listing all ports")
+ listPorts(t)
+
+ // Get port
+ if portID == "" {
+ t.Fatalf("In order to retrieve a port, the portID must be set")
+ }
+ p, err := ports.Get(Client, portID).Extract()
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, p.ID, portID)
+
+ // Update port
+ p, err = ports.Update(Client, portID, ports.UpdateOpts{Name: "new_port_name"}).Extract()
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, p.Name, "new_port_name")
+
+ // Delete port
+ res := ports.Delete(Client, portID)
+ th.AssertNoErr(t, res.Err)
+}
+
+func createPort(t *testing.T, networkID, subnetID string) string {
+ enable := false
+ opts := ports.CreateOpts{
+ NetworkID: networkID,
+ Name: "my_port",
+ AdminStateUp: &enable,
+ FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}},
+ }
+ p, err := ports.Create(Client, opts).Extract()
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, p.NetworkID, networkID)
+ th.AssertEquals(t, p.Name, "my_port")
+ th.AssertEquals(t, p.AdminStateUp, false)
+
+ return p.ID
+}
+
+func listPorts(t *testing.T) {
+ count := 0
+ pager := ports.List(Client, ports.ListOpts{})
+ err := pager.EachPage(func(page pagination.Page) (bool, error) {
+ count++
+ t.Logf("--- Page ---")
+
+ portList, err := ports.ExtractPorts(page)
+ th.AssertNoErr(t, err)
+
+ for _, p := range portList {
+ t.Logf("Port: ID [%s] Name [%s] Status [%d] MAC addr [%s] Fixed IPs [%#v] Security groups [%#v]",
+ p.ID, p.Name, p.Status, p.MACAddress, p.FixedIPs, p.SecurityGroups)
+ }
+
+ return true, nil
+ })
+
+ th.CheckNoErr(t, err)
+
+ if count == 0 {
+ t.Logf("No pages were iterated over when listing ports")
+ }
+}
+
+func createNetwork() (string, error) {
+ res, err := networks.Create(Client, networks.CreateOpts{Name: "tmp_network", AdminStateUp: networks.Up}).Extract()
+ return res.ID, err
+}
+
+func createSubnet(networkID string) (string, error) {
+ s, err := subnets.Create(Client, subnets.CreateOpts{
+ NetworkID: networkID,
+ CIDR: "192.168.199.0/24",
+ IPVersion: subnets.IPv4,
+ Name: "my_subnet",
+ EnableDHCP: subnets.Down,
+ }).Extract()
+ return s.ID, err
+}
+
+func TestPortBatchCreate(t *testing.T) {
+ // todo
+}
diff --git a/_site/acceptance/openstack/networking/v2/subnet_test.go b/_site/acceptance/openstack/networking/v2/subnet_test.go
new file mode 100644
index 0000000..097a303
--- /dev/null
+++ b/_site/acceptance/openstack/networking/v2/subnet_test.go
@@ -0,0 +1,86 @@
+// +build acceptance networking
+
+package v2
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
+ "github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+ Setup(t)
+ defer Teardown()
+
+ pager := subnets.List(Client, subnets.ListOpts{Limit: 2})
+ err := pager.EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("--- Page ---")
+
+ subnetList, err := subnets.ExtractSubnets(page)
+ th.AssertNoErr(t, err)
+
+ for _, s := range subnetList {
+ t.Logf("Subnet: ID [%s] Name [%s] IP Version [%d] CIDR [%s] GatewayIP [%s]",
+ s.ID, s.Name, s.IPVersion, s.CIDR, s.GatewayIP)
+ }
+
+ return true, nil
+ })
+ th.CheckNoErr(t, err)
+}
+
+func TestCRUD(t *testing.T) {
+ Setup(t)
+ defer Teardown()
+
+ // Setup network
+ t.Log("Setting up network")
+ n, err := networks.Create(Client, networks.CreateOpts{Name: "tmp_network", AdminStateUp: networks.Up}).Extract()
+ th.AssertNoErr(t, err)
+ networkID := n.ID
+ defer networks.Delete(Client, networkID)
+
+ // Create subnet
+ t.Log("Create subnet")
+ enable := false
+ opts := subnets.CreateOpts{
+ NetworkID: networkID,
+ CIDR: "192.168.199.0/24",
+ IPVersion: subnets.IPv4,
+ Name: "my_subnet",
+ EnableDHCP: &enable,
+ }
+ s, err := subnets.Create(Client, opts).Extract()
+ th.AssertNoErr(t, err)
+
+ th.AssertEquals(t, s.NetworkID, networkID)
+ th.AssertEquals(t, s.CIDR, "192.168.199.0/24")
+ th.AssertEquals(t, s.IPVersion, 4)
+ th.AssertEquals(t, s.Name, "my_subnet")
+ th.AssertEquals(t, s.EnableDHCP, false)
+ subnetID := s.ID
+
+ // Get subnet
+ t.Log("Getting subnet")
+ s, err = subnets.Get(Client, subnetID).Extract()
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, s.ID, subnetID)
+
+ // Update subnet
+ t.Log("Update subnet")
+ s, err = subnets.Update(Client, subnetID, subnets.UpdateOpts{Name: "new_subnet_name"}).Extract()
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, s.Name, "new_subnet_name")
+
+ // Delete subnet
+ t.Log("Delete subnet")
+ res := subnets.Delete(Client, subnetID)
+ th.AssertNoErr(t, res.Err)
+}
+
+func TestBatchCreate(t *testing.T) {
+ // todo
+}
diff --git a/_site/acceptance/openstack/objectstorage/v1/accounts_test.go b/_site/acceptance/openstack/objectstorage/v1/accounts_test.go
new file mode 100644
index 0000000..6768927
--- /dev/null
+++ b/_site/acceptance/openstack/objectstorage/v1/accounts_test.go
@@ -0,0 +1,58 @@
+// +build acceptance
+
+package v1
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts"
+)
+
+func TestAccounts(t *testing.T) {
+ // Create a provider client for making the HTTP requests.
+ // See common.go in this directory for more information.
+ client, err := newClient()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // Update an account's metadata.
+ err = accounts.Update(client, accounts.UpdateOpts{
+ Metadata: metadata,
+ })
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ // Defer the deletion of the metadata set above.
+ defer func() {
+ tempMap := make(map[string]string)
+ for k := range metadata {
+ tempMap[k] = ""
+ }
+ err = accounts.Update(client, accounts.UpdateOpts{
+ Metadata: tempMap,
+ })
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ }()
+
+ // Retrieve account metadata.
+ gr, err := accounts.Get(client, accounts.GetOpts{})
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ // Extract the custom metadata from the 'Get' response.
+ am := accounts.ExtractMetadata(gr)
+ for k := range metadata {
+ if am[k] != metadata[strings.Title(k)] {
+ t.Errorf("Expected custom metadata with key: %s", k)
+ return
+ }
+ }
+}
diff --git a/_site/acceptance/openstack/objectstorage/v1/common.go b/_site/acceptance/openstack/objectstorage/v1/common.go
new file mode 100644
index 0000000..08065a4
--- /dev/null
+++ b/_site/acceptance/openstack/objectstorage/v1/common.go
@@ -0,0 +1,28 @@
+// +build acceptance
+
+package v1
+
+import (
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/openstack"
+ "github.com/rackspace/gophercloud/openstack/utils"
+ "os"
+)
+
+var metadata = map[string]string{"gopher": "cloud"}
+
+func newClient() (*gophercloud.ServiceClient, error) {
+ ao, err := utils.AuthOptions()
+ if err != nil {
+ return nil, err
+ }
+
+ client, err := openstack.AuthenticatedClient(ao)
+ if err != nil {
+ return nil, err
+ }
+
+ return openstack.NewStorageV1(client, gophercloud.EndpointOpts{
+ Region: os.Getenv("OS_REGION_NAME"),
+ })
+}
diff --git a/_site/acceptance/openstack/objectstorage/v1/containers_test.go b/_site/acceptance/openstack/objectstorage/v1/containers_test.go
new file mode 100644
index 0000000..b541307
--- /dev/null
+++ b/_site/acceptance/openstack/objectstorage/v1/containers_test.go
@@ -0,0 +1,108 @@
+// +build acceptance
+
+package v1
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/rackspace/gophercloud/acceptance/tools"
+ "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+// numContainers is the number of containers to create for testing.
+var numContainers = 2
+
+func TestContainers(t *testing.T) {
+ // Create a new client to execute the HTTP requests. See common.go for newClient body.
+ client, err := newClient()
+ if err != nil {
+ t.Error(err)
+ }
+
+ // Create a slice of random container names.
+ cNames := make([]string, numContainers)
+ for i := 0; i < numContainers; i++ {
+ cNames[i] = tools.RandomString("gophercloud-test-container-", 8)
+ }
+
+ // Create numContainers containers.
+ for i := 0; i < len(cNames); i++ {
+ _, err := containers.Create(client, cNames[i], nil).ExtractHeaders()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ // Delete the numContainers containers after function completion.
+ defer func() {
+ for i := 0; i < len(cNames); i++ {
+ _, err = containers.Delete(client, cNames[i]).ExtractHeaders()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ }()
+
+ // List the numContainer names that were just created. To just list those,
+ // the 'prefix' parameter is used.
+ err = containers.List(client, &containers.ListOpts{Full: true, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) {
+ containerList, err := containers.ExtractInfo(page)
+ if err != nil {
+ t.Error(err)
+ }
+ for _, n := range containerList {
+ t.Logf("Container: Name [%s] Count [%d] Bytes [%d]",
+ n.Name, n.Count, n.Bytes)
+ }
+
+ return true, nil
+ })
+ if err != nil {
+ t.Error(err)
+ }
+
+ // List the info for the numContainer containers that were created.
+ err = containers.List(client, &containers.ListOpts{Full: false, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) {
+ containerList, err := containers.ExtractNames(page)
+ if err != nil {
+ return false, err
+ }
+ for _, n := range containerList {
+ t.Logf("Container: Name [%s]", n)
+ }
+
+ return true, nil
+ })
+ if err != nil {
+ t.Error(err)
+ }
+
+ // Update one of the numContainer container metadata.
+ _, err = containers.Update(client, cNames[0], &containers.UpdateOpts{Metadata: metadata}).ExtractHeaders()
+ if err != nil {
+ t.Error(err)
+ }
+ // After the tests are done, delete the metadata that was set.
+ defer func() {
+ tempMap := make(map[string]string)
+ for k := range metadata {
+ tempMap[k] = ""
+ }
+ _, err = containers.Update(client, cNames[0], &containers.UpdateOpts{Metadata: tempMap}).ExtractHeaders()
+ if err != nil {
+ t.Error(err)
+ }
+ }()
+
+ // Retrieve a container's metadata.
+ cm, err := containers.Get(client, cNames[0]).ExtractMetadata()
+ if err != nil {
+ t.Error(err)
+ }
+ for k := range metadata {
+ if cm[k] != metadata[strings.Title(k)] {
+ t.Errorf("Expected custom metadata with key: %s", k)
+ }
+ }
+}
diff --git a/_site/acceptance/openstack/objectstorage/v1/objects_test.go b/_site/acceptance/openstack/objectstorage/v1/objects_test.go
new file mode 100644
index 0000000..5a63a4c
--- /dev/null
+++ b/_site/acceptance/openstack/objectstorage/v1/objects_test.go
@@ -0,0 +1,162 @@
+// +build acceptance
+
+package v1
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+
+ "github.com/rackspace/gophercloud/acceptance/tools"
+ "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
+ "github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+// numObjects is the number of objects to create for testing.
+var numObjects = 2
+
+func TestObjects(t *testing.T) {
+ // Create a provider client for executing the HTTP request.
+ // See common.go for more information.
+ client, err := newClient()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // Make a slice of length numObjects to hold the random object names.
+ oNames := make([]string, numObjects)
+ for i := 0; i < len(oNames); i++ {
+ oNames[i] = tools.RandomString("test-object-", 8)
+ }
+
+ // Create a container to hold the test objects.
+ cName := tools.RandomString("test-container-", 8)
+ _, err = containers.Create(client, cName, nil).ExtractHeaders()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ // Defer deletion of the container until after testing.
+ defer func() {
+ _, err = containers.Delete(client, cName).ExtractHeaders()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ }()
+
+ // Create a slice of buffers to hold the test object content.
+ oContents := make([]*bytes.Buffer, numObjects)
+ for i := 0; i < numObjects; i++ {
+ oContents[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10)))
+ _, err = objects.Create(client, cName, oNames[i], oContents[i], nil).ExtractHeaders()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ }
+ // Delete the objects after testing.
+ defer func() {
+ for i := 0; i < numObjects; i++ {
+ _, err = objects.Delete(client, cName, oNames[i], nil).ExtractHeaders()
+ }
+ }()
+
+ ons := make([]string, 0, len(oNames))
+ err = objects.List(client, cName, &objects.ListOpts{Full: false, Prefix: "test-object-"}).EachPage(func(page pagination.Page) (bool, error) {
+ names, err := objects.ExtractNames(page)
+ if err != nil {
+ return false, err
+ }
+ ons = append(ons, names...)
+
+ return true, nil
+ })
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if len(ons) != len(oNames) {
+ t.Errorf("Expected %d names and got %d", len(oNames), len(ons))
+ return
+ }
+
+ ois := make([]objects.Object, 0, len(oNames))
+ err = objects.List(client, cName, &objects.ListOpts{Full: true, Prefix: "test-object-"}).EachPage(func(page pagination.Page) (bool, error) {
+ info, err := objects.ExtractInfo(page)
+ if err != nil {
+ return false, nil
+ }
+
+ ois = append(ois, info...)
+
+ return true, nil
+ })
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if len(ois) != len(oNames) {
+ t.Errorf("Expected %d containers and got %d", len(oNames), len(ois))
+ return
+ }
+
+ // Copy the contents of one object to another.
+ _, err = objects.Copy(client, cName, oNames[0], &objects.CopyOpts{Destination: cName + "/" + oNames[1]}).ExtractHeaders()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // Download one of the objects that was created above.
+ o1Content, err := objects.Download(client, cName, oNames[0], nil).ExtractContent()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ // Download the another object that was create above.
+ o2Content, err := objects.Download(client, cName, oNames[1], nil).ExtractContent()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ // Compare the two object's contents to test that the copy worked.
+ if string(o2Content) != string(o1Content) {
+ t.Errorf("Copy failed. Expected\n%s\nand got\n%s", string(o1Content), string(o2Content))
+ return
+ }
+
+ // Update an object's metadata.
+ _, err = objects.Update(client, cName, oNames[0], &objects.UpdateOpts{Metadata: metadata}).ExtractHeaders()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ // Delete the object's metadata after testing.
+ defer func() {
+ tempMap := make(map[string]string)
+ for k := range metadata {
+ tempMap[k] = ""
+ }
+ _, err = objects.Update(client, cName, oNames[0], &objects.UpdateOpts{Metadata: tempMap}).ExtractHeaders()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ }()
+
+ // Retrieve an object's metadata.
+ om, err := objects.Get(client, cName, oNames[0], nil).ExtractMetadata()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ for k := range metadata {
+ if om[k] != metadata[strings.Title(k)] {
+ t.Errorf("Expected custom metadata with key: %s", k)
+ return
+ }
+ }
+}
diff --git a/_site/acceptance/openstack/pkg.go b/_site/acceptance/openstack/pkg.go
new file mode 100644
index 0000000..3a8ecdb
--- /dev/null
+++ b/_site/acceptance/openstack/pkg.go
@@ -0,0 +1,4 @@
+// +build acceptance
+
+package openstack
+
diff --git a/_site/acceptance/rackspace/client_test.go b/_site/acceptance/rackspace/client_test.go
new file mode 100644
index 0000000..e68aef8
--- /dev/null
+++ b/_site/acceptance/rackspace/client_test.go
@@ -0,0 +1,29 @@
+// +build acceptance
+
+package rackspace
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud/openstack/utils"
+ "github.com/rackspace/gophercloud/rackspace"
+)
+
+func TestAuthenticatedClient(t *testing.T) {
+ // Obtain credentials from the environment.
+ ao, err := utils.AuthOptions()
+ if err != nil {
+ t.Fatalf("Unable to acquire credentials: %v", err)
+ }
+
+ client, err := rackspace.AuthenticatedClient(ao)
+ if err != nil {
+ t.Fatalf("Unable to authenticate: %v", err)
+ }
+
+ if client.TokenID == "" {
+ t.Errorf("No token ID assigned to the client")
+ }
+
+ t.Logf("Client successfully acquired a token: %v", client.TokenID)
+}
diff --git a/_site/acceptance/rackspace/identity/v2/extension_test.go b/_site/acceptance/rackspace/identity/v2/extension_test.go
new file mode 100644
index 0000000..a50e015
--- /dev/null
+++ b/_site/acceptance/rackspace/identity/v2/extension_test.go
@@ -0,0 +1,54 @@
+// +build acceptance
+
+package v2
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud/pagination"
+ extensions2 "github.com/rackspace/gophercloud/rackspace/identity/v2/extensions"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestExtensions(t *testing.T) {
+ service := authenticatedClient(t)
+
+ t.Logf("Extensions available on this identity endpoint:")
+ count := 0
+ var chosen string
+ err := extensions2.List(service).EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("--- Page %02d ---", count)
+
+ extensions, err := extensions2.ExtractExtensions(page)
+ th.AssertNoErr(t, err)
+
+ for i, ext := range extensions {
+ if chosen == "" {
+ chosen = ext.Alias
+ }
+
+ t.Logf("[%02d] name=[%s] namespace=[%s]", i, ext.Name, ext.Namespace)
+ t.Logf(" alias=[%s] updated=[%s]", ext.Alias, ext.Updated)
+ t.Logf(" description=[%s]", ext.Description)
+ }
+
+ count++
+ return true, nil
+ })
+ th.AssertNoErr(t, err)
+
+ if chosen == "" {
+ t.Logf("No extensions found.")
+ return
+ }
+
+ ext, err := extensions2.Get(service, chosen).Extract()
+ th.AssertNoErr(t, err)
+
+ t.Logf("Detail for extension [%s]:", chosen)
+ t.Logf(" name=[%s]", ext.Name)
+ t.Logf(" namespace=[%s]", ext.Namespace)
+ t.Logf(" alias=[%s]", ext.Alias)
+ t.Logf(" updated=[%s]", ext.Updated)
+ t.Logf(" description=[%s]", ext.Description)
+}
diff --git a/_site/acceptance/rackspace/identity/v2/identity_test.go b/_site/acceptance/rackspace/identity/v2/identity_test.go
new file mode 100644
index 0000000..019a9e6
--- /dev/null
+++ b/_site/acceptance/rackspace/identity/v2/identity_test.go
@@ -0,0 +1,51 @@
+// +build acceptance
+
+package v2
+
+import (
+ "os"
+ "testing"
+
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/rackspace"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func rackspaceAuthOptions(t *testing.T) gophercloud.AuthOptions {
+ // Obtain credentials from the environment.
+ options := gophercloud.AuthOptions{
+ Username: os.Getenv("RS_USERNAME"),
+ APIKey: os.Getenv("RS_APIKEY"),
+ }
+
+ if options.Username == "" {
+ t.Fatal("Please provide a Rackspace username as RS_USERNAME.")
+ }
+ if options.APIKey == "" {
+ t.Fatal("Please provide a Rackspace API key as RS_APIKEY.")
+ }
+
+ return options
+}
+
+func createClient(t *testing.T, auth bool) *gophercloud.ServiceClient {
+ ao := rackspaceAuthOptions(t)
+
+ provider, err := rackspace.NewClient(ao.IdentityEndpoint)
+ th.AssertNoErr(t, err)
+
+ if auth {
+ err = rackspace.Authenticate(provider, ao)
+ th.AssertNoErr(t, err)
+ }
+
+ return rackspace.NewIdentityV2(provider)
+}
+
+func unauthenticatedClient(t *testing.T) *gophercloud.ServiceClient {
+ return createClient(t, false)
+}
+
+func authenticatedClient(t *testing.T) *gophercloud.ServiceClient {
+ return createClient(t, true)
+}
diff --git a/_site/acceptance/rackspace/identity/v2/tenant_test.go b/_site/acceptance/rackspace/identity/v2/tenant_test.go
new file mode 100644
index 0000000..6081a49
--- /dev/null
+++ b/_site/acceptance/rackspace/identity/v2/tenant_test.go
@@ -0,0 +1,37 @@
+// +build acceptance
+
+package v2
+
+import (
+ "testing"
+
+ "github.com/rackspace/gophercloud/pagination"
+ rstenants "github.com/rackspace/gophercloud/rackspace/identity/v2/tenants"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestTenants(t *testing.T) {
+ service := authenticatedClient(t)
+
+ t.Logf("Tenants available to the currently issued token:")
+ count := 0
+ err := rstenants.List(service, nil).EachPage(func(page pagination.Page) (bool, error) {
+ t.Logf("--- Page %02d ---", count)
+
+ tenants, err := rstenants.ExtractTenants(page)
+ th.AssertNoErr(t, err)
+
+ for i, tenant := range tenants {
+ t.Logf("[%02d] id=[%s]", i, tenant.ID)
+ t.Logf(" name=[%s] enabled=[%v]", i, tenant.Name, tenant.Enabled)
+ t.Logf(" description=[%s]", tenant.Description)
+ }
+
+ count++
+ return true, nil
+ })
+ th.AssertNoErr(t, err)
+ if count == 0 {
+ t.Errorf("No tenants listed for your current token.")
+ }
+}
diff --git a/_site/acceptance/rackspace/pkg.go b/_site/acceptance/rackspace/pkg.go
new file mode 100644
index 0000000..5d17b32
--- /dev/null
+++ b/_site/acceptance/rackspace/pkg.go
@@ -0,0 +1 @@
+package rackspace
diff --git a/_site/acceptance/tools/tools.go b/_site/acceptance/tools/tools.go
new file mode 100644
index 0000000..4771ebb
--- /dev/null
+++ b/_site/acceptance/tools/tools.go
@@ -0,0 +1,50 @@
+// +build acceptance
+package tools
+
+import (
+ "crypto/rand"
+ "errors"
+ "time"
+)
+
+// ErrTimeout is returned if WaitFor takes longer than 300 second to happen.
+var ErrTimeout = errors.New("Timed out")
+
+// WaitFor polls a predicate function once per second to wait for a certain state to arrive.
+func WaitFor(predicate func() (bool, error)) error {
+ for i := 0; i < 300; i++ {
+ time.Sleep(1 * time.Second)
+
+ satisfied, err := predicate()
+ if err != nil {
+ return err
+ }
+ if satisfied {
+ return nil
+ }
+ }
+ return ErrTimeout
+}
+
+// MakeNewPassword generates a new string that's guaranteed to be different than the given one.
+func MakeNewPassword(oldPass string) string {
+ randomPassword := RandomString("", 16)
+ for randomPassword == oldPass {
+ randomPassword = RandomString("", 16)
+ }
+ return randomPassword
+}
+
+// RandomString generates a string of given length, but random content.
+// All content will be within the ASCII graphic character set.
+// (Implementation from Even Shaw's contribution on
+// http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go).
+func RandomString(prefix string, n int) string {
+ const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ var bytes = make([]byte, n)
+ rand.Read(bytes)
+ for i, b := range bytes {
+ bytes[i] = alphanum[b%byte(len(alphanum))]
+ }
+ return prefix + string(bytes)
+}