merge lbaasv2, portsbinding, volumes v2; remove 'rackspace' refs; update docs
diff --git a/.travis.yml b/.travis.yml
index 42c095d..4b317c2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,20 +1,21 @@
 language: go
 sudo: false
 install:
-  - go get golang.org/x/crypto/ssh
-  - go get -v -tags 'fixtures acceptance' ./...
+- go get golang.org/x/crypto/ssh
+- go get -v -tags 'fixtures acceptance' ./...
 go:
-  - 1.4
-  - 1.5
-  - tip
+- 1.6
+- tip
 env:
-  - COVERALLS_TOKEN=2k7PTU3xa474Hymwgdj6XjqenNfGTNkO8
+  global:
+  - secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ="
 before_install:
-  - go get github.com/axw/gocov/gocov
-  - go get github.com/mattn/goveralls
-  - go get github.com/pierrre/gotestcover
-  - if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
+- go get github.com/axw/gocov/gocov
+- go get github.com/mattn/goveralls
+- go get github.com/pierrre/gotestcover
+- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover;
+  fi
 script:
-  - $HOME/gopath/bin/gotestcover -v -tags=fixtures -coverprofile=cover.out ./...
+- $HOME/gopath/bin/gotestcover -v -tags=fixtures -coverprofile=cover.out ./...
 after_success:
-  - $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out
+- $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/CHANGELOG.md
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 73c95bd..b2713f5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -17,10 +17,10 @@
 2. Move into the directory that houses your local repository:
 
    ```bash
-   cd ${GOPATH}/src/github.com/rackspace/gophercloud
+   cd ${GOPATH}/src/github.com/gophercloud/gophercloud
    ```
 
-3. Fork the `rackspace/gophercloud` repository and update your remote refs. You
+3. Fork the `gophercloud/gophercloud` repository and update your remote refs. You
 will need to rename the `origin` remote branch to `upstream`, and add your
 fork as `origin` instead:
 
@@ -50,12 +50,12 @@
 
 When working on a new or existing feature, testing will be the backbone of your
 work since it helps uncover and prevent regressions in the codebase. There are
-two types of test we use in gophercloud: unit tests and acceptance tests, which
+two types of test we use in Gophercloud: unit tests and acceptance tests, which
 are both described below.
 
 ### Unit tests
 
-Unit tests are the fine-grained tests that establish and ensure the behaviour
+Unit tests are the fine-grained tests that establish and ensure the behavior
 of individual units of functionality. We usually test on an
 operation-by-operation basis (an operation typically being an API action) with
 the use of mocking to set up explicit expectations. Each operation will set up
@@ -69,7 +69,7 @@
 import (
   "testing"
 
-  "github.com/rackspace/gophercloud/testhelper"
+  "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestSomething(t *testing.T) {
@@ -95,8 +95,8 @@
 import (
 	"testing"
 
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
 )
 
 func TestGet(t *testing.T) {
@@ -142,7 +142,7 @@
 ### Acceptance tests
 
 As we've already mentioned, unit tests have a very narrow and confined focus -
-they test small units of behaviour. Acceptance tests on the other hand have a
+they test small units of behavior. Acceptance tests on the other hand have a
 far larger scope: they are fully functional tests that test the entire API of a
 service in one fell swoop. They don't care about unit isolation or mocking
 expectations, they instead do a full run-through and consequently test how the
@@ -180,85 +180,40 @@
 cd ./path/to/package && go test -tags fixtures .
 ```
 
-## Basic style guide
+## Style guide
 
-We follow the standard formatting recommendations and language idioms set out
-in the [Effective Go](https://golang.org/doc/effective_go.html) guide. It's
-definitely worth reading - but the relevant sections are
-[formatting](https://golang.org/doc/effective_go.html#formatting)
-and [names](https://golang.org/doc/effective_go.html#names).
+See [here](/STYLEGUIDE.md)
 
-## 5 ways to get involved
+## 3 ways to get involved
 
 There are five main ways you can get involved in our open-source project, and
 each is described briefly below. Once you've made up your mind and decided on
 your fix, you will need to follow the same basic steps that all submissions are
 required to adhere to:
 
-1. [fork](https://help.github.com/articles/fork-a-repo/) the `rackspace/gophercloud` repository
+1. [fork](https://help.github.com/articles/fork-a-repo/) the `gophercloud/gophercloud` repository
 2. checkout a [new branch](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches)
 3. submit your branch as a [pull request](https://help.github.com/articles/creating-a-pull-request/)
 
-### 1. Providing feedback
-
-On of the easiest ways to get readily involved in our project is to let us know
-about your experiences using our SDK. Feedback like this is incredibly useful
-to us, because it allows us to refine and change features based on what our
-users want and expect of us. There are a bunch of ways to get in contact! You
-can [ping us](https://developer.rackspace.com/support/) via e-mail, talk to us on irc
-(#rackspace-dev on freenode), [tweet us](https://twitter.com/rackspace), or
-submit an issue on our [bug tracker](/issues). Things you might like to tell us
-are:
-
-* how easy was it to start using our SDK?
-* did it meet your expectations? If not, why not?
-* did our documentation help or hinder you?
-* what could we improve in general?
-
-### 2. Fixing bugs
+### 1. Fixing bugs
 
 If you want to start fixing open bugs, we'd really appreciate that! Bug fixing
 is central to any project. The best way to get started is by heading to our
-[bug tracker](https://github.com/rackspace/gophercloud/issues) and finding open
+[bug tracker](https://github.com/gophercloud/gophercloud/issues) and finding open
 bugs that you think nobody is working on. It might be useful to comment on the
 thread to see the current state of the issue and if anybody has made any
 breakthroughs on it so far.
 
-### 3. Improving documentation
-
-We have three forms of documentation:
-
-* short README documents that briefly introduce a topic
-* reference documentation on [godoc.org](http://godoc.org) that is automatically
-generated from source code comments
-* user documentation on our [homepage](http://gophercloud.io) that includes
-getting started guides, installation guides and code samples
+### 2. Improving documentation
+The best source of documentation is on [godoc.org](http://godoc.org). It is
+automatically generated from the source code.
 
 If you feel that a certain section could be improved - whether it's to clarify
 ambiguity, correct a technical mistake, or to fix a grammatical error - please
 feel entitled to do so! We welcome doc pull requests with the same childlike
 enthusiasm as any other contribution!
 
-### 4. Optimizing existing features
-
-If you would like to improve or optimize an existing feature, please be aware
-that we adhere to [semantic versioning](http://semver.org) - which means that
-we cannot introduce breaking changes to the API without a major version change
-(v1.x -> v2.x). Making that leap is a big step, so we encourage contributors to
-refactor rather than rewrite. Running tests will prevent regression and avoid
-the possibility of breaking somebody's current implementation.
-
-Another tip is to keep the focus of your work as small as possible - try not to
-introduce a change that affects lots and lots of files because it introduces
-added risk and increases the cognitive load on the reviewers checking your
-work. Change-sets which are easily understood and will not negatively impact
-users are more likely to be integrated quickly.
-
-Lastly, if you're seeking to optimize a particular operation, you should try to
-demonstrate a negative performance impact - perhaps using go's inbuilt
-[benchmark capabilities](http://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go).
-
-### 5. Working on a new feature
+###3. Working on a new feature
 
 If you've found something we've left out, definitely feel free to start work on
 introducing that feature. It's always useful to open an issue or submit a pull
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
deleted file mode 100644
index 63beb30..0000000
--- a/CONTRIBUTORS.md
+++ /dev/null
@@ -1,13 +0,0 @@
-Contributors
-============
-
-| Name | Email |
-| ---- | ----- |
-| Samuel A. Falvo II | <sam.falvo@rackspace.com>
-| Glen Campbell | <glen.campbell@rackspace.com>
-| Jesse Noller | <jesse.noller@rackspace.com>
-| Jon Perritt | <jon.perritt@rackspace.com>
-| Ash Wilson | <ash.wilson@rackspace.com>
-| Jamie Hannaford | <jamie.hannaford@rackspace.com>
-| Don Schenck | don.schenck@rackspace.com>
-| Joe Topjian | <joe@topjian.net>
diff --git a/README.md b/README.md
index 4b0042b..4d2efaf 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,12 @@
 # Gophercloud: an OpenStack SDK for Go
-[![Build Status](https://travis-ci.org/rackspace/gophercloud.svg?branch=master)](https://travis-ci.org/rackspace/gophercloud) [![Coverage Status](https://coveralls.io/repos/rackspace/gophercloud/badge.png)](https://coveralls.io/r/rackspace/gophercloud)
+[![Build Status](https://travis-ci.org/gophercloud/gophercloud.svg?branch=master)](https://travis-ci.org/gophercloud/gophercloud)
+[![Coverage Status](https://coveralls.io/repos/github/gophercloud/gophercloud/badge.svg?branch=master)](https://coveralls.io/github/gophercloud/gophercloud?branch=master)
 
-Gophercloud is a flexible SDK that allows you to consume and work with OpenStack
-clouds in a simple and idiomatic way using golang. Many services are supported,
-including Compute, Block Storage, Object Storage, Networking, and Identity.
-Each service API is backed with getting started guides, code samples, reference
-documentation, unit tests and acceptance tests.
+Gophercloud is an OpenStack Go SDK.
 
 ## Useful links
 
-* [Gophercloud homepage](http://gophercloud.io)
-* [Reference documentation](http://godoc.org/github.com/rackspace/gophercloud)
-* [Getting started guides](http://gophercloud.io/docs)
+* [Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud)
 * [Effective Go](https://golang.org/doc/effective_go.html)
 
 ## How to install
@@ -30,9 +25,9 @@
 Gophercloud as a dependency like so:
 
 ```bash
-go get github.com/rackspace/gophercloud
+go get github.com/gophercloud/gophercloud
 
-# Edit your code to import relevant packages from "github.com/rackspace/gophercloud"
+# Edit your code to import relevant packages from "github.com/gophercloud/gophercloud"
 
 godep save ./...
 ```
@@ -54,7 +49,6 @@
 
 * username
 * password
-* tenant name or tenant ID
 * a valid Keystone identity URL
 
 For users that have the OpenStack dashboard installed, there's a shortcut. If
@@ -73,9 +67,9 @@
 
 ```go
 import (
-  "github.com/rackspace/gophercloud"
-  "github.com/rackspace/gophercloud/openstack"
-  "github.com/rackspace/gophercloud/openstack/utils"
+  "github.com/gophercloud/gophercloud"
+  "github.com/gophercloud/gophercloud/openstack"
+  "github.com/gophercloud/gophercloud/openstack/utils"
 )
 
 // Option 1: Pass in the values yourself
@@ -83,7 +77,6 @@
   IdentityEndpoint: "https://my-openstack.com:5000/v2.0",
   Username: "{username}",
   Password: "{password}",
-  TenantID: "{tenant_id}",
 }
 
 // Option 2: Use a utility function to retrieve all your environment variables
@@ -119,7 +112,7 @@
 interested in:
 
 ```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+import "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 
 server, err := servers.Create(client, servers.CreateOpts{
   Name:      "My new server!",
@@ -128,33 +121,19 @@
 }).Extract()
 ```
 
-If you are unsure about what images and flavors are, you can read our [Compute
-Getting Started guide](http://gophercloud.io/docs/compute). The above code
-sample creates a new server with the parameters, and embodies the new resource
-in the `server` variable (a
-[`servers.Server`](http://godoc.org/github.com/rackspace/gophercloud) struct).
+The above code sample creates a new server with the parameters, and embodies the
+new resource in the `server` variable (a
+[`servers.Server`](http://godoc.org/github.com/gophercloud/gophercloud) struct).
 
-### Next steps
+## Backwards-Compatibility Guarantees
 
-Cool! You've handled authentication, got your `ProviderClient` and provisioned
-a new server. You're now ready to use more OpenStack services.
-
-* [Getting started with Compute](http://gophercloud.io/docs/compute)
-* [Getting started with Object Storage](http://gophercloud.io/docs/object-storage)
-* [Getting started with Networking](http://gophercloud.io/docs/networking)
-* [Getting started with Block Storage](http://gophercloud.io/docs/block-storage)
-* [Getting started with Identity](http://gophercloud.io/docs/identity)
+None. Vendor it and write tests covering the parts you use.
 
 ## Contributing
 
-Engaging the community and lowering barriers for contributors is something we
-care a lot about. For this reason, we've taken the time to write a [contributing
-guide](./CONTRIBUTING.md) for folks interested in getting involved in our project.
-If you're not sure how you can get involved, feel free to submit an issue or
-[contact us](https://developer.rackspace.com/support/). You don't need to be a
-Go expert - all members of the community are welcome!
+See the [contributing guide](./CONTRIBUTING.md).
 
 ## Help and feedback
 
 If you're struggling with something or have spotted a potential bug, feel free
-to submit an issue to our [bug tracker](/issues) or [contact us directly](https://developer.rackspace.com/support/).
+to submit an issue to our [bug tracker](/issues).
diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md
new file mode 100644
index 0000000..4cc84f6
--- /dev/null
+++ b/STYLEGUIDE.md
@@ -0,0 +1,39 @@
+
+## On Pull Requests
+
+- Before you start a PR there needs to be a Github issue and a discussion about it
+  on that issue with a core contributor, even if it's just a 'SGTM'.
+
+- A PR's description must reference the issue it closes with a `For <ISSUE NUMBER>` (e.g. For #293).
+
+- A PR's description must contain link(s) to the line(s) in the OpenStack
+  source code (on Github) that prove(s) the PR code to be valid. Links to documentation
+  are not good enough. The link(s) should be to a non-`master` branch. For example,
+  a pull request implementing the creation of a Neutron v2 subnet might put the
+  following link in the description:
+  https://github.com/openstack/neutron/blob/stable/mitaka/neutron/api/v2/attributes.py#L749
+  From that link, a reviewer (or user) can verify the fields in the request/response
+  objects in the PR.
+
+- A PR that is in-progress should have `[wip]` in front of the PR's title. When
+  ready for review, remove the `[wip]` and ping a core contributor with an `@`.
+
+- A PR should be small. Even if you intend on implementing an entire
+  service, a PR should only be one route of that service
+  (e.g. create server or get server, but not both).
+
+- Unless explicitly asked, do not squash commits in the middle of a review; only
+  append. It makes it difficult for the reviewer to see what's changed from one
+  review to the next.
+
+## On Code
+
+- In re design: follow as closely as is reasonable the code already in the library.
+  Most operations (e.g. create, delete) admit the same design.
+
+- Unit tests and acceptance (integration) tests must be written to cover each PR.
+  Tests for operations with several options (e.g. list, create) should include all
+  the options in the tests. This will allow users to verify an operation on their
+  own infrastructure and see an example of usage.
+
+- If in doubt, ask in-line on the PR.
diff --git a/UPGRADING.md b/UPGRADING.md
deleted file mode 100644
index 76a94d5..0000000
--- a/UPGRADING.md
+++ /dev/null
@@ -1,338 +0,0 @@
-# Upgrading to v1.0.0
-
-With the arrival of this new major version increment, the unfortunate news is
-that breaking changes have been introduced to existing services. The API
-has been completely rewritten from the ground up to make the library more
-extensible, maintainable and easy-to-use.
-
-Below we've compiled upgrade instructions for the various services that
-existed before. If you have a specific issue that is not addressed below,
-please [submit an issue](/issues/new) or
-[e-mail our support team](https://developer.rackspace.com/support/).
-
-* [Authentication](#authentication)
-* [Servers](#servers)
-  * [List servers](#list-servers)
-  * [Get server details](#get-server-details)
-  * [Create server](#create-server)
-  * [Resize server](#resize-server)
-  * [Reboot server](#reboot-server)
-  * [Update server](#update-server)
-  * [Rebuild server](#rebuild-server)
-  * [Change admin password](#change-admin-password)
-  * [Delete server](#delete-server)
-  * [Rescue server](#rescue-server)
-* [Images and flavors](#images-and-flavors)
-  * [List images](#list-images)
-  * [List flavors](#list-flavors)
-  * [Create/delete image](#createdelete-image)
-* [Other](#other)
-  * [List keypairs](#list-keypairs)
-  * [Create/delete keypair](#createdelete-keypair)
-  * [List IP addresses](#list-ip-addresses)
-
-# Authentication
-
-One of the major differences that this release introduces is the level of
-sub-packaging to differentiate between services and providers. You now have
-the option of authenticating with OpenStack and other providers (like Rackspace).
-
-To authenticate with a vanilla OpenStack installation, you can either specify
-your credentials like this:
-
-```go
-import (
-  "github.com/rackspace/gophercloud"
-  "github.com/rackspace/gophercloud/openstack"
-)
-
-opts := gophercloud.AuthOptions{
-  IdentityEndpoint: "https://my-openstack.com:5000/v2.0",
-  Username: "{username}",
-  Password: "{password}",
-  TenantID: "{tenant_id}",
-}
-```
-
-Or have them pulled in through environment variables, like this:
-
-```go
-opts, err := openstack.AuthOptionsFromEnv()
-```
-
-Once you have your `AuthOptions` struct, you pass it in to get back a `Provider`,
-like so:
-
-```go
-provider, err := openstack.AuthenticatedClient(opts)
-```
-
-This provider is the top-level structure that all services are created from.
-
-# Servers
-
-Before you can interact with the Compute API, you need to retrieve a
-`gophercloud.ServiceClient`. To do this:
-
-```go
-// Define your region, etc.
-opts := gophercloud.EndpointOpts{Region: "RegionOne"}
-
-client, err := openstack.NewComputeV2(provider, opts)
-```
-
-## List servers
-
-All operations that involve API collections (servers, flavors, images) now use
-the `pagination.Pager` interface. This interface represents paginated entities
-that can be iterated over.
-
-Once you have a Pager, you can then pass a callback function into its `EachPage`
-method, and this will allow you to traverse over the collection and execute
-arbitrary functionality. So, an example with list servers:
-
-```go
-import (
-  "fmt"
-  "github.com/rackspace/gophercloud/pagination"
-  "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-)
-
-// We have the option of filtering the server list. If we want the full
-// collection, leave it as an empty struct or nil
-opts := servers.ListOpts{Name: "server_1"}
-
-// Retrieve a pager (i.e. a paginated collection)
-pager := servers.List(client, opts)
-
-// Define an anonymous function to be executed on each page's iteration
-err := pager.EachPage(func(page pagination.Page) (bool, error) {
-  serverList, err := servers.ExtractServers(page)
-
-  // `s' will be a servers.Server struct
-  for _, s := range serverList {
-    fmt.Printf("We have a server. ID=%s, Name=%s", s.ID, s.Name)
-  }
-})
-```
-
-## Get server details
-
-```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-
-// Get the HTTP result
-response := servers.Get(client, "server_id")
-
-// Extract a Server struct from the response
-server, err := response.Extract()
-```
-
-## Create server
-
-```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-
-// Define our options
-opts := servers.CreateOpts{
-  Name: "new_server",
-  FlavorRef: "flavorID",
-  ImageRef: "imageID",
-}
-
-// Get our response
-response := servers.Create(client, opts)
-
-// Extract
-server, err := response.Extract()
-```
-
-## Change admin password
-
-```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-
-result := servers.ChangeAdminPassword(client, "server_id", "newPassword_&123")
-```
-
-## Resize server
-
-```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-
-result := servers.Resize(client, "server_id", "new_flavor_id")
-```
-
-## Reboot server
-
-```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-
-// You have a choice of two reboot methods: servers.SoftReboot or servers.HardReboot
-result := servers.Reboot(client, "server_id", servers.SoftReboot)
-```
-
-## Update server
-
-```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-
-opts := servers.UpdateOpts{Name: "new_name"}
-
-server, err := servers.Update(client, "server_id", opts).Extract()
-```
-
-## Rebuild server
-
-```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-
-// You have the option of specifying additional options
-opts := RebuildOpts{
-  Name:      "new_name",
-  AdminPass: "admin_password",
-  ImageID:   "image_id",
-  Metadata:  map[string]string{"owner": "me"},
-}
-
-result := servers.Rebuild(client, "server_id", opts)
-
-// You can extract a servers.Server struct from the HTTP response
-server, err := result.Extract()
-```
-
-## Delete server
-
-```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-
-response := servers.Delete(client, "server_id")
-```
-
-## Rescue server
-
-The server rescue extension for Compute is not currently supported.
-
-# Images and flavors
-
-## List images
-
-As with listing servers (see above), you first retrieve a Pager, and then pass
-in a callback over each page:
-
-```go
-import (
-  "github.com/rackspace/gophercloud/pagination"
-  "github.com/rackspace/gophercloud/openstack/compute/v2/images"
-)
-
-// We have the option of filtering the image list. If we want the full
-// collection, leave it as an empty struct
-opts := images.ListOpts{ChangesSince: "2014-01-01T01:02:03Z", Name: "Ubuntu 12.04"}
-
-// Retrieve a pager (i.e. a paginated collection)
-pager := images.List(client, opts)
-
-// Define an anonymous function to be executed on each page's iteration
-err := pager.EachPage(func(page pagination.Page) (bool, error) {
-  imageList, err := images.ExtractImages(page)
-
-  for _, i := range imageList {
-    // "i" will be an images.Image
-  }
-})
-```
-
-## List flavors
-
-```go
-import (
-  "github.com/rackspace/gophercloud/pagination"
-  "github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
-)
-
-// We have the option of filtering the flavor list. If we want the full
-// collection, leave it as an empty struct
-opts := flavors.ListOpts{ChangesSince: "2014-01-01T01:02:03Z", MinRAM: 4}
-
-// Retrieve a pager (i.e. a paginated collection)
-pager := flavors.List(client, opts)
-
-// Define an anonymous function to be executed on each page's iteration
-err := pager.EachPage(func(page pagination.Page) (bool, error) {
-  flavorList, err := networks.ExtractFlavors(page)
-
-  for _, f := range flavorList {
-    // "f" will be a flavors.Flavor
-  }
-})
-```
-
-## Create/delete image
-
-Image management has been shifted to Glance, but unfortunately this service is
-not supported as of yet. You can, however, list Compute images like so:
-
-```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/images"
-
-// Retrieve a pager (i.e. a paginated collection)
-pager := images.List(client, opts)
-
-// Define an anonymous function to be executed on each page's iteration
-err := pager.EachPage(func(page pagination.Page) (bool, error) {
-  imageList, err := images.ExtractImages(page)
-
-  for _, i := range imageList {
-    // "i" will be an images.Image
-  }
-})
-```
-
-# Other
-
-## List keypairs
-
-```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
-
-// Retrieve a pager (i.e. a paginated collection)
-pager := keypairs.List(client, opts)
-
-// Define an anonymous function to be executed on each page's iteration
-err := pager.EachPage(func(page pagination.Page) (bool, error) {
-  keyList, err := keypairs.ExtractKeyPairs(page)
-
-  for _, k := range keyList {
-    // "k" will be a keypairs.KeyPair
-  }
-})
-```
-
-## Create/delete keypairs
-
-To create a new keypair, you need to specify its name and, optionally, a
-pregenerated OpenSSH-formatted public key.
-
-```go
-import "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
-
-opts := keypairs.CreateOpts{
-  Name: "new_key",
-  PublicKey: "...",
-}
-
-response := keypairs.Create(client, opts)
-
-key, err := response.Extract()
-```
-
-To delete an existing keypair:
-
-```go
-response := keypairs.Delete(client, "keypair_id")
-```
-
-## List IP addresses
-
-This operation is not currently supported.
diff --git a/acceptance/openstack/blockstorage/v1/pkg.go b/acceptance/openstack/blockstorage/v1/pkg.go
index 16d2567..4efa6fb 100644
--- a/acceptance/openstack/blockstorage/v1/pkg.go
+++ b/acceptance/openstack/blockstorage/v1/pkg.go
@@ -1,3 +1,2 @@
-// The v1 package contains acceptance tests for the Openstack Cinder V1 service.
-
+// Package v1 contains openstack cinder acceptance tests
 package v1
diff --git a/acceptance/openstack/blockstorage/v1/snapshots_test.go b/acceptance/openstack/blockstorage/v1/snapshots_test.go
index 7741aa9..2b737e7 100644
--- a/acceptance/openstack/blockstorage/v1/snapshots_test.go
+++ b/acceptance/openstack/blockstorage/v1/snapshots_test.go
@@ -5,10 +5,10 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots"
-	"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestSnapshots(t *testing.T) {
diff --git a/acceptance/openstack/blockstorage/v1/volumes_test.go b/acceptance/openstack/blockstorage/v1/volumes_test.go
index 7760427..a7bf123 100644
--- a/acceptance/openstack/blockstorage/v1/volumes_test.go
+++ b/acceptance/openstack/blockstorage/v1/volumes_test.go
@@ -6,11 +6,11 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack"
-	"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func newClient(t *testing.T) (*gophercloud.ServiceClient, error) {
diff --git a/acceptance/openstack/blockstorage/v1/volumetypes_test.go b/acceptance/openstack/blockstorage/v1/volumetypes_test.go
index 000bc01..ebfa3de 100644
--- a/acceptance/openstack/blockstorage/v1/volumetypes_test.go
+++ b/acceptance/openstack/blockstorage/v1/volumetypes_test.go
@@ -6,9 +6,9 @@
 	"testing"
 	"time"
 
-	"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumetypes"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestVolumeTypes(t *testing.T) {
diff --git a/acceptance/openstack/blockstorage/v2/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/v2/extensions/volumeactions_test.go
index e01967b..20a4597 100644
--- a/acceptance/openstack/blockstorage/v2/extensions/volumeactions_test.go
+++ b/acceptance/openstack/blockstorage/v2/extensions/volumeactions_test.go
@@ -6,11 +6,11 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack"
-	"github.com/rackspace/gophercloud/openstack/blockstorage/v2/extensions/volumeactions"
-	"github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/extensions/volumeactions"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func newClient(t *testing.T) (*gophercloud.ServiceClient, error) {
diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/acceptance/openstack/blockstorage/v2/volumes_test.go
index 9b82654..9edf31a 100644
--- a/acceptance/openstack/blockstorage/v2/volumes_test.go
+++ b/acceptance/openstack/blockstorage/v2/volumes_test.go
@@ -6,11 +6,11 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack"
-	"github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func newClient(t *testing.T) (*gophercloud.ServiceClient, error) {
diff --git a/acceptance/openstack/client_test.go b/acceptance/openstack/client_test.go
index 6e88819..2758f60 100644
--- a/acceptance/openstack/client_test.go
+++ b/acceptance/openstack/client_test.go
@@ -6,8 +6,8 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
 )
 
 func TestAuthenticatedClient(t *testing.T) {
diff --git a/acceptance/openstack/compute/v2/bootfromvolume_test.go b/acceptance/openstack/compute/v2/bootfromvolume_test.go
index bbecfad..d1607be 100644
--- a/acceptance/openstack/compute/v2/bootfromvolume_test.go
+++ b/acceptance/openstack/compute/v2/bootfromvolume_test.go
@@ -5,10 +5,10 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestBootFromVolume(t *testing.T) {
diff --git a/acceptance/openstack/compute/v2/compute_test.go b/acceptance/openstack/compute/v2/compute_test.go
index c1bbf79..83b0e35 100644
--- a/acceptance/openstack/compute/v2/compute_test.go
+++ b/acceptance/openstack/compute/v2/compute_test.go
@@ -7,10 +7,10 @@
 	"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/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 )
 
 func newClient() (*gophercloud.ServiceClient, error) {
diff --git a/acceptance/openstack/compute/v2/extension_test.go b/acceptance/openstack/compute/v2/extension_test.go
index 1356ffa..0fd6fec 100644
--- a/acceptance/openstack/compute/v2/extension_test.go
+++ b/acceptance/openstack/compute/v2/extension_test.go
@@ -5,9 +5,9 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestListExtensions(t *testing.T) {
diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go
index 9f51b12..365281d 100644
--- a/acceptance/openstack/compute/v2/flavors_test.go
+++ b/acceptance/openstack/compute/v2/flavors_test.go
@@ -5,8 +5,8 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 func TestListFlavors(t *testing.T) {
diff --git a/acceptance/openstack/compute/v2/floatingip_test.go b/acceptance/openstack/compute/v2/floatingip_test.go
index de6efc9..8231bd6 100644
--- a/acceptance/openstack/compute/v2/floatingip_test.go
+++ b/acceptance/openstack/compute/v2/floatingip_test.go
@@ -6,11 +6,11 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingip"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func createFIPServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
diff --git a/acceptance/openstack/compute/v2/images_test.go b/acceptance/openstack/compute/v2/images_test.go
index ceab22f..9b740ad 100644
--- a/acceptance/openstack/compute/v2/images_test.go
+++ b/acceptance/openstack/compute/v2/images_test.go
@@ -5,8 +5,8 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud/openstack/compute/v2/images"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 func TestListImages(t *testing.T) {
diff --git a/acceptance/openstack/compute/v2/keypairs_test.go b/acceptance/openstack/compute/v2/keypairs_test.go
index a4fe8db..326c6f9 100644
--- a/acceptance/openstack/compute/v2/keypairs_test.go
+++ b/acceptance/openstack/compute/v2/keypairs_test.go
@@ -7,10 +7,10 @@
 	"crypto/rsa"
 	"testing"
 
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
 
 	"golang.org/x/crypto/ssh"
 )
diff --git a/acceptance/openstack/compute/v2/network_test.go b/acceptance/openstack/compute/v2/network_test.go
index 7ebe7ec..615e391 100644
--- a/acceptance/openstack/compute/v2/network_test.go
+++ b/acceptance/openstack/compute/v2/network_test.go
@@ -6,10 +6,10 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func getNetworkIDFromNetworkExtension(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
diff --git a/acceptance/openstack/compute/v2/pkg.go b/acceptance/openstack/compute/v2/pkg.go
index bb158c3..a57c1e7 100644
--- a/acceptance/openstack/compute/v2/pkg.go
+++ b/acceptance/openstack/compute/v2/pkg.go
@@ -1,3 +1,2 @@
-// The v2 package contains acceptance tests for the Openstack Compute V2 service.
-
+// Package v2 package contains acceptance tests for the Openstack Compute V2 service.
 package v2
diff --git a/acceptance/openstack/compute/v2/quotaset_test.go b/acceptance/openstack/compute/v2/quotaset_test.go
index 3851edf..d7883df 100644
--- a/acceptance/openstack/compute/v2/quotaset_test.go
+++ b/acceptance/openstack/compute/v2/quotaset_test.go
@@ -5,12 +5,12 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets"
-	"github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets"
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestGetQuotaset(t *testing.T) {
diff --git a/acceptance/openstack/compute/v2/secdefrules_test.go b/acceptance/openstack/compute/v2/secdefrules_test.go
index 78b0798..15809e2 100644
--- a/acceptance/openstack/compute/v2/secdefrules_test.go
+++ b/acceptance/openstack/compute/v2/secdefrules_test.go
@@ -5,11 +5,11 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	dsr "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestSecDefRules(t *testing.T) {
diff --git a/acceptance/openstack/compute/v2/secgroup_test.go b/acceptance/openstack/compute/v2/secgroup_test.go
index 6ec3149..da06c35 100644
--- a/acceptance/openstack/compute/v2/secgroup_test.go
+++ b/acceptance/openstack/compute/v2/secgroup_test.go
@@ -5,12 +5,12 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestSecGroups(t *testing.T) {
diff --git a/acceptance/openstack/compute/v2/servergroup_test.go b/acceptance/openstack/compute/v2/servergroup_test.go
index 945854e..79f7c92 100644
--- a/acceptance/openstack/compute/v2/servergroup_test.go
+++ b/acceptance/openstack/compute/v2/servergroup_test.go
@@ -6,12 +6,12 @@
 	"fmt"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func createServerGroup(t *testing.T, computeClient *gophercloud.ServiceClient) (*servergroups.ServerGroup, error) {
diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go
index f6c7c05..4c4fb42 100644
--- a/acceptance/openstack/compute/v2/servers_test.go
+++ b/acceptance/openstack/compute/v2/servers_test.go
@@ -6,13 +6,13 @@
 	"os"
 	"testing"
 
-	"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/networking/v2/networks"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestListServers(t *testing.T) {
diff --git a/acceptance/openstack/compute/v2/tenantnetworks_test.go b/acceptance/openstack/compute/v2/tenantnetworks_test.go
index a92e8bf..58208c0 100644
--- a/acceptance/openstack/compute/v2/tenantnetworks_test.go
+++ b/acceptance/openstack/compute/v2/tenantnetworks_test.go
@@ -6,11 +6,11 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func getNetworkID(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
diff --git a/acceptance/openstack/compute/v2/volumeattach_test.go b/acceptance/openstack/compute/v2/volumeattach_test.go
index 34634c9..459d283 100644
--- a/acceptance/openstack/compute/v2/volumeattach_test.go
+++ b/acceptance/openstack/compute/v2/volumeattach_test.go
@@ -6,13 +6,13 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack"
-	"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func newBlockClient(t *testing.T) (*gophercloud.ServiceClient, error) {
diff --git a/acceptance/openstack/db/v1/common.go b/acceptance/openstack/db/v1/common.go
index f7ffc37..bbe7ebd 100644
--- a/acceptance/openstack/db/v1/common.go
+++ b/acceptance/openstack/db/v1/common.go
@@ -6,10 +6,10 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack"
-	"github.com/rackspace/gophercloud/openstack/db/v1/instances"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/instances"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func newClient(t *testing.T) *gophercloud.ServiceClient {
diff --git a/acceptance/openstack/db/v1/database_test.go b/acceptance/openstack/db/v1/database_test.go
index 2fd3175..c52357a 100644
--- a/acceptance/openstack/db/v1/database_test.go
+++ b/acceptance/openstack/db/v1/database_test.go
@@ -3,8 +3,8 @@
 package v1
 
 import (
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	"github.com/rackspace/gophercloud/pagination"
+	db "github.com/gophercloud/gophercloud/openstack/db/v1/databases"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 func (c context) createDBs() {
diff --git a/acceptance/openstack/db/v1/flavor_test.go b/acceptance/openstack/db/v1/flavor_test.go
index 46f986c..6440cc9 100644
--- a/acceptance/openstack/db/v1/flavor_test.go
+++ b/acceptance/openstack/db/v1/flavor_test.go
@@ -3,8 +3,8 @@
 package v1
 
 import (
-	"github.com/rackspace/gophercloud/openstack/db/v1/flavors"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/flavors"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 func (c context) listFlavors() {
diff --git a/acceptance/openstack/db/v1/instance_test.go b/acceptance/openstack/db/v1/instance_test.go
index dfded21..75668a2 100644
--- a/acceptance/openstack/db/v1/instance_test.go
+++ b/acceptance/openstack/db/v1/instance_test.go
@@ -6,10 +6,10 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack/db/v1/instances"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/instances"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 const envDSType = "DATASTORE_TYPE_ID"
diff --git a/acceptance/openstack/db/v1/user_test.go b/acceptance/openstack/db/v1/user_test.go
index 25a4794..0f5fcc2 100644
--- a/acceptance/openstack/db/v1/user_test.go
+++ b/acceptance/openstack/db/v1/user_test.go
@@ -3,10 +3,10 @@
 package v1
 
 import (
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	u "github.com/rackspace/gophercloud/openstack/db/v1/users"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	db "github.com/gophercloud/gophercloud/openstack/db/v1/databases"
+	u "github.com/gophercloud/gophercloud/openstack/db/v1/users"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 func (c context) createUsers() {
diff --git a/acceptance/openstack/identity/v2/extension_test.go b/acceptance/openstack/identity/v2/extension_test.go
index d1fa1e3..23627dc 100644
--- a/acceptance/openstack/identity/v2/extension_test.go
+++ b/acceptance/openstack/identity/v2/extension_test.go
@@ -5,9 +5,9 @@
 import (
 	"testing"
 
-	extensions2 "github.com/rackspace/gophercloud/openstack/identity/v2/extensions"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	extensions2 "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestEnumerateExtensions(t *testing.T) {
diff --git a/acceptance/openstack/identity/v2/identity_test.go b/acceptance/openstack/identity/v2/identity_test.go
index 96bf1fd..e0e2c0e 100644
--- a/acceptance/openstack/identity/v2/identity_test.go
+++ b/acceptance/openstack/identity/v2/identity_test.go
@@ -5,9 +5,9 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func v2AuthOptions(t *testing.T) gophercloud.AuthOptions {
diff --git a/acceptance/openstack/identity/v2/role_test.go b/acceptance/openstack/identity/v2/role_test.go
index ba243fe..3f47858 100644
--- a/acceptance/openstack/identity/v2/role_test.go
+++ b/acceptance/openstack/identity/v2/role_test.go
@@ -5,10 +5,10 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/extensions/admin/roles"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestRoles(t *testing.T) {
diff --git a/acceptance/openstack/identity/v2/tenant_test.go b/acceptance/openstack/identity/v2/tenant_test.go
index 578fc48..5f7440d 100644
--- a/acceptance/openstack/identity/v2/tenant_test.go
+++ b/acceptance/openstack/identity/v2/tenant_test.go
@@ -5,9 +5,9 @@
 import (
 	"testing"
 
-	tenants2 "github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	tenants2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestEnumerateTenants(t *testing.T) {
diff --git a/acceptance/openstack/identity/v2/token_test.go b/acceptance/openstack/identity/v2/token_test.go
index e01b3b3..c2f7e51 100644
--- a/acceptance/openstack/identity/v2/token_test.go
+++ b/acceptance/openstack/identity/v2/token_test.go
@@ -5,8 +5,8 @@
 import (
 	"testing"
 
-	tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
-	th "github.com/rackspace/gophercloud/testhelper"
+	tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestAuthenticateAndValidate(t *testing.T) {
diff --git a/acceptance/openstack/identity/v2/user_test.go b/acceptance/openstack/identity/v2/user_test.go
index fe73d19..7938b37 100644
--- a/acceptance/openstack/identity/v2/user_test.go
+++ b/acceptance/openstack/identity/v2/user_test.go
@@ -6,12 +6,12 @@
 	"strconv"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
-	"github.com/rackspace/gophercloud/openstack/identity/v2/users"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/users"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestUsers(t *testing.T) {
diff --git a/acceptance/openstack/identity/v3/endpoint_test.go b/acceptance/openstack/identity/v3/endpoint_test.go
index ea893c2..f575649 100644
--- a/acceptance/openstack/identity/v3/endpoint_test.go
+++ b/acceptance/openstack/identity/v3/endpoint_test.go
@@ -5,10 +5,10 @@
 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"
+	"github.com/gophercloud/gophercloud"
+	endpoints3 "github.com/gophercloud/gophercloud/openstack/identity/v3/endpoints"
+	services3 "github.com/gophercloud/gophercloud/openstack/identity/v3/services"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 func TestListEndpoints(t *testing.T) {
diff --git a/acceptance/openstack/identity/v3/identity_test.go b/acceptance/openstack/identity/v3/identity_test.go
index ce64345..6974ad0 100644
--- a/acceptance/openstack/identity/v3/identity_test.go
+++ b/acceptance/openstack/identity/v3/identity_test.go
@@ -5,9 +5,9 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func createAuthenticatedClient(t *testing.T) *gophercloud.ServiceClient {
diff --git a/acceptance/openstack/identity/v3/service_test.go b/acceptance/openstack/identity/v3/service_test.go
index 082bd11..b39ba7f 100644
--- a/acceptance/openstack/identity/v3/service_test.go
+++ b/acceptance/openstack/identity/v3/service_test.go
@@ -5,8 +5,8 @@
 import (
 	"testing"
 
-	services3 "github.com/rackspace/gophercloud/openstack/identity/v3/services"
-	"github.com/rackspace/gophercloud/pagination"
+	services3 "github.com/gophercloud/gophercloud/openstack/identity/v3/services"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 func TestListServices(t *testing.T) {
diff --git a/acceptance/openstack/identity/v3/token_test.go b/acceptance/openstack/identity/v3/token_test.go
index 4342ade..5340ead 100644
--- a/acceptance/openstack/identity/v3/token_test.go
+++ b/acceptance/openstack/identity/v3/token_test.go
@@ -5,8 +5,8 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud/openstack"
-	tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
+	"github.com/gophercloud/gophercloud/openstack"
+	tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
 )
 
 func TestGetToken(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/apiversion_test.go b/acceptance/openstack/networking/v2/apiversion_test.go
index 99e1d01..22827d6 100644
--- a/acceptance/openstack/networking/v2/apiversion_test.go
+++ b/acceptance/openstack/networking/v2/apiversion_test.go
@@ -5,9 +5,9 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud/openstack/networking/v2/apiversions"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/apiversions"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestListAPIVersions(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/common.go b/acceptance/openstack/networking/v2/common.go
index 1efac2c..b48855b 100644
--- a/acceptance/openstack/networking/v2/common.go
+++ b/acceptance/openstack/networking/v2/common.go
@@ -4,9 +4,9 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 var Client *gophercloud.ServiceClient
diff --git a/acceptance/openstack/networking/v2/extension_test.go b/acceptance/openstack/networking/v2/extension_test.go
index edcbba4..e125034 100644
--- a/acceptance/openstack/networking/v2/extension_test.go
+++ b/acceptance/openstack/networking/v2/extension_test.go
@@ -5,9 +5,9 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestListExts(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go
index 80246b6..ef1fb1a 100644
--- a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go
+++ b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go
@@ -6,12 +6,12 @@
 	"testing"
 	"time"
 
-	"github.com/rackspace/gophercloud"
-	base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func firewallSetup(t *testing.T) string {
diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go
index fdca22e..84bae52 100644
--- a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go
+++ b/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go
@@ -5,11 +5,11 @@
 import (
 	"testing"
 
-	base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func firewallPolicySetup(t *testing.T) string {
diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go
index 144aa09..aa11ec6 100644
--- a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go
+++ b/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go
@@ -5,10 +5,10 @@
 import (
 	"testing"
 
-	base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestFirewallRules(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/extensions/layer3_test.go b/acceptance/openstack/networking/v2/extensions/layer3_test.go
index 63e0be3..7d9dba3 100644
--- a/acceptance/openstack/networking/v2/extensions/layer3_test.go
+++ b/acceptance/openstack/networking/v2/extensions/layer3_test.go
@@ -5,15 +5,15 @@
 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"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 const (
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/common.go b/acceptance/openstack/networking/v2/extensions/lbaas/common.go
index 02c71bd..760ee5b 100644
--- a/acceptance/openstack/networking/v2/extensions/lbaas/common.go
+++ b/acceptance/openstack/networking/v2/extensions/lbaas/common.go
@@ -3,12 +3,13 @@
 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"
+	"github.com/gophercloud/gophercloud"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func SetupTopology(t *testing.T) (string, string) {
@@ -22,7 +23,7 @@
 	s, err := subnets.Create(base.Client, subnets.CreateOpts{
 		NetworkID: n.ID,
 		CIDR:      "192.168.199.0/24",
-		IPVersion: subnets.IPv4,
+		IPVersion: gophercloud.IPv4,
 		Name:      "tmp_subnet",
 	}).Extract()
 	th.AssertNoErr(t, err)
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go
index 9b60582..dce3bbb 100644
--- a/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go
+++ b/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go
@@ -5,10 +5,10 @@
 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"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestMembers(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go
index 9056fff..e8e7192 100644
--- a/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go
+++ b/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go
@@ -5,10 +5,10 @@
 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"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestMonitors(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go
index 8efb7d0..6151217 100644
--- a/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go
+++ b/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go
@@ -5,10 +5,10 @@
 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"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestPools(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go
index c8dff2d..d38e9c1 100644
--- a/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go
+++ b/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go
@@ -5,10 +5,10 @@
 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"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestVIPs(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancer_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancer_test.go
index 7cc84ab..051b7eb 100644
--- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancer_test.go
+++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancer_test.go
@@ -6,16 +6,16 @@
 	"testing"
 	"time"
 
-	"github.com/rackspace/gophercloud"
-	base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
-	"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"
+	"github.com/gophercloud/gophercloud"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 // Note: when creating a new Loadbalancer (VM), it can take some time before it is ready for use,
diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go
index 5a79945..b703e3b 100644
--- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go
+++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go
@@ -5,13 +5,13 @@
 import (
 	"testing"
 
-	base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/portsbinding"
-	"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"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestPortBinding(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/extensions/provider_test.go b/acceptance/openstack/networking/v2/extensions/provider_test.go
index f10c9d9..55acbc9 100644
--- a/acceptance/openstack/networking/v2/extensions/provider_test.go
+++ b/acceptance/openstack/networking/v2/extensions/provider_test.go
@@ -6,10 +6,10 @@
 	"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"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestNetworkCRUDOperations(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/extensions/security_test.go b/acceptance/openstack/networking/v2/extensions/security_test.go
index 7d75292..fe02ada 100644
--- a/acceptance/openstack/networking/v2/extensions/security_test.go
+++ b/acceptance/openstack/networking/v2/extensions/security_test.go
@@ -5,13 +5,13 @@
 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"
+	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestSecurityGroups(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/network_test.go b/acceptance/openstack/networking/v2/network_test.go
index be8a3a1..1926999 100644
--- a/acceptance/openstack/networking/v2/network_test.go
+++ b/acceptance/openstack/networking/v2/network_test.go
@@ -6,9 +6,9 @@
 	"strconv"
 	"testing"
 
-	"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestNetworkCRUDOperations(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/port_test.go b/acceptance/openstack/networking/v2/port_test.go
index 91bf5bd..2ef3408 100644
--- a/acceptance/openstack/networking/v2/port_test.go
+++ b/acceptance/openstack/networking/v2/port_test.go
@@ -5,11 +5,11 @@
 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"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestPortCRUD(t *testing.T) {
diff --git a/acceptance/openstack/networking/v2/subnet_test.go b/acceptance/openstack/networking/v2/subnet_test.go
index e6a789e..c9efd5c 100644
--- a/acceptance/openstack/networking/v2/subnet_test.go
+++ b/acceptance/openstack/networking/v2/subnet_test.go
@@ -5,10 +5,10 @@
 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"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestSubnetList(t *testing.T) {
diff --git a/acceptance/openstack/objectstorage/v1/accounts_test.go b/acceptance/openstack/objectstorage/v1/accounts_test.go
index 24cc62b..5a29235 100644
--- a/acceptance/openstack/objectstorage/v1/accounts_test.go
+++ b/acceptance/openstack/objectstorage/v1/accounts_test.go
@@ -6,8 +6,8 @@
 	"strings"
 	"testing"
 
-	"github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestAccounts(t *testing.T) {
diff --git a/acceptance/openstack/objectstorage/v1/common.go b/acceptance/openstack/objectstorage/v1/common.go
index 1eac681..1114ed5 100644
--- a/acceptance/openstack/objectstorage/v1/common.go
+++ b/acceptance/openstack/objectstorage/v1/common.go
@@ -6,9 +6,9 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 var metadata = map[string]string{"gopher": "cloud"}
diff --git a/acceptance/openstack/objectstorage/v1/containers_test.go b/acceptance/openstack/objectstorage/v1/containers_test.go
index 8328a4f..056b2a9 100644
--- a/acceptance/openstack/objectstorage/v1/containers_test.go
+++ b/acceptance/openstack/objectstorage/v1/containers_test.go
@@ -6,10 +6,10 @@
 	"strings"
 	"testing"
 
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 // numContainers is the number of containers to create for testing.
diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go
index a8de338..3a27738 100644
--- a/acceptance/openstack/objectstorage/v1/objects_test.go
+++ b/acceptance/openstack/objectstorage/v1/objects_test.go
@@ -7,11 +7,11 @@
 	"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"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers"
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 // numObjects is the number of objects to create for testing.
diff --git a/acceptance/rackspace/db/v1/pkg.go b/acceptance/openstack/objectstorage/v1/pkg.go
similarity index 100%
rename from acceptance/rackspace/db/v1/pkg.go
rename to acceptance/openstack/objectstorage/v1/pkg.go
diff --git a/acceptance/openstack/orchestration/v1/buildinfo_test.go b/acceptance/openstack/orchestration/v1/buildinfo_test.go
index 05a5e1d..1b48662 100644
--- a/acceptance/openstack/orchestration/v1/buildinfo_test.go
+++ b/acceptance/openstack/orchestration/v1/buildinfo_test.go
@@ -5,8 +5,8 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud/openstack/orchestration/v1/buildinfo"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/buildinfo"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestBuildInfo(t *testing.T) {
diff --git a/acceptance/openstack/orchestration/v1/common.go b/acceptance/openstack/orchestration/v1/common.go
index 2c28dcb..4eec2e3 100644
--- a/acceptance/openstack/orchestration/v1/common.go
+++ b/acceptance/openstack/orchestration/v1/common.go
@@ -7,9 +7,9 @@
 	"os"
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 var template = fmt.Sprintf(`
diff --git a/acceptance/rackspace/db/v1/pkg.go b/acceptance/openstack/orchestration/v1/pkg.go
similarity index 100%
copy from acceptance/rackspace/db/v1/pkg.go
copy to acceptance/openstack/orchestration/v1/pkg.go
diff --git a/acceptance/openstack/orchestration/v1/stackevents_test.go b/acceptance/openstack/orchestration/v1/stackevents_test.go
index e356c86..4be4bf6 100644
--- a/acceptance/openstack/orchestration/v1/stackevents_test.go
+++ b/acceptance/openstack/orchestration/v1/stackevents_test.go
@@ -5,11 +5,11 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents"
-	"github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestStackEvents(t *testing.T) {
diff --git a/acceptance/openstack/orchestration/v1/stackresources_test.go b/acceptance/openstack/orchestration/v1/stackresources_test.go
index b614f1c..50a0f06 100644
--- a/acceptance/openstack/orchestration/v1/stackresources_test.go
+++ b/acceptance/openstack/orchestration/v1/stackresources_test.go
@@ -5,11 +5,11 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources"
-	"github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestStackResources(t *testing.T) {
diff --git a/acceptance/openstack/orchestration/v1/stacks_test.go b/acceptance/openstack/orchestration/v1/stacks_test.go
index db31cd4..c87cc5d 100644
--- a/acceptance/openstack/orchestration/v1/stacks_test.go
+++ b/acceptance/openstack/orchestration/v1/stacks_test.go
@@ -5,10 +5,10 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestStacks(t *testing.T) {
diff --git a/acceptance/openstack/orchestration/v1/stacktemplates_test.go b/acceptance/openstack/orchestration/v1/stacktemplates_test.go
index 22d5e88..9992e0c 100644
--- a/acceptance/openstack/orchestration/v1/stacktemplates_test.go
+++ b/acceptance/openstack/orchestration/v1/stacktemplates_test.go
@@ -5,10 +5,10 @@
 import (
 	"testing"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
-	"github.com/rackspace/gophercloud/openstack/orchestration/v1/stacktemplates"
-	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacktemplates"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestStackTemplates(t *testing.T) {
diff --git a/acceptance/rackspace/blockstorage/v1/common.go b/acceptance/rackspace/blockstorage/v1/common.go
deleted file mode 100644
index e9fdd99..0000000
--- a/acceptance/rackspace/blockstorage/v1/common.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"os"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/rackspace"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func newClient() (*gophercloud.ServiceClient, error) {
-	opts, err := rackspace.AuthOptionsFromEnv()
-	if err != nil {
-		return nil, err
-	}
-	opts = tools.OnlyRS(opts)
-	region := os.Getenv("RS_REGION")
-
-	provider, err := rackspace.AuthenticatedClient(opts)
-	if err != nil {
-		return nil, err
-	}
-
-	return rackspace.NewBlockStorageV1(provider, gophercloud.EndpointOpts{
-		Region: region,
-	})
-}
-
-func setup(t *testing.T) *gophercloud.ServiceClient {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	return client
-}
diff --git a/acceptance/rackspace/blockstorage/v1/snapshot_test.go b/acceptance/rackspace/blockstorage/v1/snapshot_test.go
deleted file mode 100644
index 25b2cfe..0000000
--- a/acceptance/rackspace/blockstorage/v1/snapshot_test.go
+++ /dev/null
@@ -1,82 +0,0 @@
-// +build acceptance blockstorage snapshots
-
-package v1
-
-import (
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/blockstorage/v1/snapshots"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestSnapshots(t *testing.T) {
-	client := setup(t)
-	volID := testVolumeCreate(t, client)
-
-	t.Log("Creating snapshots")
-	s := testSnapshotCreate(t, client, volID)
-	id := s.ID
-
-	t.Log("Listing snapshots")
-	testSnapshotList(t, client)
-
-	t.Logf("Getting snapshot %s", id)
-	testSnapshotGet(t, client, id)
-
-	t.Logf("Updating snapshot %s", id)
-	testSnapshotUpdate(t, client, id)
-
-	t.Logf("Deleting snapshot %s", id)
-	testSnapshotDelete(t, client, id)
-	s.WaitUntilDeleted(client, -1)
-
-	t.Logf("Deleting volume %s", volID)
-	testVolumeDelete(t, client, volID)
-}
-
-func testSnapshotCreate(t *testing.T, client *gophercloud.ServiceClient, volID string) *snapshots.Snapshot {
-	opts := snapshots.CreateOpts{VolumeID: volID, Name: "snapshot-001"}
-	s, err := snapshots.Create(client, opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created snapshot %s", s.ID)
-
-	t.Logf("Waiting for new snapshot to become available...")
-	start := time.Now().Second()
-	s.WaitUntilComplete(client, -1)
-	t.Logf("Snapshot completed after %ds", time.Now().Second()-start)
-
-	return s
-}
-
-func testSnapshotList(t *testing.T, client *gophercloud.ServiceClient) {
-	snapshots.List(client).EachPage(func(page pagination.Page) (bool, error) {
-		sList, err := snapshots.ExtractSnapshots(page)
-		th.AssertNoErr(t, err)
-
-		for _, s := range sList {
-			t.Logf("Snapshot: ID [%s] Name [%s] Volume ID [%s] Progress [%s] Created [%s]",
-				s.ID, s.Name, s.VolumeID, s.Progress, s.CreatedAt)
-		}
-
-		return true, nil
-	})
-}
-
-func testSnapshotGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	_, err := snapshots.Get(client, id).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func testSnapshotUpdate(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	_, err := snapshots.Update(client, id, snapshots.UpdateOpts{Name: "new_name"}).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func testSnapshotDelete(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	res := snapshots.Delete(client, id)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted snapshot %s", id)
-}
diff --git a/acceptance/rackspace/blockstorage/v1/volume_test.go b/acceptance/rackspace/blockstorage/v1/volume_test.go
deleted file mode 100644
index f86f9ad..0000000
--- a/acceptance/rackspace/blockstorage/v1/volume_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// +build acceptance blockstorage volumes
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/blockstorage/v1/volumes"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestVolumes(t *testing.T) {
-	client := setup(t)
-
-	t.Logf("Listing volumes")
-	testVolumeList(t, client)
-
-	t.Logf("Creating volume")
-	volumeID := testVolumeCreate(t, client)
-
-	t.Logf("Getting volume %s", volumeID)
-	testVolumeGet(t, client, volumeID)
-
-	t.Logf("Updating volume %s", volumeID)
-	testVolumeUpdate(t, client, volumeID)
-
-	t.Logf("Deleting volume %s", volumeID)
-	testVolumeDelete(t, client, volumeID)
-}
-
-func testVolumeList(t *testing.T, client *gophercloud.ServiceClient) {
-	volumes.List(client).EachPage(func(page pagination.Page) (bool, error) {
-		vList, err := volumes.ExtractVolumes(page)
-		th.AssertNoErr(t, err)
-
-		for _, v := range vList {
-			t.Logf("Volume: ID [%s] Name [%s] Type [%s] Created [%s]", v.ID, v.Name,
-				v.VolumeType, v.CreatedAt)
-		}
-
-		return true, nil
-	})
-}
-
-func testVolumeCreate(t *testing.T, client *gophercloud.ServiceClient) string {
-	vol, err := volumes.Create(client, os.CreateOpts{Size: 75}).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created volume: ID [%s] Size [%s]", vol.ID, vol.Size)
-	return vol.ID
-}
-
-func testVolumeGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	vol, err := volumes.Get(client, id).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created volume: ID [%s] Size [%s]", vol.ID, vol.Size)
-}
-
-func testVolumeUpdate(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	vol, err := volumes.Update(client, id, volumes.UpdateOpts{Name: "new_name"}).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created volume: ID [%s] Name [%s]", vol.ID, vol.Name)
-}
-
-func testVolumeDelete(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	res := volumes.Delete(client, id)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted volume %s", id)
-}
diff --git a/acceptance/rackspace/blockstorage/v1/volume_type_test.go b/acceptance/rackspace/blockstorage/v1/volume_type_test.go
deleted file mode 100644
index 716f2b9..0000000
--- a/acceptance/rackspace/blockstorage/v1/volume_type_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// +build acceptance blockstorage volumetypes
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/blockstorage/v1/volumetypes"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestAll(t *testing.T) {
-	client := setup(t)
-
-	t.Logf("Listing volume types")
-	id := testList(t, client)
-
-	t.Logf("Getting volume type %s", id)
-	testGet(t, client, id)
-}
-
-func testList(t *testing.T, client *gophercloud.ServiceClient) string {
-	var lastID string
-
-	volumetypes.List(client).EachPage(func(page pagination.Page) (bool, error) {
-		typeList, err := volumetypes.ExtractVolumeTypes(page)
-		th.AssertNoErr(t, err)
-
-		for _, vt := range typeList {
-			t.Logf("Volume type: ID [%s] Name [%s]", vt.ID, vt.Name)
-			lastID = vt.ID
-		}
-
-		return true, nil
-	})
-
-	return lastID
-}
-
-func testGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	vt, err := volumetypes.Get(client, id).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Volume: ID [%s] Name [%s]", vt.ID, vt.Name)
-}
diff --git a/acceptance/rackspace/cdn/v1/base_test.go b/acceptance/rackspace/cdn/v1/base_test.go
deleted file mode 100644
index 135f5b3..0000000
--- a/acceptance/rackspace/cdn/v1/base_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace/cdn/v1/base"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestBaseOps(t *testing.T) {
-	client := newClient(t)
-	t.Log("Retrieving Home Document")
-	testHomeDocumentGet(t, client)
-
-	t.Log("Pinging root URL")
-	testPing(t, client)
-}
-
-func testHomeDocumentGet(t *testing.T, client *gophercloud.ServiceClient) {
-	hd, err := base.Get(client).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Retrieved home document: %+v", *hd)
-}
-
-func testPing(t *testing.T, client *gophercloud.ServiceClient) {
-	err := base.Ping(client).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Successfully pinged root URL")
-}
diff --git a/acceptance/rackspace/cdn/v1/common.go b/acceptance/rackspace/cdn/v1/common.go
deleted file mode 100644
index 2333ca7..0000000
--- a/acceptance/rackspace/cdn/v1/common.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func newClient(t *testing.T) *gophercloud.ServiceClient {
-	ao, err := rackspace.AuthOptionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	client, err := rackspace.AuthenticatedClient(ao)
-	th.AssertNoErr(t, err)
-
-	c, err := rackspace.NewCDNV1(client, gophercloud.EndpointOpts{})
-	th.AssertNoErr(t, err)
-	return c
-}
diff --git a/acceptance/rackspace/cdn/v1/flavor_test.go b/acceptance/rackspace/cdn/v1/flavor_test.go
deleted file mode 100644
index f26cff0..0000000
--- a/acceptance/rackspace/cdn/v1/flavor_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/cdn/v1/flavors"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/cdn/v1/flavors"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestFlavor(t *testing.T) {
-	client := newClient(t)
-
-	t.Log("Listing Flavors")
-	id := testFlavorsList(t, client)
-
-	t.Log("Retrieving Flavor")
-	testFlavorGet(t, client, id)
-}
-
-func testFlavorsList(t *testing.T, client *gophercloud.ServiceClient) string {
-	var id string
-	err := flavors.List(client).EachPage(func(page pagination.Page) (bool, error) {
-		flavorList, err := os.ExtractFlavors(page)
-		th.AssertNoErr(t, err)
-
-		for _, flavor := range flavorList {
-			t.Logf("Listing flavor: ID [%s] Providers [%+v]", flavor.ID, flavor.Providers)
-			id = flavor.ID
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	return id
-}
-
-func testFlavorGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	flavor, err := flavors.Get(client, id).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Retrieved Flavor: %+v", *flavor)
-}
diff --git a/acceptance/rackspace/cdn/v1/service_test.go b/acceptance/rackspace/cdn/v1/service_test.go
deleted file mode 100644
index c19c241..0000000
--- a/acceptance/rackspace/cdn/v1/service_test.go
+++ /dev/null
@@ -1,93 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/cdn/v1/services"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/cdn/v1/services"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestService(t *testing.T) {
-	client := newClient(t)
-
-	t.Log("Creating Service")
-	loc := testServiceCreate(t, client, "test-site-1")
-	t.Logf("Created service at location: %s", loc)
-
-	defer testServiceDelete(t, client, loc)
-
-	t.Log("Updating Service")
-	testServiceUpdate(t, client, loc)
-
-	t.Log("Retrieving Service")
-	testServiceGet(t, client, loc)
-
-	t.Log("Listing Services")
-	testServiceList(t, client)
-}
-
-func testServiceCreate(t *testing.T, client *gophercloud.ServiceClient, name string) string {
-	createOpts := os.CreateOpts{
-		Name: name,
-		Domains: []os.Domain{
-			os.Domain{
-				Domain: "www." + name + ".com",
-			},
-		},
-		Origins: []os.Origin{
-			os.Origin{
-				Origin: name + ".com",
-				Port:   80,
-				SSL:    false,
-			},
-		},
-		FlavorID: "cdn",
-	}
-	l, err := services.Create(client, createOpts).Extract()
-	th.AssertNoErr(t, err)
-	return l
-}
-
-func testServiceGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	s, err := services.Get(client, id).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Retrieved service: %+v", *s)
-}
-
-func testServiceUpdate(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	opts := os.UpdateOpts{
-		os.Append{
-			Value: os.Domain{Domain: "newDomain.com", Protocol: "http"},
-		},
-	}
-
-	loc, err := services.Update(client, id, opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Successfully updated service at location: %s", loc)
-}
-
-func testServiceList(t *testing.T, client *gophercloud.ServiceClient) {
-	err := services.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
-		serviceList, err := os.ExtractServices(page)
-		th.AssertNoErr(t, err)
-
-		for _, service := range serviceList {
-			t.Logf("Listing service: %+v", service)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func testServiceDelete(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	err := services.Delete(client, id).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Successfully deleted service (%s)", id)
-}
diff --git a/acceptance/rackspace/cdn/v1/serviceasset_test.go b/acceptance/rackspace/cdn/v1/serviceasset_test.go
deleted file mode 100644
index c32bf25..0000000
--- a/acceptance/rackspace/cdn/v1/serviceasset_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	osServiceAssets "github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets"
-	"github.com/rackspace/gophercloud/rackspace/cdn/v1/serviceassets"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestServiceAsset(t *testing.T) {
-	client := newClient(t)
-
-	t.Log("Creating Service")
-	loc := testServiceCreate(t, client, "test-site-2")
-	t.Logf("Created service at location: %s", loc)
-
-	t.Log("Deleting Service Assets")
-	testServiceAssetDelete(t, client, loc)
-}
-
-func testServiceAssetDelete(t *testing.T, client *gophercloud.ServiceClient, url string) {
-	deleteOpts := osServiceAssets.DeleteOpts{
-		All: true,
-	}
-	err := serviceassets.Delete(client, url, deleteOpts).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Log("Successfully deleted all Service Assets")
-}
diff --git a/acceptance/rackspace/client_test.go b/acceptance/rackspace/client_test.go
deleted file mode 100644
index 61214c0..0000000
--- a/acceptance/rackspace/client_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// +build acceptance
-
-package rackspace
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/rackspace"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestAuthenticatedClient(t *testing.T) {
-	// Obtain credentials from the environment.
-	ao, err := rackspace.AuthOptionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	client, err := rackspace.AuthenticatedClient(tools.OnlyRS(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/acceptance/rackspace/compute/v2/bootfromvolume_test.go b/acceptance/rackspace/compute/v2/bootfromvolume_test.go
deleted file mode 100644
index d7e6aa7..0000000
--- a/acceptance/rackspace/compute/v2/bootfromvolume_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// +build acceptance
-
-package v2
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	osBFV "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/bootfromvolume"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestBootFromVolume(t *testing.T) {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	if testing.Short() {
-		t.Skip("Skipping test that requires server creation in short mode.")
-	}
-
-	options, err := optionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	name := tools.RandomString("Gophercloud-", 8)
-	t.Logf("Creating server [%s].", name)
-
-	bd := []osBFV.BlockDevice{
-		osBFV.BlockDevice{
-			UUID:       options.imageID,
-			SourceType: osBFV.Image,
-			VolumeSize: 10,
-		},
-	}
-
-	server, err := bootfromvolume.Create(client, servers.CreateOpts{
-		Name:        name,
-		FlavorRef:   "performance1-1",
-		BlockDevice: bd,
-	}).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created server: %+v\n", server)
-	defer deleteServer(t, client, server)
-
-	getServer(t, client, server)
-
-	listServers(t, client)
-}
diff --git a/acceptance/rackspace/compute/v2/compute_test.go b/acceptance/rackspace/compute/v2/compute_test.go
deleted file mode 100644
index 3ca6dc9..0000000
--- a/acceptance/rackspace/compute/v2/compute_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// +build acceptance
-
-package v2
-
-import (
-	"errors"
-	"os"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/rackspace"
-)
-
-func newClient() (*gophercloud.ServiceClient, error) {
-	// Obtain credentials from the environment.
-	options, err := rackspace.AuthOptionsFromEnv()
-	if err != nil {
-		return nil, err
-	}
-	options = tools.OnlyRS(options)
-	region := os.Getenv("RS_REGION")
-
-	if options.Username == "" {
-		return nil, errors.New("Please provide a Rackspace username as RS_USERNAME.")
-	}
-	if options.APIKey == "" {
-		return nil, errors.New("Please provide a Rackspace API key as RS_API_KEY.")
-	}
-	if region == "" {
-		return nil, errors.New("Please provide a Rackspace region as RS_REGION.")
-	}
-
-	client, err := rackspace.AuthenticatedClient(options)
-	if err != nil {
-		return nil, err
-	}
-
-	return rackspace.NewComputeV2(client, gophercloud.EndpointOpts{
-		Region: region,
-	})
-}
-
-type serverOpts struct {
-	imageID  string
-	flavorID string
-}
-
-func optionsFromEnv() (*serverOpts, error) {
-	options := &serverOpts{
-		imageID:  os.Getenv("RS_IMAGE_ID"),
-		flavorID: os.Getenv("RS_FLAVOR_ID"),
-	}
-	if options.imageID == "" {
-		return nil, errors.New("Please provide a valid Rackspace image ID as RS_IMAGE_ID")
-	}
-	if options.flavorID == "" {
-		return nil, errors.New("Please provide a valid Rackspace flavor ID as RS_FLAVOR_ID")
-	}
-	return options, nil
-}
diff --git a/acceptance/rackspace/compute/v2/flavors_test.go b/acceptance/rackspace/compute/v2/flavors_test.go
deleted file mode 100644
index 4618ecc..0000000
--- a/acceptance/rackspace/compute/v2/flavors_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// +build acceptance
-
-package v2
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/flavors"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestListFlavors(t *testing.T) {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	count := 0
-	err = flavors.ListDetail(client, nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		t.Logf("-- Page %0d --", count)
-
-		fs, err := flavors.ExtractFlavors(page)
-		th.AssertNoErr(t, err)
-
-		for i, flavor := range fs {
-			t.Logf("[%02d]      id=[%s]", i, flavor.ID)
-			t.Logf("        name=[%s]", flavor.Name)
-			t.Logf("        disk=[%d]", flavor.Disk)
-			t.Logf("         RAM=[%d]", flavor.RAM)
-			t.Logf(" rxtx_factor=[%f]", flavor.RxTxFactor)
-			t.Logf("        swap=[%d]", flavor.Swap)
-			t.Logf("       VCPUs=[%d]", flavor.VCPUs)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	if count == 0 {
-		t.Errorf("No flavors listed!")
-	}
-}
-
-func TestGetFlavor(t *testing.T) {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	options, err := optionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	flavor, err := flavors.Get(client, options.flavorID).Extract()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Requested flavor:")
-	t.Logf("          id=[%s]", flavor.ID)
-	t.Logf("        name=[%s]", flavor.Name)
-	t.Logf("        disk=[%d]", flavor.Disk)
-	t.Logf("         RAM=[%d]", flavor.RAM)
-	t.Logf(" rxtx_factor=[%f]", flavor.RxTxFactor)
-	t.Logf("        swap=[%d]", flavor.Swap)
-	t.Logf("       VCPUs=[%d]", flavor.VCPUs)
-}
diff --git a/acceptance/rackspace/compute/v2/images_test.go b/acceptance/rackspace/compute/v2/images_test.go
deleted file mode 100644
index 5e36c2e..0000000
--- a/acceptance/rackspace/compute/v2/images_test.go
+++ /dev/null
@@ -1,63 +0,0 @@
-// +build acceptance
-
-package v2
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/images"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestListImages(t *testing.T) {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	count := 0
-	err = images.ListDetail(client, nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		t.Logf("-- Page %02d --", count)
-
-		is, err := images.ExtractImages(page)
-		th.AssertNoErr(t, err)
-
-		for i, image := range is {
-			t.Logf("[%02d]   id=[%s]", i, image.ID)
-			t.Logf("     name=[%s]", image.Name)
-			t.Logf("  created=[%s]", image.Created)
-			t.Logf("  updated=[%s]", image.Updated)
-			t.Logf(" min disk=[%d]", image.MinDisk)
-			t.Logf("  min RAM=[%d]", image.MinRAM)
-			t.Logf(" progress=[%d]", image.Progress)
-			t.Logf("   status=[%s]", image.Status)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	if count < 1 {
-		t.Errorf("Expected at least one page of images.")
-	}
-}
-
-func TestGetImage(t *testing.T) {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	options, err := optionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	image, err := images.Get(client, options.imageID).Extract()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Requested image:")
-	t.Logf("       id=[%s]", image.ID)
-	t.Logf("     name=[%s]", image.Name)
-	t.Logf("  created=[%s]", image.Created)
-	t.Logf("  updated=[%s]", image.Updated)
-	t.Logf(" min disk=[%d]", image.MinDisk)
-	t.Logf("  min RAM=[%d]", image.MinRAM)
-	t.Logf(" progress=[%d]", image.Progress)
-	t.Logf("   status=[%s]", image.Status)
-}
diff --git a/acceptance/rackspace/compute/v2/keypairs_test.go b/acceptance/rackspace/compute/v2/keypairs_test.go
deleted file mode 100644
index 9bd6eb4..0000000
--- a/acceptance/rackspace/compute/v2/keypairs_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// +build acceptance rackspace
-
-package v2
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/keypairs"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func deleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, name string) {
-	err := keypairs.Delete(client, name).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Successfully deleted key [%s].", name)
-}
-
-func TestCreateKeyPair(t *testing.T) {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	name := tools.RandomString("createdkey-", 8)
-	k, err := keypairs.Create(client, os.CreateOpts{Name: name}).Extract()
-	th.AssertNoErr(t, err)
-	defer deleteKeyPair(t, client, name)
-
-	t.Logf("Created a new keypair:")
-	t.Logf("        name=[%s]", k.Name)
-	t.Logf(" fingerprint=[%s]", k.Fingerprint)
-	t.Logf("   publickey=[%s]", tools.Elide(k.PublicKey))
-	t.Logf("  privatekey=[%s]", tools.Elide(k.PrivateKey))
-	t.Logf("      userid=[%s]", k.UserID)
-}
-
-func TestImportKeyPair(t *testing.T) {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	name := tools.RandomString("importedkey-", 8)
-	pubkey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDlIQ3r+zd97kb9Hzmujd3V6pbO53eb3Go4q2E8iqVGWQfZTrFdL9KACJnqJIm9HmncfRkUTxE37hqeGCCv8uD+ZPmPiZG2E60OX1mGDjbbzAyReRwYWXgXHopggZTLak5k4mwZYaxwaufbVBDRn847e01lZnaXaszEToLM37NLw+uz29sl3TwYy2R0RGHPwPc160aWmdLjSyd1Nd4c9pvvOP/EoEuBjIC6NJJwg2Rvg9sjjx9jYj0QUgc8CqKLN25oMZ69kNJzlFylKRUoeeVr89txlR59yehJWk6Uw6lYFTdJmcmQOFVAJ12RMmS1hLWCM8UzAgtw+EDa0eqBxBDl smash@winter"
-
-	k, err := keypairs.Create(client, os.CreateOpts{
-		Name:      name,
-		PublicKey: pubkey,
-	}).Extract()
-	th.AssertNoErr(t, err)
-	defer deleteKeyPair(t, client, name)
-
-	th.CheckEquals(t, pubkey, k.PublicKey)
-	th.CheckEquals(t, "", k.PrivateKey)
-
-	t.Logf("Imported an existing keypair:")
-	t.Logf("        name=[%s]", k.Name)
-	t.Logf(" fingerprint=[%s]", k.Fingerprint)
-	t.Logf("   publickey=[%s]", tools.Elide(k.PublicKey))
-	t.Logf("  privatekey=[%s]", tools.Elide(k.PrivateKey))
-	t.Logf("      userid=[%s]", k.UserID)
-}
-
-func TestListKeyPairs(t *testing.T) {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	count := 0
-	err = keypairs.List(client).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		t.Logf("--- %02d ---", count)
-
-		ks, err := keypairs.ExtractKeyPairs(page)
-		th.AssertNoErr(t, err)
-
-		for i, keypair := range ks {
-			t.Logf("[%02d]    name=[%s]", i, keypair.Name)
-			t.Logf(" fingerprint=[%s]", keypair.Fingerprint)
-			t.Logf("   publickey=[%s]", tools.Elide(keypair.PublicKey))
-			t.Logf("  privatekey=[%s]", tools.Elide(keypair.PrivateKey))
-			t.Logf("      userid=[%s]", keypair.UserID)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-}
diff --git a/acceptance/rackspace/compute/v2/networks_test.go b/acceptance/rackspace/compute/v2/networks_test.go
deleted file mode 100644
index e8fc4d3..0000000
--- a/acceptance/rackspace/compute/v2/networks_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// +build acceptance rackspace
-
-package v2
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/networks"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestNetworks(t *testing.T) {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	// Create a network
-	n, err := networks.Create(client, networks.CreateOpts{Label: "sample_network", CIDR: "172.20.0.0/24"}).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created network: %+v\n", n)
-	defer networks.Delete(client, n.ID)
-	th.AssertEquals(t, n.Label, "sample_network")
-	th.AssertEquals(t, n.CIDR, "172.20.0.0/24")
-	networkID := n.ID
-
-	// List networks
-	pager := networks.List(client)
-	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] Label [%s] CIDR [%s]",
-				n.ID, n.Label, n.CIDR)
-		}
-
-		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()
-	t.Logf("Retrieved Network: %+v\n", n)
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, n.CIDR, "172.20.0.0/24")
-	th.AssertEquals(t, n.Label, "sample_network")
-	th.AssertEquals(t, n.ID, networkID)
-}
diff --git a/acceptance/rackspace/compute/v2/pkg.go b/acceptance/rackspace/compute/v2/pkg.go
deleted file mode 100644
index 5ec3cc8..0000000
--- a/acceptance/rackspace/compute/v2/pkg.go
+++ /dev/null
@@ -1 +0,0 @@
-package v2
diff --git a/acceptance/rackspace/compute/v2/servers_test.go b/acceptance/rackspace/compute/v2/servers_test.go
deleted file mode 100644
index a8b5937..0000000
--- a/acceptance/rackspace/compute/v2/servers_test.go
+++ /dev/null
@@ -1,217 +0,0 @@
-// +build acceptance
-
-package v2
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig"
-	oskey "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/keypairs"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func createServerKeyPair(t *testing.T, client *gophercloud.ServiceClient) *oskey.KeyPair {
-	name := tools.RandomString("importedkey-", 8)
-	pubkey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDlIQ3r+zd97kb9Hzmujd3V6pbO53eb3Go4q2E8iqVGWQfZTrFdL9KACJnqJIm9HmncfRkUTxE37hqeGCCv8uD+ZPmPiZG2E60OX1mGDjbbzAyReRwYWXgXHopggZTLak5k4mwZYaxwaufbVBDRn847e01lZnaXaszEToLM37NLw+uz29sl3TwYy2R0RGHPwPc160aWmdLjSyd1Nd4c9pvvOP/EoEuBjIC6NJJwg2Rvg9sjjx9jYj0QUgc8CqKLN25oMZ69kNJzlFylKRUoeeVr89txlR59yehJWk6Uw6lYFTdJmcmQOFVAJ12RMmS1hLWCM8UzAgtw+EDa0eqBxBDl smash@winter"
-
-	k, err := keypairs.Create(client, oskey.CreateOpts{
-		Name:      name,
-		PublicKey: pubkey,
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	return k
-}
-
-func createServer(t *testing.T, client *gophercloud.ServiceClient, keyName string) *os.Server {
-	if testing.Short() {
-		t.Skip("Skipping test that requires server creation in short mode.")
-	}
-
-	options, err := optionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	name := tools.RandomString("Gophercloud-", 8)
-
-	pwd := tools.MakeNewPassword("")
-
-	opts := &servers.CreateOpts{
-		Name:       name,
-		ImageRef:   options.imageID,
-		FlavorRef:  options.flavorID,
-		DiskConfig: diskconfig.Manual,
-		AdminPass:  pwd,
-	}
-
-	if keyName != "" {
-		opts.KeyPair = keyName
-	}
-
-	t.Logf("Creating server [%s].", name)
-	s, err := servers.Create(client, opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Creating server.")
-
-	err = servers.WaitForStatus(client, s.ID, "ACTIVE", 300)
-	th.AssertNoErr(t, err)
-	t.Logf("Server created successfully.")
-
-	th.CheckEquals(t, pwd, s.AdminPass)
-
-	return s
-}
-
-func logServer(t *testing.T, server *os.Server, index int) {
-	if index == -1 {
-		t.Logf("             id=[%s]", server.ID)
-	} else {
-		t.Logf("[%02d]             id=[%s]", index, server.ID)
-	}
-	t.Logf("           name=[%s]", server.Name)
-	t.Logf("      tenant ID=[%s]", server.TenantID)
-	t.Logf("        user ID=[%s]", server.UserID)
-	t.Logf("        updated=[%s]", server.Updated)
-	t.Logf("        created=[%s]", server.Created)
-	t.Logf("        host ID=[%s]", server.HostID)
-	t.Logf("    access IPv4=[%s]", server.AccessIPv4)
-	t.Logf("    access IPv6=[%s]", server.AccessIPv6)
-	t.Logf("          image=[%v]", server.Image)
-	t.Logf("         flavor=[%v]", server.Flavor)
-	t.Logf("      addresses=[%v]", server.Addresses)
-	t.Logf("       metadata=[%v]", server.Metadata)
-	t.Logf("          links=[%v]", server.Links)
-	t.Logf("        keyname=[%s]", server.KeyName)
-	t.Logf(" admin password=[%s]", server.AdminPass)
-	t.Logf("         status=[%s]", server.Status)
-	t.Logf("       progress=[%d]", server.Progress)
-}
-
-func getServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
-	t.Logf("> servers.Get")
-
-	details, err := servers.Get(client, server.ID).Extract()
-	th.AssertNoErr(t, err)
-	logServer(t, details, -1)
-}
-
-func updateServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
-	t.Logf("> servers.Get")
-
-	opts := os.UpdateOpts{
-		Name: "updated-server",
-	}
-	updatedServer, err := servers.Update(client, server.ID, opts).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, "updated-server", updatedServer.Name)
-	logServer(t, updatedServer, -1)
-}
-
-func listServers(t *testing.T, client *gophercloud.ServiceClient) {
-	t.Logf("> servers.List")
-
-	count := 0
-	err := servers.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		t.Logf("--- Page %02d ---", count)
-
-		s, err := servers.ExtractServers(page)
-		th.AssertNoErr(t, err)
-		for index, server := range s {
-			logServer(t, &server, index)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-}
-
-func changeAdminPassword(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
-	t.Logf("> servers.ChangeAdminPassword")
-
-	original := server.AdminPass
-
-	t.Logf("Changing server password.")
-	err := servers.ChangeAdminPassword(client, server.ID, tools.MakeNewPassword(original)).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300)
-	th.AssertNoErr(t, err)
-	t.Logf("Password changed successfully.")
-}
-
-func rebootServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
-	t.Logf("> servers.Reboot")
-
-	err := servers.Reboot(client, server.ID, os.HardReboot).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300)
-	th.AssertNoErr(t, err)
-
-	t.Logf("Server successfully rebooted.")
-}
-
-func rebuildServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
-	t.Logf("> servers.Rebuild")
-
-	options, err := optionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	opts := servers.RebuildOpts{
-		Name:       tools.RandomString("RenamedGopher", 16),
-		AdminPass:  tools.MakeNewPassword(server.AdminPass),
-		ImageID:    options.imageID,
-		DiskConfig: diskconfig.Manual,
-	}
-	after, err := servers.Rebuild(client, server.ID, opts).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, after.ID, server.ID)
-
-	err = servers.WaitForStatus(client, after.ID, "ACTIVE", 300)
-	th.AssertNoErr(t, err)
-
-	t.Logf("Server successfully rebuilt.")
-	logServer(t, after, -1)
-}
-
-func deleteServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
-	t.Logf("> servers.Delete")
-
-	res := servers.Delete(client, server.ID)
-	th.AssertNoErr(t, res.Err)
-
-	t.Logf("Server deleted successfully.")
-}
-
-func deleteServerKeyPair(t *testing.T, client *gophercloud.ServiceClient, k *oskey.KeyPair) {
-	t.Logf("> keypairs.Delete")
-
-	err := keypairs.Delete(client, k.Name).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Keypair deleted successfully.")
-}
-
-func TestServerOperations(t *testing.T) {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	kp := createServerKeyPair(t, client)
-	defer deleteServerKeyPair(t, client, kp)
-
-	server := createServer(t, client, kp.Name)
-	defer deleteServer(t, client, server)
-
-	getServer(t, client, server)
-	updateServer(t, client, server)
-	listServers(t, client)
-	changeAdminPassword(t, client, server)
-	rebootServer(t, client, server)
-	rebuildServer(t, client, server)
-}
diff --git a/acceptance/rackspace/compute/v2/virtualinterfaces_test.go b/acceptance/rackspace/compute/v2/virtualinterfaces_test.go
deleted file mode 100644
index 39475e1..0000000
--- a/acceptance/rackspace/compute/v2/virtualinterfaces_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// +build acceptance rackspace
-
-package v2
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/networks"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/virtualinterfaces"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestVirtualInterfaces(t *testing.T) {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	// Create a server
-	server := createServer(t, client, "")
-	t.Logf("Created Server: %v\n", server)
-	defer deleteServer(t, client, server)
-	serverID := server.ID
-
-	// Create a network
-	n, err := networks.Create(client, networks.CreateOpts{Label: "sample_network", CIDR: "172.20.0.0/24"}).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created Network: %v\n", n)
-	defer networks.Delete(client, n.ID)
-	networkID := n.ID
-
-	// Create a virtual interface
-	vi, err := virtualinterfaces.Create(client, serverID, networkID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created virtual interface: %+v\n", vi)
-	defer virtualinterfaces.Delete(client, serverID, vi.ID)
-
-	// List virtual interfaces
-	pager := virtualinterfaces.List(client, serverID)
-	err = pager.EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page ---")
-
-		virtualinterfacesList, err := virtualinterfaces.ExtractVirtualInterfaces(page)
-		th.AssertNoErr(t, err)
-
-		for _, vi := range virtualinterfacesList {
-			t.Logf("Virtual Interface: ID [%s] MAC Address [%s] IP Addresses [%v]",
-				vi.ID, vi.MACAddress, vi.IPAddresses)
-		}
-
-		return true, nil
-	})
-	th.CheckNoErr(t, err)
-}
diff --git a/acceptance/rackspace/compute/v2/volumeattach_test.go b/acceptance/rackspace/compute/v2/volumeattach_test.go
deleted file mode 100644
index 9848e2e..0000000
--- a/acceptance/rackspace/compute/v2/volumeattach_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// +build acceptance compute servers
-
-package v2
-
-import (
-	"os"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack"
-	osVolumes "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
-	osVolumeAttach "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
-	osServers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	"github.com/rackspace/gophercloud/rackspace"
-	"github.com/rackspace/gophercloud/rackspace/blockstorage/v1/volumes"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/volumeattach"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func newBlockClient(t *testing.T) (*gophercloud.ServiceClient, error) {
-	ao, err := rackspace.AuthOptionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	client, err := rackspace.AuthenticatedClient(ao)
-	th.AssertNoErr(t, err)
-
-	return openstack.NewBlockStorageV1(client, gophercloud.EndpointOpts{
-		Region: os.Getenv("RS_REGION_NAME"),
-	})
-}
-
-func createVAServer(t *testing.T, computeClient *gophercloud.ServiceClient, choices *serverOpts) (*osServers.Server, error) {
-	if testing.Short() {
-		t.Skip("Skipping test that requires server creation in short mode.")
-	}
-
-	name := tools.RandomString("ACPTTEST", 16)
-	t.Logf("Attempting to create server: %s\n", name)
-
-	pwd := tools.MakeNewPassword("")
-
-	server, err := servers.Create(computeClient, osServers.CreateOpts{
-		Name:      name,
-		FlavorRef: choices.flavorID,
-		ImageRef:  choices.imageID,
-		AdminPass: pwd,
-	}).Extract()
-	if err != nil {
-		t.Fatalf("Unable to create server: %v", err)
-	}
-
-	th.AssertEquals(t, pwd, server.AdminPass)
-
-	return server, err
-}
-
-func createVAVolume(t *testing.T, blockClient *gophercloud.ServiceClient) (*volumes.Volume, error) {
-	volume, err := volumes.Create(blockClient, &osVolumes.CreateOpts{
-		Size: 80,
-		Name: "gophercloud-test-volume",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	defer func() {
-		err = osVolumes.WaitForStatus(blockClient, volume.ID, "available", 60)
-		th.AssertNoErr(t, err)
-	}()
-
-	return volume, err
-}
-
-func createVolumeAttachment(t *testing.T, computeClient *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, serverID string, volumeID string) {
-	va, err := volumeattach.Create(computeClient, serverID, &osVolumeAttach.CreateOpts{
-		VolumeID: volumeID,
-	}).Extract()
-	th.AssertNoErr(t, err)
-	defer func() {
-		err = osVolumes.WaitForStatus(blockClient, volumeID, "in-use", 60)
-		th.AssertNoErr(t, err)
-		err = volumeattach.Delete(computeClient, serverID, va.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-		err = osVolumes.WaitForStatus(blockClient, volumeID, "available", 60)
-		th.AssertNoErr(t, err)
-	}()
-	t.Logf("Attached volume to server: %+v", va)
-}
-
-func TestAttachVolume(t *testing.T) {
-	choices, err := optionsFromEnv()
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	computeClient, err := newClient()
-	if err != nil {
-		t.Fatalf("Unable to create a compute client: %v", err)
-	}
-
-	blockClient, err := newBlockClient(t)
-	if err != nil {
-		t.Fatalf("Unable to create a blockstorage client: %v", err)
-	}
-
-	server, err := createVAServer(t, computeClient, choices)
-	if err != nil {
-		t.Fatalf("Unable to create server: %v", err)
-	}
-	defer func() {
-		servers.Delete(computeClient, server.ID)
-		t.Logf("Server deleted.")
-	}()
-
-	if err = osServers.WaitForStatus(computeClient, server.ID, "ACTIVE", 300); err != nil {
-		t.Fatalf("Unable to wait for server: %v", err)
-	}
-
-	volume, err := createVAVolume(t, blockClient)
-	if err != nil {
-		t.Fatalf("Unable to create volume: %v", err)
-	}
-	defer func() {
-		err = volumes.Delete(blockClient, volume.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-		t.Logf("Volume deleted.")
-	}()
-
-	createVolumeAttachment(t, computeClient, blockClient, server.ID, volume.ID)
-
-}
diff --git a/acceptance/rackspace/db/v1/backup_test.go b/acceptance/rackspace/db/v1/backup_test.go
deleted file mode 100644
index 522aace..0000000
--- a/acceptance/rackspace/db/v1/backup_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// +build acceptance db rackspace
-
-package v1
-
-import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/rackspace/gophercloud/rackspace/db/v1/backups"
-	"github.com/rackspace/gophercloud/rackspace/db/v1/instances"
-)
-
-func (c *context) createBackup() {
-	opts := backups.CreateOpts{
-		Name:       tools.RandomString("backup_", 5),
-		InstanceID: c.instanceID,
-	}
-
-	backup, err := backups.Create(c.client, opts).Extract()
-
-	c.Logf("Created backup %#v", backup)
-	c.AssertNoErr(err)
-
-	err = gophercloud.WaitFor(60, func() (bool, error) {
-		b, err := backups.Get(c.client, backup.ID).Extract()
-		if err != nil {
-			return false, err
-		}
-		if b.Status == "COMPLETED" {
-			return true, nil
-		}
-		return false, nil
-	})
-	c.AssertNoErr(err)
-
-	c.backupID = backup.ID
-}
-
-func (c *context) getBackup() {
-	backup, err := backups.Get(c.client, c.backupID).Extract()
-	c.AssertNoErr(err)
-	c.Logf("Getting backup %s", backup.ID)
-}
-
-func (c *context) listAllBackups() {
-	c.Logf("Listing backups")
-
-	err := backups.List(c.client, nil).EachPage(func(page pagination.Page) (bool, error) {
-		backupList, err := backups.ExtractBackups(page)
-		c.AssertNoErr(err)
-
-		for _, b := range backupList {
-			c.Logf("Backup: %#v", b)
-		}
-
-		return true, nil
-	})
-
-	c.AssertNoErr(err)
-}
-
-func (c *context) listInstanceBackups() {
-	c.Logf("Listing backups for instance %s", c.instanceID)
-
-	err := instances.ListBackups(c.client, c.instanceID).EachPage(func(page pagination.Page) (bool, error) {
-		backupList, err := backups.ExtractBackups(page)
-		c.AssertNoErr(err)
-
-		for _, b := range backupList {
-			c.Logf("Backup: %#v", b)
-		}
-
-		return true, nil
-	})
-
-	c.AssertNoErr(err)
-}
-
-func (c *context) deleteBackup() {
-	err := backups.Delete(c.client, c.backupID).ExtractErr()
-	c.AssertNoErr(err)
-	c.Logf("Deleted backup %s", c.backupID)
-}
diff --git a/acceptance/rackspace/db/v1/common.go b/acceptance/rackspace/db/v1/common.go
deleted file mode 100644
index 24512b9..0000000
--- a/acceptance/rackspace/db/v1/common.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// +build acceptance db rackspace
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/rackspace"
-	"github.com/rackspace/gophercloud/rackspace/db/v1/instances"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func newClient(t *testing.T) *gophercloud.ServiceClient {
-	opts, err := rackspace.AuthOptionsFromEnv()
-	th.AssertNoErr(t, err)
-	opts = tools.OnlyRS(opts)
-
-	client, err := rackspace.AuthenticatedClient(opts)
-	th.AssertNoErr(t, err)
-
-	c, err := rackspace.NewDBV1(client, gophercloud.EndpointOpts{
-		Region: "IAD",
-	})
-	th.AssertNoErr(t, err)
-
-	return c
-}
-
-type context struct {
-	test          *testing.T
-	client        *gophercloud.ServiceClient
-	instanceID    string
-	DBIDs         []string
-	replicaID     string
-	backupID      string
-	configGroupID string
-	users         []string
-}
-
-func newContext(t *testing.T) context {
-	return context{
-		test:   t,
-		client: newClient(t),
-	}
-}
-
-func (c context) Logf(msg string, args ...interface{}) {
-	if len(args) > 0 {
-		c.test.Logf(msg, args...)
-	} else {
-		c.test.Log(msg)
-	}
-}
-
-func (c context) AssertNoErr(err error) {
-	th.AssertNoErr(c.test, err)
-}
-
-func (c context) WaitUntilActive(id string) {
-	err := gophercloud.WaitFor(60, func() (bool, error) {
-		inst, err := instances.Get(c.client, id).Extract()
-		if err != nil {
-			return false, err
-		}
-		if inst.Status == "ACTIVE" {
-			return true, nil
-		}
-		return false, nil
-	})
-	c.AssertNoErr(err)
-}
diff --git a/acceptance/rackspace/db/v1/config_group_test.go b/acceptance/rackspace/db/v1/config_group_test.go
deleted file mode 100644
index 81bd40a..0000000
--- a/acceptance/rackspace/db/v1/config_group_test.go
+++ /dev/null
@@ -1,93 +0,0 @@
-// +build acceptance db rackspace
-
-package v1
-
-import (
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/configurations"
-	"github.com/rackspace/gophercloud/pagination"
-	config "github.com/rackspace/gophercloud/rackspace/db/v1/configurations"
-	"github.com/rackspace/gophercloud/rackspace/db/v1/instances"
-)
-
-func (c *context) createConfigGrp() {
-	opts := os.CreateOpts{
-		Name: tools.RandomString("config_", 5),
-		Values: map[string]interface{}{
-			"connect_timeout":  300,
-			"join_buffer_size": 900000,
-		},
-	}
-
-	cg, err := config.Create(c.client, opts).Extract()
-
-	c.AssertNoErr(err)
-	c.Logf("Created config group %#v", cg)
-
-	c.configGroupID = cg.ID
-}
-
-func (c *context) getConfigGrp() {
-	cg, err := config.Get(c.client, c.configGroupID).Extract()
-	c.Logf("Getting config group: %#v", cg)
-	c.AssertNoErr(err)
-}
-
-func (c *context) updateConfigGrp() {
-	opts := os.UpdateOpts{
-		Name: tools.RandomString("new_name_", 5),
-		Values: map[string]interface{}{
-			"connect_timeout": 250,
-		},
-	}
-	err := config.Update(c.client, c.configGroupID, opts).ExtractErr()
-	c.Logf("Updated config group %s", c.configGroupID)
-	c.AssertNoErr(err)
-}
-
-func (c *context) replaceConfigGrp() {
-	opts := os.UpdateOpts{
-		Values: map[string]interface{}{
-			"big_tables": 1,
-		},
-	}
-
-	err := config.Replace(c.client, c.configGroupID, opts).ExtractErr()
-	c.Logf("Replaced values for config group %s", c.configGroupID)
-	c.AssertNoErr(err)
-}
-
-func (c *context) associateInstanceWithConfigGrp() {
-	err := instances.AssociateWithConfigGroup(c.client, c.instanceID, c.configGroupID).ExtractErr()
-	c.Logf("Associated instance %s with config group %s", c.instanceID, c.configGroupID)
-	c.AssertNoErr(err)
-}
-
-func (c *context) listConfigGrpInstances() {
-	c.Logf("Listing all instances associated with config group %s", c.configGroupID)
-
-	err := config.ListInstances(c.client, c.configGroupID).EachPage(func(page pagination.Page) (bool, error) {
-		instanceList, err := instances.ExtractInstances(page)
-		c.AssertNoErr(err)
-
-		for _, instance := range instanceList {
-			c.Logf("Instance: %#v", instance)
-		}
-
-		return true, nil
-	})
-
-	c.AssertNoErr(err)
-}
-
-func (c *context) deleteConfigGrp() {
-	err := config.Delete(c.client, c.configGroupID).ExtractErr()
-	c.Logf("Deleted config group %s", c.configGroupID)
-	c.AssertNoErr(err)
-}
-
-func (c *context) detachInstanceFromGrp() {
-	err := instances.DetachFromConfigGroup(c.client, c.instanceID).ExtractErr()
-	c.Logf("Detached instance %s from config groups", c.instanceID)
-	c.AssertNoErr(err)
-}
diff --git a/acceptance/rackspace/db/v1/database_test.go b/acceptance/rackspace/db/v1/database_test.go
deleted file mode 100644
index d5c448f..0000000
--- a/acceptance/rackspace/db/v1/database_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// +build acceptance db rackspace
-
-package v1
-
-import (
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-func (c *context) createDBs() {
-	dbs := []string{
-		tools.RandomString("db_", 5),
-		tools.RandomString("db_", 5),
-		tools.RandomString("db_", 5),
-	}
-
-	opts := db.BatchCreateOpts{
-		db.CreateOpts{Name: dbs[0]},
-		db.CreateOpts{Name: dbs[1]},
-		db.CreateOpts{Name: dbs[2]},
-	}
-
-	err := db.Create(c.client, c.instanceID, opts).ExtractErr()
-	c.Logf("Created three databases on instance %s: %s, %s, %s", c.instanceID, dbs[0], dbs[1], dbs[2])
-	c.AssertNoErr(err)
-
-	c.DBIDs = dbs
-}
-
-func (c *context) listDBs() {
-	c.Logf("Listing databases on instance %s", c.instanceID)
-
-	err := db.List(c.client, c.instanceID).EachPage(func(page pagination.Page) (bool, error) {
-		dbList, err := db.ExtractDBs(page)
-		c.AssertNoErr(err)
-
-		for _, db := range dbList {
-			c.Logf("DB: %#v", db)
-		}
-
-		return true, nil
-	})
-
-	c.AssertNoErr(err)
-}
-
-func (c *context) deleteDBs() {
-	for _, id := range c.DBIDs {
-		err := db.Delete(c.client, c.instanceID, id).ExtractErr()
-		c.AssertNoErr(err)
-		c.Logf("Deleted DB %s", id)
-	}
-}
diff --git a/acceptance/rackspace/db/v1/flavor_test.go b/acceptance/rackspace/db/v1/flavor_test.go
deleted file mode 100644
index 0d6e6df..0000000
--- a/acceptance/rackspace/db/v1/flavor_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// +build acceptance db rackspace
-
-package v1
-
-import (
-	os "github.com/rackspace/gophercloud/openstack/db/v1/flavors"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/db/v1/flavors"
-)
-
-func (c context) listFlavors() {
-	c.Logf("Listing flavors")
-
-	err := flavors.List(c.client).EachPage(func(page pagination.Page) (bool, error) {
-		flavorList, err := os.ExtractFlavors(page)
-		c.AssertNoErr(err)
-
-		for _, f := range flavorList {
-			c.Logf("Flavor: ID [%s] Name [%s] RAM [%d]", f.ID, f.Name, f.RAM)
-		}
-
-		return true, nil
-	})
-
-	c.AssertNoErr(err)
-}
-
-func (c context) getFlavor() {
-	flavor, err := flavors.Get(c.client, "1").Extract()
-	c.Logf("Getting flavor %s", flavor.ID)
-	c.AssertNoErr(err)
-}
diff --git a/acceptance/rackspace/db/v1/instance_test.go b/acceptance/rackspace/db/v1/instance_test.go
deleted file mode 100644
index b5540e3..0000000
--- a/acceptance/rackspace/db/v1/instance_test.go
+++ /dev/null
@@ -1,169 +0,0 @@
-// +build acceptance db rackspace
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/db/v1/instances"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestRunner(t *testing.T) {
-	c := newContext(t)
-
-	// FLAVOR tests
-	c.listFlavors()
-	c.getFlavor()
-
-	// INSTANCE tests
-	c.createInstance()
-	c.listInstances()
-	c.getInstance()
-	c.isRootEnabled()
-	c.enableRootUser()
-	c.isRootEnabled()
-	c.restartInstance()
-	c.resizeInstance()
-	c.resizeVol()
-	c.getDefaultConfig()
-
-	// REPLICA tests
-	c.createReplica()
-	c.detachReplica()
-
-	// BACKUP tests
-	c.createBackup()
-	c.getBackup()
-	c.listAllBackups()
-	c.listInstanceBackups()
-	c.deleteBackup()
-
-	// CONFIG GROUP tests
-	c.createConfigGrp()
-	c.getConfigGrp()
-	c.updateConfigGrp()
-	c.replaceConfigGrp()
-	c.associateInstanceWithConfigGrp()
-	c.listConfigGrpInstances()
-	c.detachInstanceFromGrp()
-	c.deleteConfigGrp()
-
-	// DATABASE tests
-	c.createDBs()
-	c.listDBs()
-
-	// USER tests
-	c.createUsers()
-	c.listUsers()
-	c.changeUserPwd()
-	c.getUser()
-	c.updateUser()
-	c.listUserAccess()
-	c.revokeUserAccess()
-	c.grantUserAccess()
-
-	// TEARDOWN
-	c.deleteUsers()
-	c.deleteDBs()
-
-	c.restartInstance()
-	c.WaitUntilActive(c.instanceID)
-
-	c.deleteInstance(c.replicaID)
-	c.deleteInstance(c.instanceID)
-}
-
-func (c *context) createInstance() {
-	opts := instances.CreateOpts{
-		FlavorRef: "1",
-		Size:      1,
-		Name:      tools.RandomString("gopher_db", 5),
-	}
-
-	instance, err := instances.Create(c.client, opts).Extract()
-	th.AssertNoErr(c.test, err)
-
-	c.Logf("Creating %s. Waiting...", instance.ID)
-	c.WaitUntilActive(instance.ID)
-	c.Logf("Created instance %s", instance.ID)
-
-	c.instanceID = instance.ID
-}
-
-func (c *context) listInstances() {
-	c.Logf("Listing instances")
-
-	err := instances.List(c.client, nil).EachPage(func(page pagination.Page) (bool, error) {
-		instanceList, err := instances.ExtractInstances(page)
-		c.AssertNoErr(err)
-
-		for _, i := range instanceList {
-			c.Logf("Instance: ID [%s] Name [%s] Status [%s] VolSize [%d] Datastore Type [%s]",
-				i.ID, i.Name, i.Status, i.Volume.Size, i.Datastore.Type)
-		}
-
-		return true, nil
-	})
-
-	c.AssertNoErr(err)
-}
-
-func (c *context) getInstance() {
-	instance, err := instances.Get(c.client, c.instanceID).Extract()
-	c.AssertNoErr(err)
-	c.Logf("Getting instance: %#v", instance)
-}
-
-func (c *context) deleteInstance(id string) {
-	err := instances.Delete(c.client, id).ExtractErr()
-	c.AssertNoErr(err)
-	c.Logf("Deleted instance %s", id)
-}
-
-func (c *context) enableRootUser() {
-	_, err := instances.EnableRootUser(c.client, c.instanceID).Extract()
-	c.AssertNoErr(err)
-	c.Logf("Enabled root user on %s", c.instanceID)
-}
-
-func (c *context) isRootEnabled() {
-	enabled, err := instances.IsRootEnabled(c.client, c.instanceID)
-	c.AssertNoErr(err)
-	c.Logf("Is root enabled? %s", enabled)
-}
-
-func (c *context) restartInstance() {
-	id := c.instanceID
-	err := instances.Restart(c.client, id).ExtractErr()
-	c.AssertNoErr(err)
-	c.Logf("Restarting %s. Waiting...", id)
-	c.WaitUntilActive(id)
-	c.Logf("Restarted %s", id)
-}
-
-func (c *context) resizeInstance() {
-	id := c.instanceID
-	err := instances.Resize(c.client, id, "2").ExtractErr()
-	c.AssertNoErr(err)
-	c.Logf("Resizing %s. Waiting...", id)
-	c.WaitUntilActive(id)
-	c.Logf("Resized %s with flavorRef %s", id, "2")
-}
-
-func (c *context) resizeVol() {
-	id := c.instanceID
-	err := instances.ResizeVolume(c.client, id, 2).ExtractErr()
-	c.AssertNoErr(err)
-	c.Logf("Resizing volume of %s. Waiting...", id)
-	c.WaitUntilActive(id)
-	c.Logf("Resized the volume of %s to %d GB", id, 2)
-}
-
-func (c *context) getDefaultConfig() {
-	config, err := instances.GetDefaultConfig(c.client, c.instanceID).Extract()
-	c.Logf("Default config group for instance %s: %#v", c.instanceID, config)
-	c.AssertNoErr(err)
-}
diff --git a/acceptance/rackspace/db/v1/replica_test.go b/acceptance/rackspace/db/v1/replica_test.go
deleted file mode 100644
index 89edf9d..0000000
--- a/acceptance/rackspace/db/v1/replica_test.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// +build acceptance db rackspace
-
-package v1
-
-import (
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/rackspace/db/v1/instances"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func (c *context) createReplica() {
-	opts := instances.CreateOpts{
-		FlavorRef: "2",
-		Size:      1,
-		Name:      tools.RandomString("gopher_db", 5),
-		ReplicaOf: c.instanceID,
-	}
-
-	repl, err := instances.Create(c.client, opts).Extract()
-	th.AssertNoErr(c.test, err)
-
-	c.Logf("Creating replica of %s. Waiting...", c.instanceID)
-	c.WaitUntilActive(repl.ID)
-	c.Logf("Created replica %#v", repl)
-
-	c.replicaID = repl.ID
-}
-
-func (c *context) detachReplica() {
-	err := instances.DetachReplica(c.client, c.replicaID).ExtractErr()
-	c.Logf("Detached replica %s", c.replicaID)
-	c.AssertNoErr(err)
-}
diff --git a/acceptance/rackspace/db/v1/user_test.go b/acceptance/rackspace/db/v1/user_test.go
deleted file mode 100644
index 0488f5d..0000000
--- a/acceptance/rackspace/db/v1/user_test.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// +build acceptance db rackspace
-
-package v1
-
-import (
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/users"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/db/v1/users"
-)
-
-func (c *context) createUsers() {
-	c.users = []string{
-		tools.RandomString("user_", 5),
-		tools.RandomString("user_", 5),
-		tools.RandomString("user_", 5),
-	}
-
-	db1 := db.CreateOpts{Name: c.DBIDs[0]}
-	db2 := db.CreateOpts{Name: c.DBIDs[1]}
-	db3 := db.CreateOpts{Name: c.DBIDs[2]}
-
-	opts := os.BatchCreateOpts{
-		os.CreateOpts{
-			Name:      c.users[0],
-			Password:  tools.RandomString("db_", 5),
-			Databases: db.BatchCreateOpts{db1, db2, db3},
-		},
-		os.CreateOpts{
-			Name:      c.users[1],
-			Password:  tools.RandomString("db_", 5),
-			Databases: db.BatchCreateOpts{db1, db2},
-		},
-		os.CreateOpts{
-			Name:      c.users[2],
-			Password:  tools.RandomString("db_", 5),
-			Databases: db.BatchCreateOpts{db3},
-		},
-	}
-
-	err := users.Create(c.client, c.instanceID, opts).ExtractErr()
-	c.Logf("Created three users on instance %s: %s, %s, %s", c.instanceID, c.users[0], c.users[1], c.users[2])
-	c.AssertNoErr(err)
-}
-
-func (c *context) listUsers() {
-	c.Logf("Listing users on instance %s", c.instanceID)
-
-	err := os.List(c.client, c.instanceID).EachPage(func(page pagination.Page) (bool, error) {
-		uList, err := os.ExtractUsers(page)
-		c.AssertNoErr(err)
-
-		for _, u := range uList {
-			c.Logf("User: %#v", u)
-		}
-
-		return true, nil
-	})
-
-	c.AssertNoErr(err)
-}
-
-func (c *context) deleteUsers() {
-	for _, id := range c.users {
-		err := users.Delete(c.client, c.instanceID, id).ExtractErr()
-		c.AssertNoErr(err)
-		c.Logf("Deleted user %s", id)
-	}
-}
-
-func (c *context) changeUserPwd() {
-	opts := os.BatchCreateOpts{}
-
-	for _, name := range c.users[:1] {
-		opts = append(opts, os.CreateOpts{Name: name, Password: tools.RandomString("", 5)})
-	}
-
-	err := users.ChangePassword(c.client, c.instanceID, opts).ExtractErr()
-	c.Logf("Updated 2 users' passwords")
-	c.AssertNoErr(err)
-}
-
-func (c *context) getUser() {
-	user, err := users.Get(c.client, c.instanceID, c.users[0]).Extract()
-	c.Logf("Getting user %s", user)
-	c.AssertNoErr(err)
-}
-
-func (c *context) updateUser() {
-	opts := users.UpdateOpts{Name: tools.RandomString("new_name_", 5)}
-	err := users.Update(c.client, c.instanceID, c.users[0], opts).ExtractErr()
-	c.Logf("Updated user %s", c.users[0])
-	c.AssertNoErr(err)
-	c.users[0] = opts.Name
-}
-
-func (c *context) listUserAccess() {
-	err := users.ListAccess(c.client, c.instanceID, c.users[0]).EachPage(func(page pagination.Page) (bool, error) {
-		dbList, err := users.ExtractDBs(page)
-		c.AssertNoErr(err)
-
-		for _, db := range dbList {
-			c.Logf("User %s has access to DB: %#v", db)
-		}
-
-		return true, nil
-	})
-
-	c.AssertNoErr(err)
-}
-
-func (c *context) grantUserAccess() {
-	opts := db.BatchCreateOpts{db.CreateOpts{Name: c.DBIDs[0]}}
-	err := users.GrantAccess(c.client, c.instanceID, c.users[0], opts).ExtractErr()
-	c.Logf("Granted access for user %s to DB %s", c.users[0], c.DBIDs[0])
-	c.AssertNoErr(err)
-}
-
-func (c *context) revokeUserAccess() {
-	dbName, userName := c.DBIDs[0], c.users[0]
-	err := users.RevokeAccess(c.client, c.instanceID, userName, dbName).ExtractErr()
-	c.Logf("Revoked access for user %s to DB %s", userName, dbName)
-	c.AssertNoErr(err)
-}
diff --git a/acceptance/rackspace/identity/v2/extension_test.go b/acceptance/rackspace/identity/v2/extension_test.go
deleted file mode 100644
index a50e015..0000000
--- a/acceptance/rackspace/identity/v2/extension_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// +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/acceptance/rackspace/identity/v2/identity_test.go b/acceptance/rackspace/identity/v2/identity_test.go
deleted file mode 100644
index 1182982..0000000
--- a/acceptance/rackspace/identity/v2/identity_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// +build acceptance
-
-package v2
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/rackspace"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func rackspaceAuthOptions(t *testing.T) gophercloud.AuthOptions {
-	// Obtain credentials from the environment.
-	options, err := rackspace.AuthOptionsFromEnv()
-	th.AssertNoErr(t, err)
-	options = tools.OnlyRS(options)
-
-	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_API_KEY.")
-	}
-
-	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/acceptance/rackspace/identity/v2/pkg.go b/acceptance/rackspace/identity/v2/pkg.go
deleted file mode 100644
index 5ec3cc8..0000000
--- a/acceptance/rackspace/identity/v2/pkg.go
+++ /dev/null
@@ -1 +0,0 @@
-package v2
diff --git a/acceptance/rackspace/identity/v2/role_test.go b/acceptance/rackspace/identity/v2/role_test.go
deleted file mode 100644
index efaeb75..0000000
--- a/acceptance/rackspace/identity/v2/role_test.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// +build acceptance identity roles
-
-package v2
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles"
-
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/identity/v2/roles"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestRoles(t *testing.T) {
-	client := authenticatedClient(t)
-
-	userID := createUser(t, client)
-	roleID := listRoles(t, client)
-
-	addUserRole(t, client, userID, roleID)
-
-	deleteUserRole(t, client, userID, roleID)
-
-	deleteUser(t, client, userID)
-}
-
-func listRoles(t *testing.T, client *gophercloud.ServiceClient) string {
-	var roleID string
-
-	err := roles.List(client).EachPage(func(page pagination.Page) (bool, error) {
-		roleList, err := os.ExtractRoles(page)
-		th.AssertNoErr(t, err)
-
-		for _, role := range roleList {
-			t.Logf("Listing role: ID [%s] Name [%s]", role.ID, role.Name)
-			roleID = role.ID
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-
-	return roleID
-}
-
-func addUserRole(t *testing.T, client *gophercloud.ServiceClient, userID, roleID string) {
-	err := roles.AddUserRole(client, userID, roleID).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Added role %s to user %s", roleID, userID)
-}
-
-func deleteUserRole(t *testing.T, client *gophercloud.ServiceClient, userID, roleID string) {
-	err := roles.DeleteUserRole(client, userID, roleID).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Removed role %s from user %s", roleID, userID)
-}
diff --git a/acceptance/rackspace/identity/v2/tenant_test.go b/acceptance/rackspace/identity/v2/tenant_test.go
deleted file mode 100644
index 6081a49..0000000
--- a/acceptance/rackspace/identity/v2/tenant_test.go
+++ /dev/null
@@ -1,37 +0,0 @@
-// +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/acceptance/rackspace/identity/v2/tokens_test.go b/acceptance/rackspace/identity/v2/tokens_test.go
deleted file mode 100644
index b241cd9..0000000
--- a/acceptance/rackspace/identity/v2/tokens_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// +build acceptance
-
-package v2
-
-import (
-	"os"
-	"testing"
-
-	"github.com/rackspace/gophercloud/rackspace/identity/v2/tokens"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestTokenAuth(t *testing.T) {
-	authedClient := createClient(t, true)
-	token := authedClient.TokenID
-
-	tenantID := os.Getenv("RS_TENANT_ID")
-	if tenantID == "" {
-		t.Skip("You must set RS_TENANT_ID environment variable to run this test")
-	}
-
-	authOpts := tokens.AuthOptions{}
-	authOpts.TenantID = tenantID
-	authOpts.TokenID = token
-
-	_, err := tokens.Create(authedClient, authOpts).ExtractToken()
-	th.AssertNoErr(t, err)
-}
diff --git a/acceptance/rackspace/identity/v2/user_test.go b/acceptance/rackspace/identity/v2/user_test.go
deleted file mode 100644
index 28c0c83..0000000
--- a/acceptance/rackspace/identity/v2/user_test.go
+++ /dev/null
@@ -1,93 +0,0 @@
-// +build acceptance identity
-
-package v2
-
-import (
-	"strconv"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	os "github.com/rackspace/gophercloud/openstack/identity/v2/users"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/identity/v2/users"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestUsers(t *testing.T) {
-	client := authenticatedClient(t)
-
-	userID := createUser(t, client)
-
-	listUsers(t, client)
-
-	getUser(t, client, userID)
-
-	updateUser(t, client, userID)
-
-	resetApiKey(t, client, userID)
-
-	deleteUser(t, client, userID)
-}
-
-func createUser(t *testing.T, client *gophercloud.ServiceClient) string {
-	t.Log("Creating user")
-
-	opts := users.CreateOpts{
-		Username: tools.RandomString("user_", 5),
-		Enabled:  os.Disabled,
-		Email:    "new_user@foo.com",
-	}
-
-	user, err := users.Create(client, opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created user %s", user.ID)
-
-	return user.ID
-}
-
-func listUsers(t *testing.T, client *gophercloud.ServiceClient) {
-	err := users.List(client).EachPage(func(page pagination.Page) (bool, error) {
-		userList, err := os.ExtractUsers(page)
-		th.AssertNoErr(t, err)
-
-		for _, user := range userList {
-			t.Logf("Listing user: ID [%s] Username [%s] Email [%s] Enabled? [%s]",
-				user.ID, user.Username, user.Email, strconv.FormatBool(user.Enabled))
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func getUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
-	_, err := users.Get(client, userID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Getting user %s", userID)
-}
-
-func updateUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
-	opts := users.UpdateOpts{Username: tools.RandomString("new_name", 5), Email: "new@foo.com"}
-	user, err := users.Update(client, userID, opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Updated user %s: Username [%s] Email [%s]", userID, user.Username, user.Email)
-}
-
-func deleteUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
-	res := users.Delete(client, userID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted user %s", userID)
-}
-
-func resetApiKey(t *testing.T, client *gophercloud.ServiceClient, userID string) {
-	key, err := users.ResetAPIKey(client, userID).Extract()
-	th.AssertNoErr(t, err)
-
-	if key.APIKey == "" {
-		t.Fatal("failed to reset API key for user")
-	}
-
-	t.Logf("Reset API key for user %s to %s", key.Username, key.APIKey)
-}
diff --git a/acceptance/rackspace/lb/v1/acl_test.go b/acceptance/rackspace/lb/v1/acl_test.go
deleted file mode 100644
index 7a38027..0000000
--- a/acceptance/rackspace/lb/v1/acl_test.go
+++ /dev/null
@@ -1,94 +0,0 @@
-// +build acceptance lbs
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/acl"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestACL(t *testing.T) {
-	client := setup(t)
-
-	ids := createLB(t, client, 1)
-	lbID := ids[0]
-
-	createACL(t, client, lbID)
-
-	waitForLB(client, lbID, lbs.ACTIVE)
-
-	networkIDs := showACL(t, client, lbID)
-
-	deleteNetworkItem(t, client, lbID, networkIDs[0])
-	waitForLB(client, lbID, lbs.ACTIVE)
-
-	bulkDeleteACL(t, client, lbID, networkIDs[1:2])
-	waitForLB(client, lbID, lbs.ACTIVE)
-
-	deleteACL(t, client, lbID)
-	waitForLB(client, lbID, lbs.ACTIVE)
-
-	deleteLB(t, client, lbID)
-}
-
-func createACL(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	opts := acl.CreateOpts{
-		acl.CreateOpt{Address: "206.160.163.21", Type: acl.DENY},
-		acl.CreateOpt{Address: "206.160.165.11", Type: acl.DENY},
-		acl.CreateOpt{Address: "206.160.165.12", Type: acl.DENY},
-		acl.CreateOpt{Address: "206.160.165.13", Type: acl.ALLOW},
-	}
-
-	err := acl.Create(client, lbID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created ACL items for LB %d", lbID)
-}
-
-func showACL(t *testing.T, client *gophercloud.ServiceClient, lbID int) []int {
-	ids := []int{}
-
-	err := acl.List(client, lbID).EachPage(func(page pagination.Page) (bool, error) {
-		accessList, err := acl.ExtractAccessList(page)
-		th.AssertNoErr(t, err)
-
-		for _, i := range accessList {
-			t.Logf("Listing network item: ID [%s] Address [%s] Type [%s]", i.ID, i.Address, i.Type)
-			ids = append(ids, i.ID)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-
-	return ids
-}
-
-func deleteNetworkItem(t *testing.T, client *gophercloud.ServiceClient, lbID, itemID int) {
-	err := acl.Delete(client, lbID, itemID).ExtractErr()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Deleted network item %d", itemID)
-}
-
-func bulkDeleteACL(t *testing.T, client *gophercloud.ServiceClient, lbID int, items []int) {
-	err := acl.BulkDelete(client, lbID, items).ExtractErr()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Deleted network items %s", intsToStr(items))
-}
-
-func deleteACL(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	err := acl.DeleteAll(client, lbID).ExtractErr()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Deleted ACL from LB %d", lbID)
-}
diff --git a/acceptance/rackspace/lb/v1/common.go b/acceptance/rackspace/lb/v1/common.go
deleted file mode 100644
index 4ce05e6..0000000
--- a/acceptance/rackspace/lb/v1/common.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// +build acceptance lbs
-
-package v1
-
-import (
-	"os"
-	"strconv"
-	"strings"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/rackspace"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func newProvider() (*gophercloud.ProviderClient, error) {
-	opts, err := rackspace.AuthOptionsFromEnv()
-	if err != nil {
-		return nil, err
-	}
-	opts = tools.OnlyRS(opts)
-
-	return rackspace.AuthenticatedClient(opts)
-}
-
-func newClient() (*gophercloud.ServiceClient, error) {
-	provider, err := newProvider()
-	if err != nil {
-		return nil, err
-	}
-
-	return rackspace.NewLBV1(provider, gophercloud.EndpointOpts{
-		Region: os.Getenv("RS_REGION"),
-	})
-}
-
-func newComputeClient() (*gophercloud.ServiceClient, error) {
-	provider, err := newProvider()
-	if err != nil {
-		return nil, err
-	}
-
-	return rackspace.NewComputeV2(provider, gophercloud.EndpointOpts{
-		Region: os.Getenv("RS_REGION"),
-	})
-}
-
-func setup(t *testing.T) *gophercloud.ServiceClient {
-	client, err := newClient()
-	th.AssertNoErr(t, err)
-
-	return client
-}
-
-func intsToStr(ids []int) string {
-	strIDs := []string{}
-	for _, id := range ids {
-		strIDs = append(strIDs, strconv.Itoa(id))
-	}
-	return strings.Join(strIDs, ", ")
-}
diff --git a/acceptance/rackspace/lb/v1/lb_test.go b/acceptance/rackspace/lb/v1/lb_test.go
deleted file mode 100644
index c67ddec..0000000
--- a/acceptance/rackspace/lb/v1/lb_test.go
+++ /dev/null
@@ -1,214 +0,0 @@
-// +build acceptance lbs
-
-package v1
-
-import (
-	"strconv"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/vips"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestLBs(t *testing.T) {
-	client := setup(t)
-
-	ids := createLB(t, client, 3)
-	id := ids[0]
-
-	listLBProtocols(t, client)
-
-	listLBAlgorithms(t, client)
-
-	listLBs(t, client)
-
-	getLB(t, client, id)
-
-	checkLBLogging(t, client, id)
-
-	checkErrorPage(t, client, id)
-
-	getStats(t, client, id)
-
-	updateLB(t, client, id)
-
-	deleteLB(t, client, id)
-
-	batchDeleteLBs(t, client, ids[1:])
-}
-
-func createLB(t *testing.T, client *gophercloud.ServiceClient, count int) []int {
-	ids := []int{}
-
-	for i := 0; i < count; i++ {
-		opts := lbs.CreateOpts{
-			Name:     tools.RandomString("test_", 5),
-			Port:     80,
-			Protocol: "HTTP",
-			VIPs: []vips.VIP{
-				vips.VIP{Type: vips.PUBLIC},
-			},
-		}
-
-		lb, err := lbs.Create(client, opts).Extract()
-		th.AssertNoErr(t, err)
-
-		t.Logf("Created LB %d - waiting for it to build...", lb.ID)
-		waitForLB(client, lb.ID, lbs.ACTIVE)
-		t.Logf("LB %d has reached ACTIVE state", lb.ID)
-
-		ids = append(ids, lb.ID)
-	}
-
-	return ids
-}
-
-func waitForLB(client *gophercloud.ServiceClient, id int, state lbs.Status) {
-	gophercloud.WaitFor(60, func() (bool, error) {
-		lb, err := lbs.Get(client, id).Extract()
-		if err != nil {
-			return false, err
-		}
-		if lb.Status != state {
-			return false, nil
-		}
-		return true, nil
-	})
-}
-
-func listLBProtocols(t *testing.T, client *gophercloud.ServiceClient) {
-	err := lbs.ListProtocols(client).EachPage(func(page pagination.Page) (bool, error) {
-		pList, err := lbs.ExtractProtocols(page)
-		th.AssertNoErr(t, err)
-
-		for _, p := range pList {
-			t.Logf("Listing protocol: Name [%s]", p.Name)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-}
-
-func listLBAlgorithms(t *testing.T, client *gophercloud.ServiceClient) {
-	err := lbs.ListAlgorithms(client).EachPage(func(page pagination.Page) (bool, error) {
-		aList, err := lbs.ExtractAlgorithms(page)
-		th.AssertNoErr(t, err)
-
-		for _, a := range aList {
-			t.Logf("Listing algorithm: Name [%s]", a.Name)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-}
-
-func listLBs(t *testing.T, client *gophercloud.ServiceClient) {
-	err := lbs.List(client, lbs.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		lbList, err := lbs.ExtractLBs(page)
-		th.AssertNoErr(t, err)
-
-		for _, lb := range lbList {
-			t.Logf("Listing LB: ID [%d] Name [%s] Protocol [%s] Status [%s] Node count [%d] Port [%d]",
-				lb.ID, lb.Name, lb.Protocol, lb.Status, lb.NodeCount, lb.Port)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func getLB(t *testing.T, client *gophercloud.ServiceClient, id int) {
-	lb, err := lbs.Get(client, id).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Getting LB %d: Created [%s] VIPs [%#v] Logging [%#v] Persistence [%#v] SourceAddrs [%#v]",
-		lb.ID, lb.Created, lb.VIPs, lb.ConnectionLogging, lb.SessionPersistence, lb.SourceAddrs)
-}
-
-func updateLB(t *testing.T, client *gophercloud.ServiceClient, id int) {
-	opts := lbs.UpdateOpts{
-		Name:          tools.RandomString("new_", 5),
-		Protocol:      "TCP",
-		HalfClosed:    gophercloud.Enabled,
-		Algorithm:     "RANDOM",
-		Port:          8080,
-		Timeout:       100,
-		HTTPSRedirect: gophercloud.Disabled,
-	}
-
-	err := lbs.Update(client, id, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Updating LB %d - waiting for it to finish", id)
-	waitForLB(client, id, lbs.ACTIVE)
-	t.Logf("LB %d has reached ACTIVE state", id)
-}
-
-func deleteLB(t *testing.T, client *gophercloud.ServiceClient, id int) {
-	err := lbs.Delete(client, id).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Deleted LB %d", id)
-}
-
-func batchDeleteLBs(t *testing.T, client *gophercloud.ServiceClient, ids []int) {
-	err := lbs.BulkDelete(client, ids).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Deleted LB %s", intsToStr(ids))
-}
-
-func checkLBLogging(t *testing.T, client *gophercloud.ServiceClient, id int) {
-	err := lbs.EnableLogging(client, id).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Enabled logging for LB %d", id)
-
-	res, err := lbs.IsLoggingEnabled(client, id)
-	th.AssertNoErr(t, err)
-	t.Logf("LB %d log enabled? %s", id, strconv.FormatBool(res))
-
-	waitForLB(client, id, lbs.ACTIVE)
-
-	err = lbs.DisableLogging(client, id).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Disabled logging for LB %d", id)
-}
-
-func checkErrorPage(t *testing.T, client *gophercloud.ServiceClient, id int) {
-	content, err := lbs.SetErrorPage(client, id, "<html>New content!</html>").Extract()
-	t.Logf("Set error page for LB %d", id)
-
-	content, err = lbs.GetErrorPage(client, id).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Error page for LB %d: %s", id, content)
-
-	err = lbs.DeleteErrorPage(client, id).ExtractErr()
-	t.Logf("Deleted error page for LB %d", id)
-}
-
-func getStats(t *testing.T, client *gophercloud.ServiceClient, id int) {
-	waitForLB(client, id, lbs.ACTIVE)
-
-	stats, err := lbs.GetStats(client, id).Extract()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Stats for LB %d: %#v", id, stats)
-}
-
-func checkCaching(t *testing.T, client *gophercloud.ServiceClient, id int) {
-	err := lbs.EnableCaching(client, id).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Enabled caching for LB %d", id)
-
-	res, err := lbs.IsContentCached(client, id)
-	th.AssertNoErr(t, err)
-	t.Logf("Is caching enabled for LB? %s", strconv.FormatBool(res))
-
-	err = lbs.DisableCaching(client, id).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Disabled caching for LB %d", id)
-}
diff --git a/acceptance/rackspace/lb/v1/monitor_test.go b/acceptance/rackspace/lb/v1/monitor_test.go
deleted file mode 100644
index c1a8e24..0000000
--- a/acceptance/rackspace/lb/v1/monitor_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// +build acceptance lbs
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/monitors"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestMonitors(t *testing.T) {
-	client := setup(t)
-
-	ids := createLB(t, client, 1)
-	lbID := ids[0]
-
-	getMonitor(t, client, lbID)
-
-	updateMonitor(t, client, lbID)
-
-	deleteMonitor(t, client, lbID)
-
-	deleteLB(t, client, lbID)
-}
-
-func getMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	hm, err := monitors.Get(client, lbID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Health monitor for LB %d: Type [%s] Delay [%d] Timeout [%d] AttemptLimit [%d]",
-		lbID, hm.Type, hm.Delay, hm.Timeout, hm.AttemptLimit)
-}
-
-func updateMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	opts := monitors.UpdateHTTPMonitorOpts{
-		AttemptLimit: 3,
-		Delay:        10,
-		Timeout:      10,
-		BodyRegex:    "hello is it me you're looking for",
-		Path:         "/foo",
-		StatusRegex:  "200",
-		Type:         monitors.HTTP,
-	}
-
-	err := monitors.Update(client, lbID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	waitForLB(client, lbID, lbs.ACTIVE)
-	t.Logf("Updated monitor for LB %d", lbID)
-}
-
-func deleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	err := monitors.Delete(client, lbID).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	waitForLB(client, lbID, lbs.ACTIVE)
-	t.Logf("Deleted monitor for LB %d", lbID)
-}
diff --git a/acceptance/rackspace/lb/v1/node_test.go b/acceptance/rackspace/lb/v1/node_test.go
deleted file mode 100644
index 18b9fe7..0000000
--- a/acceptance/rackspace/lb/v1/node_test.go
+++ /dev/null
@@ -1,175 +0,0 @@
-// +build acceptance lbs
-
-package v1
-
-import (
-	"os"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/nodes"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestNodes(t *testing.T) {
-	client := setup(t)
-
-	serverIP := findServer(t)
-	ids := createLB(t, client, 1)
-	lbID := ids[0]
-
-	nodeID := addNodes(t, client, lbID, serverIP)
-
-	listNodes(t, client, lbID)
-
-	getNode(t, client, lbID, nodeID)
-
-	updateNode(t, client, lbID, nodeID)
-
-	listEvents(t, client, lbID)
-
-	deleteNode(t, client, lbID, nodeID)
-
-	waitForLB(client, lbID, lbs.ACTIVE)
-	deleteLB(t, client, lbID)
-}
-
-func findServer(t *testing.T) string {
-	var serverIP string
-
-	client, err := newComputeClient()
-	th.AssertNoErr(t, err)
-
-	err = servers.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
-		sList, err := servers.ExtractServers(page)
-		th.AssertNoErr(t, err)
-
-		for _, s := range sList {
-			serverIP = s.AccessIPv4
-			t.Logf("Found an existing server: ID [%s] Public IP [%s]", s.ID, serverIP)
-			break
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-
-	if serverIP == "" {
-		t.Log("No server found, creating one")
-
-		imageRef := os.Getenv("RS_IMAGE_ID")
-		if imageRef == "" {
-			t.Fatalf("OS var RS_IMAGE_ID undefined")
-		}
-		flavorRef := os.Getenv("RS_FLAVOR_ID")
-		if flavorRef == "" {
-			t.Fatalf("OS var RS_FLAVOR_ID undefined")
-		}
-
-		opts := &servers.CreateOpts{
-			Name:       tools.RandomString("lb_test_", 5),
-			ImageRef:   imageRef,
-			FlavorRef:  flavorRef,
-			DiskConfig: diskconfig.Manual,
-		}
-
-		s, err := servers.Create(client, opts).Extract()
-		th.AssertNoErr(t, err)
-		serverIP = s.AccessIPv4
-
-		t.Logf("Created server %s, waiting for it to build", s.ID)
-		err = servers.WaitForStatus(client, s.ID, "ACTIVE", 300)
-		th.AssertNoErr(t, err)
-		t.Logf("Server created successfully.")
-	}
-
-	return serverIP
-}
-
-func addNodes(t *testing.T, client *gophercloud.ServiceClient, lbID int, serverIP string) int {
-	opts := nodes.CreateOpts{
-		nodes.CreateOpt{
-			Address:   serverIP,
-			Port:      80,
-			Condition: nodes.ENABLED,
-			Type:      nodes.PRIMARY,
-		},
-	}
-
-	page := nodes.Create(client, lbID, opts)
-
-	nodeList, err := page.ExtractNodes()
-	th.AssertNoErr(t, err)
-
-	var nodeID int
-	for _, n := range nodeList {
-		nodeID = n.ID
-	}
-	if nodeID == 0 {
-		t.Fatalf("nodeID could not be extracted from create response")
-	}
-
-	t.Logf("Added node %d to LB %d", nodeID, lbID)
-	waitForLB(client, lbID, lbs.ACTIVE)
-
-	return nodeID
-}
-
-func listNodes(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	err := nodes.List(client, lbID, nil).EachPage(func(page pagination.Page) (bool, error) {
-		nodeList, err := nodes.ExtractNodes(page)
-		th.AssertNoErr(t, err)
-
-		for _, n := range nodeList {
-			t.Logf("Listing node: ID [%d] Address [%s:%d] Status [%s]", n.ID, n.Address, n.Port, n.Status)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-}
-
-func getNode(t *testing.T, client *gophercloud.ServiceClient, lbID int, nodeID int) {
-	node, err := nodes.Get(client, lbID, nodeID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Getting node %d: Type [%s] Weight [%d]", nodeID, node.Type, node.Weight)
-}
-
-func updateNode(t *testing.T, client *gophercloud.ServiceClient, lbID int, nodeID int) {
-	opts := nodes.UpdateOpts{
-		Weight:    gophercloud.IntToPointer(10),
-		Condition: nodes.DRAINING,
-		Type:      nodes.SECONDARY,
-	}
-	err := nodes.Update(client, lbID, nodeID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Updated node %d", nodeID)
-	waitForLB(client, lbID, lbs.ACTIVE)
-}
-
-func listEvents(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	pager := nodes.ListEvents(client, lbID, nodes.ListEventsOpts{})
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		eventList, err := nodes.ExtractNodeEvents(page)
-		th.AssertNoErr(t, err)
-
-		for _, e := range eventList {
-			t.Logf("Listing events for node %d: Type [%s] Msg [%s] Severity [%s] Date [%s]",
-				e.NodeID, e.Type, e.DetailedMessage, e.Severity, e.Created)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-}
-
-func deleteNode(t *testing.T, client *gophercloud.ServiceClient, lbID int, nodeID int) {
-	err := nodes.Delete(client, lbID, nodeID).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Deleted node %d", nodeID)
-}
diff --git a/acceptance/rackspace/lb/v1/session_test.go b/acceptance/rackspace/lb/v1/session_test.go
deleted file mode 100644
index 8d85655..0000000
--- a/acceptance/rackspace/lb/v1/session_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// +build acceptance lbs
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/sessions"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestSession(t *testing.T) {
-	client := setup(t)
-
-	ids := createLB(t, client, 1)
-	lbID := ids[0]
-
-	getSession(t, client, lbID)
-
-	enableSession(t, client, lbID)
-	waitForLB(client, lbID, "ACTIVE")
-
-	disableSession(t, client, lbID)
-	waitForLB(client, lbID, "ACTIVE")
-
-	deleteLB(t, client, lbID)
-}
-
-func getSession(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	sp, err := sessions.Get(client, lbID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Session config: Type [%s]", sp.Type)
-}
-
-func enableSession(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	opts := sessions.CreateOpts{Type: sessions.HTTPCOOKIE}
-	err := sessions.Enable(client, lbID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Enable %s sessions for %d", opts.Type, lbID)
-}
-
-func disableSession(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	err := sessions.Disable(client, lbID).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Disable sessions for %d", lbID)
-}
diff --git a/acceptance/rackspace/lb/v1/throttle_test.go b/acceptance/rackspace/lb/v1/throttle_test.go
deleted file mode 100644
index 1cc1235..0000000
--- a/acceptance/rackspace/lb/v1/throttle_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// +build acceptance lbs
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/throttle"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestThrottle(t *testing.T) {
-	client := setup(t)
-
-	ids := createLB(t, client, 1)
-	lbID := ids[0]
-
-	getThrottleConfig(t, client, lbID)
-
-	createThrottleConfig(t, client, lbID)
-	waitForLB(client, lbID, "ACTIVE")
-
-	deleteThrottleConfig(t, client, lbID)
-	waitForLB(client, lbID, "ACTIVE")
-
-	deleteLB(t, client, lbID)
-}
-
-func getThrottleConfig(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	sp, err := throttle.Get(client, lbID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Throttle config: MaxConns [%s]", sp.MaxConnections)
-}
-
-func createThrottleConfig(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	opts := throttle.CreateOpts{
-		MaxConnections:    200,
-		MaxConnectionRate: 100,
-		MinConnections:    0,
-		RateInterval:      10,
-	}
-
-	err := throttle.Create(client, lbID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Enable throttling for %d", lbID)
-}
-
-func deleteThrottleConfig(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	err := throttle.Delete(client, lbID).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Disable throttling for %d", lbID)
-}
diff --git a/acceptance/rackspace/lb/v1/vip_test.go b/acceptance/rackspace/lb/v1/vip_test.go
deleted file mode 100644
index bc0c2a8..0000000
--- a/acceptance/rackspace/lb/v1/vip_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// +build acceptance lbs
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/vips"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestVIPs(t *testing.T) {
-	client := setup(t)
-
-	ids := createLB(t, client, 1)
-	lbID := ids[0]
-
-	listVIPs(t, client, lbID)
-
-	vipIDs := addVIPs(t, client, lbID, 3)
-
-	deleteVIP(t, client, lbID, vipIDs[0])
-
-	bulkDeleteVIPs(t, client, lbID, vipIDs[1:])
-
-	waitForLB(client, lbID, lbs.ACTIVE)
-	deleteLB(t, client, lbID)
-}
-
-func listVIPs(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
-	err := vips.List(client, lbID).EachPage(func(page pagination.Page) (bool, error) {
-		vipList, err := vips.ExtractVIPs(page)
-		th.AssertNoErr(t, err)
-
-		for _, vip := range vipList {
-			t.Logf("Listing VIP: ID [%s] Address [%s] Type [%s] Version [%s]",
-				vip.ID, vip.Address, vip.Type, vip.Version)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-}
-
-func addVIPs(t *testing.T, client *gophercloud.ServiceClient, lbID, count int) []int {
-	ids := []int{}
-
-	for i := 0; i < count; i++ {
-		opts := vips.CreateOpts{
-			Type:    vips.PUBLIC,
-			Version: vips.IPV6,
-		}
-
-		vip, err := vips.Create(client, lbID, opts).Extract()
-		th.AssertNoErr(t, err)
-
-		t.Logf("Created VIP %d", vip.ID)
-
-		waitForLB(client, lbID, lbs.ACTIVE)
-
-		ids = append(ids, vip.ID)
-	}
-
-	return ids
-}
-
-func deleteVIP(t *testing.T, client *gophercloud.ServiceClient, lbID, vipID int) {
-	err := vips.Delete(client, lbID, vipID).ExtractErr()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Deleted VIP %d", vipID)
-
-	waitForLB(client, lbID, lbs.ACTIVE)
-}
-
-func bulkDeleteVIPs(t *testing.T, client *gophercloud.ServiceClient, lbID int, ids []int) {
-	err := vips.BulkDelete(client, lbID, ids).ExtractErr()
-	th.AssertNoErr(t, err)
-	t.Logf("Deleted VIPs %s", intsToStr(ids))
-}
diff --git a/acceptance/rackspace/networking/v2/common.go b/acceptance/rackspace/networking/v2/common.go
deleted file mode 100644
index 8170418..0000000
--- a/acceptance/rackspace/networking/v2/common.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package v2
-
-import (
-	"os"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-var Client *gophercloud.ServiceClient
-
-func NewClient() (*gophercloud.ServiceClient, error) {
-	opts, err := rackspace.AuthOptionsFromEnv()
-	if err != nil {
-		return nil, err
-	}
-
-	provider, err := rackspace.AuthenticatedClient(opts)
-	if err != nil {
-		return nil, err
-	}
-
-	return rackspace.NewNetworkV2(provider, gophercloud.EndpointOpts{
-		Name:   "cloudNetworks",
-		Region: os.Getenv("RS_REGION"),
-	})
-}
-
-func Setup(t *testing.T) {
-	client, err := NewClient()
-	th.AssertNoErr(t, err)
-	Client = client
-}
-
-func Teardown() {
-	Client = nil
-}
diff --git a/acceptance/rackspace/networking/v2/network_test.go b/acceptance/rackspace/networking/v2/network_test.go
deleted file mode 100644
index 3862123..0000000
--- a/acceptance/rackspace/networking/v2/network_test.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// +build acceptance networking
-
-package v2
-
-import (
-	"strconv"
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/networking/v2/networks"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestNetworkCRUDOperations(t *testing.T) {
-	Setup(t)
-	defer Teardown()
-
-	// Create a network
-	n, err := networks.Create(Client, os.CreateOpts{Name: "sample_network", AdminStateUp: os.Up}).Extract()
-	th.AssertNoErr(t, err)
-	defer networks.Delete(Client, n.ID)
-	th.AssertEquals(t, "sample_network", n.Name)
-	th.AssertEquals(t, true, n.AdminStateUp)
-	networkID := n.ID
-
-	// List networks
-	pager := networks.List(Client, os.ListOpts{Limit: 2})
-	err = pager.EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page ---")
-
-		networkList, err := os.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, "ACTIVE", n.Status)
-	th.AssertDeepEquals(t, []string{}, n.Subnets)
-	th.AssertEquals(t, "sample_network", n.Name)
-	th.AssertEquals(t, true, n.AdminStateUp)
-	th.AssertEquals(t, false, n.Shared)
-	th.AssertEquals(t, networkID, n.ID)
-
-	// Update network
-	n, err = networks.Update(Client, networkID, os.UpdateOpts{Name: "new_network_name"}).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, "new_network_name", n.Name)
-
-	// Delete network
-	res := networks.Delete(Client, networkID)
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/acceptance/rackspace/networking/v2/port_test.go b/acceptance/rackspace/networking/v2/port_test.go
deleted file mode 100644
index 3c42bb2..0000000
--- a/acceptance/rackspace/networking/v2/port_test.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// +build acceptance networking
-
-package v2
-
-import (
-	"testing"
-
-	osNetworks "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-	osPorts "github.com/rackspace/gophercloud/openstack/networking/v2/ports"
-	osSubnets "github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/networking/v2/networks"
-	"github.com/rackspace/gophercloud/rackspace/networking/v2/ports"
-	"github.com/rackspace/gophercloud/rackspace/networking/v2/subnets"
-	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, portID, p.ID)
-
-	// Update port
-	p, err = ports.Update(Client, portID, osPorts.UpdateOpts{Name: "new_port_name"}).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, "new_port_name", p.Name)
-
-	// Delete port
-	res := ports.Delete(Client, portID)
-	th.AssertNoErr(t, res.Err)
-}
-
-func createPort(t *testing.T, networkID, subnetID string) string {
-	enable := true
-	opts := osPorts.CreateOpts{
-		NetworkID:    networkID,
-		Name:         "my_port",
-		AdminStateUp: &enable,
-		FixedIPs:     []osPorts.IP{osPorts.IP{SubnetID: subnetID}},
-	}
-	p, err := ports.Create(Client, opts).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, networkID, p.NetworkID)
-	th.AssertEquals(t, "my_port", p.Name)
-	th.AssertEquals(t, true, p.AdminStateUp)
-
-	return p.ID
-}
-
-func listPorts(t *testing.T) {
-	count := 0
-	pager := ports.List(Client, osPorts.ListOpts{})
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		t.Logf("--- Page ---")
-
-		portList, err := osPorts.ExtractPorts(page)
-		th.AssertNoErr(t, err)
-
-		for _, p := range portList {
-			t.Logf("Port: ID [%s] Name [%s] Status [%s] 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, osNetworks.CreateOpts{Name: "tmp_network", AdminStateUp: osNetworks.Up}).Extract()
-	return res.ID, err
-}
-
-func createSubnet(networkID string) (string, error) {
-	s, err := subnets.Create(Client, osSubnets.CreateOpts{
-		NetworkID:  networkID,
-		CIDR:       "192.168.199.0/24",
-		IPVersion:  osSubnets.IPv4,
-		Name:       "my_subnet",
-		EnableDHCP: osSubnets.Down,
-	}).Extract()
-	return s.ID, err
-}
diff --git a/acceptance/rackspace/networking/v2/security_test.go b/acceptance/rackspace/networking/v2/security_test.go
deleted file mode 100644
index ec02991..0000000
--- a/acceptance/rackspace/networking/v2/security_test.go
+++ /dev/null
@@ -1,165 +0,0 @@
-// +build acceptance networking security
-
-package v2
-
-import (
-	"testing"
-
-	osGroups "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups"
-	osRules "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
-	osNetworks "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-	osPorts "github.com/rackspace/gophercloud/openstack/networking/v2/ports"
-	"github.com/rackspace/gophercloud/pagination"
-	rsNetworks "github.com/rackspace/gophercloud/rackspace/networking/v2/networks"
-	rsPorts "github.com/rackspace/gophercloud/rackspace/networking/v2/ports"
-	rsGroups "github.com/rackspace/gophercloud/rackspace/networking/v2/security/groups"
-	rsRules "github.com/rackspace/gophercloud/rackspace/networking/v2/security/rules"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestSecurityGroups(t *testing.T) {
-	Setup(t)
-	defer Teardown()
-
-	// create security group
-	groupID := createSecGroup(t)
-
-	// delete security group
-	defer deleteSecGroup(t, groupID)
-
-	// list security group
-	listSecGroups(t)
-
-	// get security group
-	getSecGroup(t, groupID)
-}
-
-func TestSecurityGroupRules(t *testing.T) {
-	Setup(t)
-	defer Teardown()
-
-	// create security group
-	groupID := createSecGroup(t)
-
-	defer deleteSecGroup(t, groupID)
-
-	// create security group rule
-	ruleID := createSecRule(t, groupID)
-
-	// delete security group rule
-	defer deleteSecRule(t, ruleID)
-
-	// list security group rule
-	listSecRules(t)
-
-	// get security group rule
-	getSecRule(t, ruleID)
-}
-
-func createSecGroup(t *testing.T) string {
-	sg, err := rsGroups.Create(Client, osGroups.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 := rsGroups.List(Client, osGroups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		list, err := osGroups.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 := rsGroups.Get(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 createSecGroupPort(t *testing.T, groupID string) (string, string) {
-	n, err := rsNetworks.Create(Client, osNetworks.CreateOpts{Name: "tmp_network"}).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created network %s", n.ID)
-
-	opts := osPorts.CreateOpts{
-		NetworkID:      n.ID,
-		Name:           "my_port",
-		SecurityGroups: []string{groupID},
-	}
-	p, err := rsPorts.Create(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 := rsGroups.Delete(Client, groupID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted security group %s", groupID)
-}
-
-func createSecRule(t *testing.T, groupID string) string {
-	r, err := rsRules.Create(Client, osRules.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 := rsRules.List(Client, osRules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		list, err := osRules.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 := rsRules.Get(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 := rsRules.Delete(Client, id)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted security rule %s", id)
-}
diff --git a/acceptance/rackspace/networking/v2/subnet_test.go b/acceptance/rackspace/networking/v2/subnet_test.go
deleted file mode 100644
index c401432..0000000
--- a/acceptance/rackspace/networking/v2/subnet_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// +build acceptance networking
-
-package v2
-
-import (
-	"testing"
-
-	osNetworks "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-	osSubnets "github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/networking/v2/networks"
-	"github.com/rackspace/gophercloud/rackspace/networking/v2/subnets"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestListSubnets(t *testing.T) {
-	Setup(t)
-	defer Teardown()
-
-	pager := subnets.List(Client, osSubnets.ListOpts{Limit: 2})
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page ---")
-
-		subnetList, err := osSubnets.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 TestSubnetCRUD(t *testing.T) {
-	Setup(t)
-	defer Teardown()
-
-	// Setup network
-	t.Log("Setting up network")
-	n, err := networks.Create(Client, osNetworks.CreateOpts{Name: "tmp_network", AdminStateUp: osNetworks.Up}).Extract()
-	th.AssertNoErr(t, err)
-	networkID := n.ID
-	defer networks.Delete(Client, networkID)
-
-	// Create subnet
-	t.Log("Create subnet")
-	enable := false
-	opts := osSubnets.CreateOpts{
-		NetworkID:  networkID,
-		CIDR:       "192.168.199.0/24",
-		IPVersion:  osSubnets.IPv4,
-		Name:       "my_subnet",
-		EnableDHCP: &enable,
-	}
-	s, err := subnets.Create(Client, opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, networkID, s.NetworkID)
-	th.AssertEquals(t, "192.168.199.0/24", s.CIDR)
-	th.AssertEquals(t, 4, s.IPVersion)
-	th.AssertEquals(t, "my_subnet", s.Name)
-	th.AssertEquals(t, false, s.EnableDHCP)
-	subnetID := s.ID
-
-	// Get subnet
-	t.Log("Getting subnet")
-	s, err = subnets.Get(Client, subnetID).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, subnetID, s.ID)
-
-	// Update subnet
-	t.Log("Update subnet")
-	s, err = subnets.Update(Client, subnetID, osSubnets.UpdateOpts{Name: "new_subnet_name"}).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, "new_subnet_name", s.Name)
-
-	// Delete subnet
-	t.Log("Delete subnet")
-	res := subnets.Delete(Client, subnetID)
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/acceptance/rackspace/objectstorage/v1/accounts_test.go b/acceptance/rackspace/objectstorage/v1/accounts_test.go
deleted file mode 100644
index 8b3cde4..0000000
--- a/acceptance/rackspace/objectstorage/v1/accounts_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// +build acceptance rackspace
-
-package v1
-
-import (
-	"testing"
-
-	raxAccounts "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/accounts"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestAccounts(t *testing.T) {
-	c, err := createClient(t, false)
-	th.AssertNoErr(t, err)
-
-	updateHeaders, err := raxAccounts.Update(c, raxAccounts.UpdateOpts{Metadata: map[string]string{"white": "mountains"}}).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Update Account Response Headers: %+v\n", updateHeaders)
-	defer func() {
-		updateres := raxAccounts.Update(c, raxAccounts.UpdateOpts{Metadata: map[string]string{"white": ""}})
-		th.AssertNoErr(t, updateres.Err)
-		metadata, err := raxAccounts.Get(c).ExtractMetadata()
-		th.AssertNoErr(t, err)
-		t.Logf("Metadata from Get Account request (after update reverted): %+v\n", metadata)
-		th.CheckEquals(t, metadata["White"], "")
-	}()
-
-	getResp := raxAccounts.Get(c)
-	th.AssertNoErr(t, getResp.Err)
-
-	getHeaders, _ := getResp.Extract()
-	t.Logf("Get Account Response Headers: %+v\n", getHeaders)
-
-	metadata, _ := getResp.ExtractMetadata()
-	t.Logf("Metadata from Get Account request (after update): %+v\n", metadata)
-
-	th.CheckEquals(t, metadata["White"], "mountains")
-}
diff --git a/acceptance/rackspace/objectstorage/v1/bulk_test.go b/acceptance/rackspace/objectstorage/v1/bulk_test.go
deleted file mode 100644
index 79013a5..0000000
--- a/acceptance/rackspace/objectstorage/v1/bulk_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// +build acceptance rackspace objectstorage v1
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/rackspace/objectstorage/v1/bulk"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestBulk(t *testing.T) {
-	c, err := createClient(t, false)
-	th.AssertNoErr(t, err)
-
-	var options bulk.DeleteOpts
-	options = append(options, "container/object1")
-	res := bulk.Delete(c, options)
-	th.AssertNoErr(t, res.Err)
-	body, err := res.ExtractBody()
-	th.AssertNoErr(t, err)
-	t.Logf("Response body from Bulk Delete Request: %+v\n", body)
-}
diff --git a/acceptance/rackspace/objectstorage/v1/cdncontainers_test.go b/acceptance/rackspace/objectstorage/v1/cdncontainers_test.go
deleted file mode 100644
index 0f56f49..0000000
--- a/acceptance/rackspace/objectstorage/v1/cdncontainers_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// +build acceptance rackspace objectstorage v1
-
-package v1
-
-import (
-	"testing"
-
-	osContainers "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
-	"github.com/rackspace/gophercloud/pagination"
-	raxCDNContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/cdncontainers"
-	raxContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/containers"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestCDNContainers(t *testing.T) {
-	raxClient, err := createClient(t, false)
-	th.AssertNoErr(t, err)
-
-	createres := raxContainers.Create(raxClient, "gophercloud-test", nil)
-	th.AssertNoErr(t, createres.Err)
-	t.Logf("Headers from Create Container request: %+v\n", createres.Header)
-	defer func() {
-		res := raxContainers.Delete(raxClient, "gophercloud-test")
-		th.AssertNoErr(t, res.Err)
-	}()
-
-	raxCDNClient, err := createClient(t, true)
-	th.AssertNoErr(t, err)
-	enableRes := raxCDNContainers.Enable(raxCDNClient, "gophercloud-test", raxCDNContainers.EnableOpts{CDNEnabled: true, TTL: 900})
-	t.Logf("Header map from Enable CDN Container request: %+v\n", enableRes.Header)
-	enableHeader, err := enableRes.Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Headers from Enable CDN Container request: %+v\n", enableHeader)
-
-	t.Logf("Container Names available to the currently issued token:")
-	count := 0
-	err = raxCDNContainers.List(raxCDNClient, &osContainers.ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page %02d ---", count)
-
-		names, err := raxCDNContainers.ExtractNames(page)
-		th.AssertNoErr(t, err)
-
-		for i, name := range names {
-			t.Logf("[%02d] %s", i, name)
-		}
-
-		count++
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	if count == 0 {
-		t.Errorf("No CDN containers listed for your current token.")
-	}
-
-	updateOpts := raxCDNContainers.UpdateOpts{XCDNEnabled: raxCDNContainers.Disabled, XLogRetention: raxCDNContainers.Enabled}
-	updateHeader, err := raxCDNContainers.Update(raxCDNClient, "gophercloud-test", updateOpts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Headers from Update CDN Container request: %+v\n", updateHeader)
-
-	getRes := raxCDNContainers.Get(raxCDNClient, "gophercloud-test")
-	getHeader, err := getRes.Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Headers from Get CDN Container request (after update): %+v\n", getHeader)
-	metadata, err := getRes.ExtractMetadata()
-	t.Logf("Metadata from Get CDN Container request (after update): %+v\n", metadata)
-}
diff --git a/acceptance/rackspace/objectstorage/v1/cdnobjects_test.go b/acceptance/rackspace/objectstorage/v1/cdnobjects_test.go
deleted file mode 100644
index 0c0ab8a..0000000
--- a/acceptance/rackspace/objectstorage/v1/cdnobjects_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// +build acceptance rackspace objectstorage v1
-
-package v1
-
-import (
-	"bytes"
-	"testing"
-
-	raxCDNContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/cdncontainers"
-	raxCDNObjects "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/cdnobjects"
-	raxContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/containers"
-	raxObjects "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/objects"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestCDNObjects(t *testing.T) {
-	raxClient, err := createClient(t, false)
-	th.AssertNoErr(t, err)
-
-	createContResult := raxContainers.Create(raxClient, "gophercloud-test", nil)
-	th.AssertNoErr(t, createContResult.Err)
-	t.Logf("Headers from Create Container request: %+v\n", createContResult.Header)
-	defer func() {
-		deleteResult := raxContainers.Delete(raxClient, "gophercloud-test")
-		th.AssertNoErr(t, deleteResult.Err)
-	}()
-
-	header, err := raxObjects.Create(raxClient, "gophercloud-test", "test-object", bytes.NewBufferString("gophercloud cdn test"), nil).ExtractHeader()
-	th.AssertNoErr(t, err)
-	t.Logf("Headers from Create Object request: %+v\n", header)
-	defer func() {
-		deleteResult := raxObjects.Delete(raxClient, "gophercloud-test", "test-object", nil)
-		th.AssertNoErr(t, deleteResult.Err)
-	}()
-
-	raxCDNClient, err := createClient(t, true)
-	th.AssertNoErr(t, err)
-
-	enableHeader, err := raxCDNContainers.Enable(raxCDNClient, "gophercloud-test", raxCDNContainers.EnableOpts{CDNEnabled: true, TTL: 900}).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Headers from Enable CDN Container request: %+v\n", enableHeader)
-
-	objCDNURL, err := raxCDNObjects.CDNURL(raxCDNClient, "gophercloud-test", "test-object")
-	th.AssertNoErr(t, err)
-	t.Logf("%s CDN URL: %s\n", "test_object", objCDNURL)
-
-	deleteHeader, err := raxCDNObjects.Delete(raxCDNClient, "gophercloud-test", "test-object", nil).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Headers from Delete CDN Object request: %+v\n", deleteHeader)
-}
diff --git a/acceptance/rackspace/objectstorage/v1/common.go b/acceptance/rackspace/objectstorage/v1/common.go
deleted file mode 100644
index 1ae0727..0000000
--- a/acceptance/rackspace/objectstorage/v1/common.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// +build acceptance rackspace objectstorage v1
-
-package v1
-
-import (
-	"os"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/acceptance/tools"
-	"github.com/rackspace/gophercloud/rackspace"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func rackspaceAuthOptions(t *testing.T) gophercloud.AuthOptions {
-	// Obtain credentials from the environment.
-	options, err := rackspace.AuthOptionsFromEnv()
-	th.AssertNoErr(t, err)
-	options = tools.OnlyRS(options)
-
-	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_API_KEY.")
-	}
-
-	return options
-}
-
-func createClient(t *testing.T, cdn bool) (*gophercloud.ServiceClient, error) {
-	region := os.Getenv("RS_REGION")
-	if region == "" {
-		t.Fatal("Please provide a Rackspace region as RS_REGION")
-	}
-
-	ao := rackspaceAuthOptions(t)
-
-	provider, err := rackspace.NewClient(ao.IdentityEndpoint)
-	th.AssertNoErr(t, err)
-
-	err = rackspace.Authenticate(provider, ao)
-	th.AssertNoErr(t, err)
-
-	if cdn {
-		return rackspace.NewObjectCDNV1(provider, gophercloud.EndpointOpts{
-			Region: region,
-		})
-	}
-
-	return rackspace.NewObjectStorageV1(provider, gophercloud.EndpointOpts{
-		Region: region,
-	})
-}
diff --git a/acceptance/rackspace/objectstorage/v1/containers_test.go b/acceptance/rackspace/objectstorage/v1/containers_test.go
deleted file mode 100644
index c895513..0000000
--- a/acceptance/rackspace/objectstorage/v1/containers_test.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// +build acceptance rackspace objectstorage v1
-
-package v1
-
-import (
-	"testing"
-
-	osContainers "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
-	"github.com/rackspace/gophercloud/pagination"
-	raxContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/containers"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestContainers(t *testing.T) {
-	c, err := createClient(t, false)
-	th.AssertNoErr(t, err)
-
-	t.Logf("Containers Info available to the currently issued token:")
-	count := 0
-	err = raxContainers.List(c, &osContainers.ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page %02d ---", count)
-
-		containers, err := raxContainers.ExtractInfo(page)
-		th.AssertNoErr(t, err)
-
-		for i, container := range containers {
-			t.Logf("[%02d]      name=[%s]", i, container.Name)
-			t.Logf("            count=[%d]", container.Count)
-			t.Logf("            bytes=[%d]", container.Bytes)
-		}
-
-		count++
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	if count == 0 {
-		t.Errorf("No containers listed for your current token.")
-	}
-
-	t.Logf("Container Names available to the currently issued token:")
-	count = 0
-	err = raxContainers.List(c, &osContainers.ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page %02d ---", count)
-
-		names, err := raxContainers.ExtractNames(page)
-		th.AssertNoErr(t, err)
-
-		for i, name := range names {
-			t.Logf("[%02d] %s", i, name)
-		}
-
-		count++
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	if count == 0 {
-		t.Errorf("No containers listed for your current token.")
-	}
-
-	createHeader, err := raxContainers.Create(c, "gophercloud-test", nil).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Headers from Create Container request: %+v\n", createHeader)
-	defer func() {
-		deleteres := raxContainers.Delete(c, "gophercloud-test")
-		deleteHeader, err := deleteres.Extract()
-		th.AssertNoErr(t, err)
-		t.Logf("Headers from Delete Container request: %+v\n", deleteres.Header)
-		t.Logf("Headers from Delete Container request: %+v\n", deleteHeader)
-	}()
-
-	updateHeader, err := raxContainers.Update(c, "gophercloud-test", raxContainers.UpdateOpts{Metadata: map[string]string{"white": "mountains"}}).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Headers from Update Container request: %+v\n", updateHeader)
-	defer func() {
-		res := raxContainers.Update(c, "gophercloud-test", raxContainers.UpdateOpts{Metadata: map[string]string{"white": ""}})
-		th.AssertNoErr(t, res.Err)
-		metadata, err := raxContainers.Get(c, "gophercloud-test").ExtractMetadata()
-		th.AssertNoErr(t, err)
-		t.Logf("Metadata from Get Container request (after update reverted): %+v\n", metadata)
-		th.CheckEquals(t, metadata["White"], "")
-	}()
-
-	getres := raxContainers.Get(c, "gophercloud-test")
-	getHeader, err := getres.Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Headers from Get Container request (after update): %+v\n", getHeader)
-	metadata, err := getres.ExtractMetadata()
-	t.Logf("Metadata from Get Container request (after update): %+v\n", metadata)
-	th.CheckEquals(t, metadata["White"], "mountains")
-}
diff --git a/acceptance/rackspace/objectstorage/v1/objects_test.go b/acceptance/rackspace/objectstorage/v1/objects_test.go
deleted file mode 100644
index 585dea7..0000000
--- a/acceptance/rackspace/objectstorage/v1/objects_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// +build acceptance rackspace objectstorage v1
-
-package v1
-
-import (
-	"bytes"
-	"testing"
-
-	osObjects "github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
-	"github.com/rackspace/gophercloud/pagination"
-	raxContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/containers"
-	raxObjects "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/objects"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestObjects(t *testing.T) {
-	c, err := createClient(t, false)
-	th.AssertNoErr(t, err)
-
-	res := raxContainers.Create(c, "gophercloud-test", nil)
-	th.AssertNoErr(t, res.Err)
-
-	defer func() {
-		t.Logf("Deleting container...")
-		res := raxContainers.Delete(c, "gophercloud-test")
-		th.AssertNoErr(t, res.Err)
-	}()
-
-	content := bytes.NewBufferString("Lewis Carroll")
-	options := &osObjects.CreateOpts{ContentType: "text/plain"}
-	createres := raxObjects.Create(c, "gophercloud-test", "o1", content, options)
-	th.AssertNoErr(t, createres.Err)
-
-	defer func() {
-		t.Logf("Deleting object o1...")
-		res := raxObjects.Delete(c, "gophercloud-test", "o1", nil)
-		th.AssertNoErr(t, res.Err)
-	}()
-
-	t.Logf("Objects Info available to the currently issued token:")
-	count := 0
-	err = raxObjects.List(c, "gophercloud-test", &osObjects.ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page %02d ---", count)
-
-		objects, err := raxObjects.ExtractInfo(page)
-		th.AssertNoErr(t, err)
-
-		for i, object := range objects {
-			t.Logf("[%02d]      name=[%s]", i, object.Name)
-			t.Logf("            content-type=[%s]", object.ContentType)
-			t.Logf("            bytes=[%d]", object.Bytes)
-			t.Logf("            last-modified=[%s]", object.LastModified)
-			t.Logf("            hash=[%s]", object.Hash)
-		}
-
-		count++
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	if count == 0 {
-		t.Errorf("No objects listed for your current token.")
-	}
-	t.Logf("Container Names available to the currently issued token:")
-	count = 0
-	err = raxObjects.List(c, "gophercloud-test", &osObjects.ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page %02d ---", count)
-
-		names, err := raxObjects.ExtractNames(page)
-		th.AssertNoErr(t, err)
-
-		for i, name := range names {
-			t.Logf("[%02d] %s", i, name)
-		}
-
-		count++
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	if count == 0 {
-		t.Errorf("No objects listed for your current token.")
-	}
-
-	copyres := raxObjects.Copy(c, "gophercloud-test", "o1", &raxObjects.CopyOpts{Destination: "gophercloud-test/o2"})
-	th.AssertNoErr(t, copyres.Err)
-	defer func() {
-		t.Logf("Deleting object o2...")
-		res := raxObjects.Delete(c, "gophercloud-test", "o2", nil)
-		th.AssertNoErr(t, res.Err)
-	}()
-
-	o1Content, err := raxObjects.Download(c, "gophercloud-test", "o1", nil).ExtractContent()
-	th.AssertNoErr(t, err)
-	o2Content, err := raxObjects.Download(c, "gophercloud-test", "o2", nil).ExtractContent()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, string(o2Content), string(o1Content))
-
-	updateres := raxObjects.Update(c, "gophercloud-test", "o2", osObjects.UpdateOpts{Metadata: map[string]string{"white": "mountains"}})
-	th.AssertNoErr(t, updateres.Err)
-	t.Logf("Headers from Update Account request: %+v\n", updateres.Header)
-	defer func() {
-		res := raxObjects.Update(c, "gophercloud-test", "o2", osObjects.UpdateOpts{Metadata: map[string]string{"white": ""}})
-		th.AssertNoErr(t, res.Err)
-		metadata, err := raxObjects.Get(c, "gophercloud-test", "o2", nil).ExtractMetadata()
-		th.AssertNoErr(t, err)
-		t.Logf("Metadata from Get Account request (after update reverted): %+v\n", metadata)
-		th.CheckEquals(t, "", metadata["White"])
-	}()
-
-	getres := raxObjects.Get(c, "gophercloud-test", "o2", nil)
-	th.AssertNoErr(t, getres.Err)
-	t.Logf("Headers from Get Account request (after update): %+v\n", getres.Header)
-	metadata, err := getres.ExtractMetadata()
-	th.AssertNoErr(t, err)
-	t.Logf("Metadata from Get Account request (after update): %+v\n", metadata)
-	th.CheckEquals(t, "mountains", metadata["White"])
-
-	createTempURLOpts := osObjects.CreateTempURLOpts{
-		Method: osObjects.GET,
-		TTL:    600,
-	}
-	tempURL, err := raxObjects.CreateTempURL(c, "gophercloud-test", "o1", createTempURLOpts)
-	th.AssertNoErr(t, err)
-	t.Logf("TempURL for object (%s): %s", "o1", tempURL)
-}
diff --git a/acceptance/rackspace/orchestration/v1/buildinfo_test.go b/acceptance/rackspace/orchestration/v1/buildinfo_test.go
deleted file mode 100644
index 42cc048..0000000
--- a/acceptance/rackspace/orchestration/v1/buildinfo_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/rackspace/orchestration/v1/buildinfo"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestBuildInfo(t *testing.T) {
-	// Create a provider client for making the HTTP requests.
-	// See common.go in this directory for more information.
-	client := newClient(t)
-
-	bi, err := buildinfo.Get(client).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("retrieved build info: %+v\n", bi)
-}
diff --git a/acceptance/rackspace/orchestration/v1/common.go b/acceptance/rackspace/orchestration/v1/common.go
deleted file mode 100644
index b9d5197..0000000
--- a/acceptance/rackspace/orchestration/v1/common.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"fmt"
-	"os"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-var template = fmt.Sprintf(`
-{
-		"heat_template_version": "2013-05-23",
-		"description": "Simple template to test heat commands",
-		"parameters": {},
-		"resources": {
-				"hello_world": {
-						"type":"OS::Nova::Server",
-						"properties": {
-								"flavor": "%s",
-								"image": "%s",
-								"user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-						}
-				}
-		}
-}
-`, os.Getenv("RS_FLAVOR_ID"), os.Getenv("RS_IMAGE_ID"))
-
-func newClient(t *testing.T) *gophercloud.ServiceClient {
-	ao, err := rackspace.AuthOptionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	client, err := rackspace.AuthenticatedClient(ao)
-	th.AssertNoErr(t, err)
-
-	c, err := rackspace.NewOrchestrationV1(client, gophercloud.EndpointOpts{
-		Region: os.Getenv("RS_REGION_NAME"),
-	})
-	th.AssertNoErr(t, err)
-	return c
-}
diff --git a/acceptance/rackspace/orchestration/v1/stackevents_test.go b/acceptance/rackspace/orchestration/v1/stackevents_test.go
deleted file mode 100644
index 9e3fc08..0000000
--- a/acceptance/rackspace/orchestration/v1/stackevents_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	osStackEvents "github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents"
-	osStacks "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stackevents"
-	"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stacks"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestStackEvents(t *testing.T) {
-	// Create a provider client for making the HTTP requests.
-	// See common.go in this directory for more information.
-	client := newClient(t)
-
-	stackName := "postman_stack_2"
-	resourceName := "hello_world"
-	var eventID string
-
-	createOpts := osStacks.CreateOpts{
-		Name:     stackName,
-		Template: template,
-		Timeout:  5,
-	}
-	stack, err := stacks.Create(client, createOpts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created stack: %+v\n", stack)
-	defer func() {
-		err := stacks.Delete(client, stackName, stack.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-		t.Logf("Deleted stack (%s)", stackName)
-	}()
-	err = gophercloud.WaitFor(60, func() (bool, error) {
-		getStack, err := stacks.Get(client, stackName, stack.ID).Extract()
-		if err != nil {
-			return false, err
-		}
-		if getStack.Status == "CREATE_COMPLETE" {
-			return true, nil
-		}
-		return false, nil
-	})
-
-	err = stackevents.List(client, stackName, stack.ID, nil).EachPage(func(page pagination.Page) (bool, error) {
-		events, err := osStackEvents.ExtractEvents(page)
-		th.AssertNoErr(t, err)
-		t.Logf("listed events: %+v\n", events)
-		eventID = events[0].ID
-		return false, nil
-	})
-	th.AssertNoErr(t, err)
-
-	err = stackevents.ListResourceEvents(client, stackName, stack.ID, resourceName, nil).EachPage(func(page pagination.Page) (bool, error) {
-		resourceEvents, err := osStackEvents.ExtractResourceEvents(page)
-		th.AssertNoErr(t, err)
-		t.Logf("listed resource events: %+v\n", resourceEvents)
-		return false, nil
-	})
-	th.AssertNoErr(t, err)
-
-	event, err := stackevents.Get(client, stackName, stack.ID, resourceName, eventID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("retrieved event: %+v\n", event)
-}
diff --git a/acceptance/rackspace/orchestration/v1/stackresources_test.go b/acceptance/rackspace/orchestration/v1/stackresources_test.go
deleted file mode 100644
index 65926e7..0000000
--- a/acceptance/rackspace/orchestration/v1/stackresources_test.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	osStackResources "github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources"
-	osStacks "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stackresources"
-	"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stacks"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestStackResources(t *testing.T) {
-	// Create a provider client for making the HTTP requests.
-	// See common.go in this directory for more information.
-	client := newClient(t)
-
-	stackName := "postman_stack_2"
-
-	createOpts := osStacks.CreateOpts{
-		Name:     stackName,
-		Template: template,
-		Timeout:  5,
-	}
-	stack, err := stacks.Create(client, createOpts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created stack: %+v\n", stack)
-	defer func() {
-		err := stacks.Delete(client, stackName, stack.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-		t.Logf("Deleted stack (%s)", stackName)
-	}()
-	err = gophercloud.WaitFor(60, func() (bool, error) {
-		getStack, err := stacks.Get(client, stackName, stack.ID).Extract()
-		if err != nil {
-			return false, err
-		}
-		if getStack.Status == "CREATE_COMPLETE" {
-			return true, nil
-		}
-		return false, nil
-	})
-
-	resourceName := "hello_world"
-	resource, err := stackresources.Get(client, stackName, stack.ID, resourceName).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Got stack resource: %+v\n", resource)
-
-	metadata, err := stackresources.Metadata(client, stackName, stack.ID, resourceName).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Got stack resource metadata: %+v\n", metadata)
-
-	err = stackresources.List(client, stackName, stack.ID, nil).EachPage(func(page pagination.Page) (bool, error) {
-		resources, err := osStackResources.ExtractResources(page)
-		th.AssertNoErr(t, err)
-		t.Logf("resources: %+v\n", resources)
-		return false, nil
-	})
-	th.AssertNoErr(t, err)
-}
diff --git a/acceptance/rackspace/orchestration/v1/stacks_test.go b/acceptance/rackspace/orchestration/v1/stacks_test.go
deleted file mode 100644
index 61969b5..0000000
--- a/acceptance/rackspace/orchestration/v1/stacks_test.go
+++ /dev/null
@@ -1,154 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	osStacks "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stacks"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestStacks(t *testing.T) {
-	// Create a provider client for making the HTTP requests.
-	// See common.go in this directory for more information.
-	client := newClient(t)
-
-	stackName1 := "gophercloud-test-stack-2"
-	createOpts := osStacks.CreateOpts{
-		Name:     stackName1,
-		Template: template,
-		Timeout:  5,
-	}
-	stack, err := stacks.Create(client, createOpts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created stack: %+v\n", stack)
-	defer func() {
-		err := stacks.Delete(client, stackName1, stack.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-		t.Logf("Deleted stack (%s)", stackName1)
-	}()
-	err = gophercloud.WaitFor(60, func() (bool, error) {
-		getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
-		if err != nil {
-			return false, err
-		}
-		if getStack.Status == "CREATE_COMPLETE" {
-			return true, nil
-		}
-		return false, nil
-	})
-
-	updateOpts := osStacks.UpdateOpts{
-		Template: template,
-		Timeout:  20,
-	}
-	err = stacks.Update(client, stackName1, stack.ID, updateOpts).ExtractErr()
-	th.AssertNoErr(t, err)
-	err = gophercloud.WaitFor(60, func() (bool, error) {
-		getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
-		if err != nil {
-			return false, err
-		}
-		if getStack.Status == "UPDATE_COMPLETE" {
-			return true, nil
-		}
-		return false, nil
-	})
-
-	t.Logf("Updated stack")
-
-	err = stacks.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
-		stackList, err := osStacks.ExtractStacks(page)
-		th.AssertNoErr(t, err)
-
-		t.Logf("Got stack list: %+v\n", stackList)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-
-	getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Got stack: %+v\n", getStack)
-
-	abandonedStack, err := stacks.Abandon(client, stackName1, stack.ID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Abandonded stack %+v\n", abandonedStack)
-	th.AssertNoErr(t, err)
-}
-
-// Test using the updated interface
-func TestStacksNewTemplateFormat(t *testing.T) {
-	// Create a provider client for making the HTTP requests.
-	// See common.go in this directory for more information.
-	client := newClient(t)
-
-	stackName1 := "gophercloud-test-stack-2"
-	templateOpts := new(osStacks.Template)
-	templateOpts.Bin = []byte(template)
-	createOpts := osStacks.CreateOpts{
-		Name:         stackName1,
-		TemplateOpts: templateOpts,
-		Timeout:      5,
-	}
-	stack, err := stacks.Create(client, createOpts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created stack: %+v\n", stack)
-	defer func() {
-		err := stacks.Delete(client, stackName1, stack.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-		t.Logf("Deleted stack (%s)", stackName1)
-	}()
-	err = gophercloud.WaitFor(60, func() (bool, error) {
-		getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
-		if err != nil {
-			return false, err
-		}
-		if getStack.Status == "CREATE_COMPLETE" {
-			return true, nil
-		}
-		return false, nil
-	})
-
-	updateOpts := osStacks.UpdateOpts{
-		TemplateOpts: templateOpts,
-		Timeout:      20,
-	}
-	err = stacks.Update(client, stackName1, stack.ID, updateOpts).ExtractErr()
-	th.AssertNoErr(t, err)
-	err = gophercloud.WaitFor(60, func() (bool, error) {
-		getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
-		if err != nil {
-			return false, err
-		}
-		if getStack.Status == "UPDATE_COMPLETE" {
-			return true, nil
-		}
-		return false, nil
-	})
-
-	t.Logf("Updated stack")
-
-	err = stacks.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
-		stackList, err := osStacks.ExtractStacks(page)
-		th.AssertNoErr(t, err)
-
-		t.Logf("Got stack list: %+v\n", stackList)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-
-	getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Got stack: %+v\n", getStack)
-
-	abandonedStack, err := stacks.Abandon(client, stackName1, stack.ID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Abandonded stack %+v\n", abandonedStack)
-	th.AssertNoErr(t, err)
-}
diff --git a/acceptance/rackspace/orchestration/v1/stacktemplates_test.go b/acceptance/rackspace/orchestration/v1/stacktemplates_test.go
deleted file mode 100644
index e4ccd9e..0000000
--- a/acceptance/rackspace/orchestration/v1/stacktemplates_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// +build acceptance
-
-package v1
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	osStacks "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
-	osStacktemplates "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacktemplates"
-	"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stacks"
-	"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stacktemplates"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestStackTemplates(t *testing.T) {
-	// Create a provider client for making the HTTP requests.
-	// See common.go in this directory for more information.
-	client := newClient(t)
-
-	stackName := "postman_stack_2"
-
-	createOpts := osStacks.CreateOpts{
-		Name:     stackName,
-		Template: template,
-		Timeout:  5,
-	}
-	stack, err := stacks.Create(client, createOpts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created stack: %+v\n", stack)
-	defer func() {
-		err := stacks.Delete(client, stackName, stack.ID).ExtractErr()
-		th.AssertNoErr(t, err)
-		t.Logf("Deleted stack (%s)", stackName)
-	}()
-	err = gophercloud.WaitFor(60, func() (bool, error) {
-		getStack, err := stacks.Get(client, stackName, stack.ID).Extract()
-		if err != nil {
-			return false, err
-		}
-		if getStack.Status == "CREATE_COMPLETE" {
-			return true, nil
-		}
-		return false, nil
-	})
-
-	tmpl, err := stacktemplates.Get(client, stackName, stack.ID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("retrieved template: %+v\n", tmpl)
-
-	validateOpts := osStacktemplates.ValidateOpts{
-		Template: `{"heat_template_version": "2013-05-23",
-			"description":           "Simple template to test heat commands",
-			"parameters": {
-				"flavor": {
-					"default": "m1.tiny",
-					"type":    "string",
-				},
-			},
-			"resources": {
-				"hello_world": {
-					"type": "OS::Nova::Server",
-					"properties": {
-						"key_name": "heat_key",
-						"flavor": {
-							"get_param": "flavor",
-						},
-						"image":     "ad091b52-742f-469e-8f3c-fd81cadf0743",
-						"user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n",
-					},
-				},
-			},
-		}`}
-	validatedTemplate, err := stacktemplates.Validate(client, validateOpts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("validated template: %+v\n", validatedTemplate)
-}
diff --git a/acceptance/rackspace/pkg.go b/acceptance/rackspace/pkg.go
deleted file mode 100644
index 5d17b32..0000000
--- a/acceptance/rackspace/pkg.go
+++ /dev/null
@@ -1 +0,0 @@
-package rackspace
diff --git a/acceptance/rackspace/rackconnect/v3/cloudnetworks_test.go b/acceptance/rackspace/rackconnect/v3/cloudnetworks_test.go
deleted file mode 100644
index 2c6287e..0000000
--- a/acceptance/rackspace/rackconnect/v3/cloudnetworks_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// +build acceptance
-
-package v3
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestCloudNetworks(t *testing.T) {
-	c := newClient(t)
-	cnID := testListNetworks(t, c)
-	testGetNetworks(t, c, cnID)
-}
-
-func testListNetworks(t *testing.T, c *gophercloud.ServiceClient) string {
-	allPages, err := cloudnetworks.List(c).AllPages()
-	th.AssertNoErr(t, err)
-	allcn, err := cloudnetworks.ExtractCloudNetworks(allPages)
-	fmt.Printf("Listing all cloud networks: %+v\n\n", allcn)
-	var cnID string
-	if len(allcn) > 0 {
-		cnID = allcn[0].ID
-	}
-	return cnID
-}
-
-func testGetNetworks(t *testing.T, c *gophercloud.ServiceClient, id string) {
-	cn, err := cloudnetworks.Get(c, id).Extract()
-	th.AssertNoErr(t, err)
-	fmt.Printf("Retrieved cloud network: %+v\n\n", cn)
-}
diff --git a/acceptance/rackspace/rackconnect/v3/common.go b/acceptance/rackspace/rackconnect/v3/common.go
deleted file mode 100644
index 8c75314..0000000
--- a/acceptance/rackspace/rackconnect/v3/common.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// +build acceptance
-
-package v3
-
-import (
-	"os"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func newClient(t *testing.T) *gophercloud.ServiceClient {
-	ao, err := rackspace.AuthOptionsFromEnv()
-	th.AssertNoErr(t, err)
-
-	client, err := rackspace.AuthenticatedClient(ao)
-	th.AssertNoErr(t, err)
-
-	c, err := rackspace.NewRackConnectV3(client, gophercloud.EndpointOpts{
-		Region: os.Getenv("RS_REGION_NAME"),
-	})
-	th.AssertNoErr(t, err)
-	return c
-}
diff --git a/acceptance/rackspace/rackconnect/v3/lbpools_test.go b/acceptance/rackspace/rackconnect/v3/lbpools_test.go
deleted file mode 100644
index 85ac931..0000000
--- a/acceptance/rackspace/rackconnect/v3/lbpools_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// +build acceptance
-
-package v3
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestLBPools(t *testing.T) {
-	c := newClient(t)
-	pID := testListPools(t, c)
-	testGetPools(t, c, pID)
-	nID := testListNodes(t, c, pID)
-	testListNodeDetails(t, c, pID)
-	testGetNode(t, c, pID, nID)
-	testGetNodeDetails(t, c, pID, nID)
-}
-
-func testListPools(t *testing.T, c *gophercloud.ServiceClient) string {
-	allPages, err := lbpools.List(c).AllPages()
-	th.AssertNoErr(t, err)
-	allp, err := lbpools.ExtractPools(allPages)
-	fmt.Printf("Listing all LB pools: %+v\n\n", allp)
-	var pID string
-	if len(allp) > 0 {
-		pID = allp[0].ID
-	}
-	return pID
-}
-
-func testGetPools(t *testing.T, c *gophercloud.ServiceClient, pID string) {
-	p, err := lbpools.Get(c, pID).Extract()
-	th.AssertNoErr(t, err)
-	fmt.Printf("Retrieved LB pool: %+v\n\n", p)
-}
-
-func testListNodes(t *testing.T, c *gophercloud.ServiceClient, pID string) string {
-	allPages, err := lbpools.ListNodes(c, pID).AllPages()
-	th.AssertNoErr(t, err)
-	alln, err := lbpools.ExtractNodes(allPages)
-	fmt.Printf("Listing all LB pool nodes for pool (%s): %+v\n\n", pID, alln)
-	var nID string
-	if len(alln) > 0 {
-		nID = alln[0].ID
-	}
-	return nID
-}
-
-func testListNodeDetails(t *testing.T, c *gophercloud.ServiceClient, pID string) {
-	allPages, err := lbpools.ListNodesDetails(c, pID).AllPages()
-	th.AssertNoErr(t, err)
-	alln, err := lbpools.ExtractNodesDetails(allPages)
-	fmt.Printf("Listing all LB pool nodes details for pool (%s): %+v\n\n", pID, alln)
-}
-
-func testGetNode(t *testing.T, c *gophercloud.ServiceClient, pID, nID string) {
-	n, err := lbpools.GetNode(c, pID, nID).Extract()
-	th.AssertNoErr(t, err)
-	fmt.Printf("Retrieved LB node: %+v\n\n", n)
-}
-
-func testGetNodeDetails(t *testing.T, c *gophercloud.ServiceClient, pID, nID string) {
-	n, err := lbpools.GetNodeDetails(c, pID, nID).Extract()
-	th.AssertNoErr(t, err)
-	fmt.Printf("Retrieved LB node details: %+v\n\n", n)
-}
diff --git a/acceptance/rackspace/rackconnect/v3/publicips_test.go b/acceptance/rackspace/rackconnect/v3/publicips_test.go
deleted file mode 100644
index 8dc6270..0000000
--- a/acceptance/rackspace/rackconnect/v3/publicips_test.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// +build acceptance
-
-package v3
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestPublicIPs(t *testing.T) {
-	c := newClient(t)
-	ipID := testListIPs(t, c)
-	sID := testGetIP(t, c, ipID)
-	testListIPsForServer(t, c, sID)
-}
-
-func testListIPs(t *testing.T, c *gophercloud.ServiceClient) string {
-	allPages, err := publicips.List(c).AllPages()
-	th.AssertNoErr(t, err)
-	allip, err := publicips.ExtractPublicIPs(allPages)
-	fmt.Printf("Listing all public IPs: %+v\n\n", allip)
-	var ipID string
-	if len(allip) > 0 {
-		ipID = allip[0].ID
-	}
-	return ipID
-}
-
-func testGetIP(t *testing.T, c *gophercloud.ServiceClient, ipID string) string {
-	ip, err := publicips.Get(c, ipID).Extract()
-	th.AssertNoErr(t, err)
-	fmt.Printf("Retrieved public IP (%s): %+v\n\n", ipID, ip)
-	return ip.CloudServer.ID
-}
-
-func testListIPsForServer(t *testing.T, c *gophercloud.ServiceClient, sID string) {
-	allPages, err := publicips.ListForServer(c, sID).AllPages()
-	th.AssertNoErr(t, err)
-	allip, err := publicips.ExtractPublicIPs(allPages)
-	fmt.Printf("Listing all public IPs for server (%s): %+v\n\n", sID, allip)
-}
diff --git a/acceptance/tools/tools.go b/acceptance/tools/tools.go
index 35679b7..bca4574 100644
--- a/acceptance/tools/tools.go
+++ b/acceptance/tools/tools.go
@@ -6,34 +6,12 @@
 	"crypto/rand"
 	"errors"
 	mrand "math/rand"
-	"os"
 	"time"
-
-	"github.com/rackspace/gophercloud"
 )
 
 // ErrTimeout is returned if WaitFor takes longer than 300 second to happen.
 var ErrTimeout = errors.New("Timed out")
 
-// OnlyRS overrides the default Gophercloud behavior of using OS_-prefixed environment variables
-// if RS_ variables aren't present. Otherwise, they'll stomp over each other here in the acceptance
-// tests, where you need to have both defined.
-func OnlyRS(original gophercloud.AuthOptions) gophercloud.AuthOptions {
-	if os.Getenv("RS_AUTH_URL") == "" {
-		original.IdentityEndpoint = ""
-	}
-	if os.Getenv("RS_USERNAME") == "" {
-		original.Username = ""
-	}
-	if os.Getenv("RS_PASSWORD") == "" {
-		original.Password = ""
-	}
-	if os.Getenv("RS_API_KEY") == "" {
-		original.APIKey = ""
-	}
-	return original
-}
-
 // 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++ {
diff --git a/auth_options.go b/auth_options.go
index 07ace13..2ef427a 100644
--- a/auth_options.go
+++ b/auth_options.go
@@ -15,27 +15,27 @@
 	// the Identity API of the appropriate version. While it's ultimately needed by
 	// all of the identity services, it will often be populated by a provider-level
 	// function.
-	IdentityEndpoint string
+	IdentityEndpoint string `json:"-"`
 
 	// Username is required if using Identity V2 API. Consult with your provider's
 	// control panel to discover your account's username. In Identity V3, either
 	// UserID or a combination of Username and DomainID or DomainName are needed.
-	Username, UserID string
+	Username string `json:"username,omitempty"`
+	UserID   string `json:"id,omitempty"`
 
-	// Exactly one of Password or APIKey is required for the Identity V2 and V3
-	// APIs. Consult with your provider's control panel to discover your account's
-	// preferred method of authentication.
-	Password, APIKey string
+	Password string `json:"password,omitempty"`
 
 	// At most one of DomainID and DomainName must be provided if using Username
 	// with Identity V3. Otherwise, either are optional.
-	DomainID, DomainName string
+	DomainID   string `json:"id,omitempty"`
+	DomainName string `json:"name,omitempty"`
 
 	// The TenantID and TenantName fields are optional for the Identity V2 API.
 	// Some providers allow you to specify a TenantName instead of the TenantId.
 	// Some require both. Your provider's authentication policies will determine
 	// how these fields influence authentication.
-	TenantID, TenantName string
+	TenantID   string `json:"tenantId,omitempty"`
+	TenantName string `json:"tenantName,omitempty"`
 
 	// AllowReauth should be set to true if you grant permission for Gophercloud to
 	// cache your credentials in memory, and to allow Gophercloud to attempt to
@@ -44,7 +44,7 @@
 	// possible.  This setting defaults to false.
 	//
 	// NOTE: The reauth function will try to re-authenticate endlessly if left unchecked.
-	// The way to limit the number of attempts is to provide a custom HTTP client to the provider client 
+	// The way to limit the number of attempts is to provide a custom HTTP client to the provider client
 	// and provide a transport that implements the RoundTripper interface and stores the number of failed retries.
 	// For an example of this, see here: https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311
 	AllowReauth bool
@@ -53,3 +53,36 @@
 	// authentication token ID.
 	TokenID string
 }
+
+// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
+// interface in the v2 tokens package
+func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
+	// Populate the request map.
+	authMap := make(map[string]interface{})
+
+	if opts.Username != "" {
+		if opts.Password != "" {
+			authMap["passwordCredentials"] = map[string]interface{}{
+				"username": opts.Username,
+				"password": opts.Password,
+			}
+		} else {
+			return nil, ErrMissingInput{Argument: "Password"}
+		}
+	} else if opts.TokenID != "" {
+		authMap["token"] = map[string]interface{}{
+			"id": opts.TokenID,
+		}
+	} else {
+		return nil, ErrMissingInput{Argument: "Username"}
+	}
+
+	if opts.TenantID != "" {
+		authMap["tenantId"] = opts.TenantID
+	}
+	if opts.TenantName != "" {
+		authMap["tenantName"] = opts.TenantName
+	}
+
+	return map[string]interface{}{"auth": authMap}, nil
+}
diff --git a/endpoint_search.go b/endpoint_search.go
index 5189431..9887947 100644
--- a/endpoint_search.go
+++ b/endpoint_search.go
@@ -1,21 +1,5 @@
 package gophercloud
 
-import "errors"
-
-var (
-	// ErrServiceNotFound is returned when no service in a service catalog matches
-	// the provided EndpointOpts. This is generally returned by provider service
-	// factory methods like "NewComputeV2()" and can mean that a service is not
-	// enabled for your account.
-	ErrServiceNotFound = errors.New("No suitable service could be found in the service catalog.")
-
-	// ErrEndpointNotFound is returned when no available endpoints match the
-	// provided EndpointOpts. This is also generally returned by provider service
-	// factory methods, and usually indicates that a region was specified
-	// incorrectly.
-	ErrEndpointNotFound = errors.New("No suitable endpoint could be found in the service catalog.")
-)
-
 // Availability indicates to whom a specific service endpoint is accessible:
 // the internet at large, internal networks only, or only to administrators.
 // Different identity services use different terminology for these. Identity v2
diff --git a/endpoint_search_test.go b/endpoint_search_test.go
deleted file mode 100644
index 3457453..0000000
--- a/endpoint_search_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package gophercloud
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestApplyDefaultsToEndpointOpts(t *testing.T) {
-	eo := EndpointOpts{Availability: AvailabilityPublic}
-	eo.ApplyDefaults("compute")
-	expected := EndpointOpts{Availability: AvailabilityPublic, Type: "compute"}
-	th.CheckDeepEquals(t, expected, eo)
-
-	eo = EndpointOpts{Type: "compute"}
-	eo.ApplyDefaults("object-store")
-	expected = EndpointOpts{Availability: AvailabilityPublic, Type: "compute"}
-	th.CheckDeepEquals(t, expected, eo)
-}
diff --git a/errors.go b/errors.go
new file mode 100644
index 0000000..978fcb5
--- /dev/null
+++ b/errors.go
@@ -0,0 +1,276 @@
+package gophercloud
+
+import "fmt"
+
+// BaseError is an error type that all other error types embed.
+type BaseError struct {
+	DefaultErrString string
+	Info             string
+}
+
+func (e BaseError) Error() string {
+	e.DefaultErrString = "An error occurred while executing a Gophercloud request."
+	return e.choseErrString()
+}
+
+func (e BaseError) choseErrString() string {
+	if e.Info != "" {
+		return e.Info
+	}
+	return e.DefaultErrString
+}
+
+// ErrMissingInput is the error when input is required in a particular
+// situation but not provided by the user
+type ErrMissingInput struct {
+	BaseError
+	Argument string
+}
+
+func (e ErrMissingInput) Error() string {
+	e.DefaultErrString = fmt.Sprintf("Missing input for argument [%s]", e.Argument)
+	return e.choseErrString()
+}
+
+// ErrInvalidInput is an error type used for most non-HTTP Gophercloud errors.
+type ErrInvalidInput struct {
+	ErrMissingInput
+	Value interface{}
+}
+
+func (e ErrInvalidInput) Error() string {
+	e.DefaultErrString = fmt.Sprintf("Invalid input provided for argument [%s]: [%+v]", e.Argument, e.Value)
+	return e.choseErrString()
+}
+
+// ErrUnexpectedResponseCode is returned by the Request method when a response code other than
+// those listed in OkCodes is encountered.
+type ErrUnexpectedResponseCode struct {
+	BaseError
+	URL      string
+	Method   string
+	Expected []int
+	Actual   int
+	Body     []byte
+}
+
+func (e ErrUnexpectedResponseCode) Error() string {
+	e.DefaultErrString = fmt.Sprintf(
+		"Expected HTTP response code %v when accessing [%s %s], but got %d instead\n%s",
+		e.Expected, e.Method, e.URL, e.Actual, e.Body,
+	)
+	return e.choseErrString()
+}
+
+// ErrDefault400 is the default error type returned on a 400 HTTP response code.
+type ErrDefault400 struct {
+	ErrUnexpectedResponseCode
+}
+
+// ErrDefault401 is the default error type returned on a 401 HTTP response code.
+type ErrDefault401 struct {
+	ErrUnexpectedResponseCode
+}
+
+// ErrDefault404 is the default error type returned on a 404 HTTP response code.
+type ErrDefault404 struct {
+	ErrUnexpectedResponseCode
+}
+
+// ErrDefault405 is the default error type returned on a 405 HTTP response code.
+type ErrDefault405 struct {
+	ErrUnexpectedResponseCode
+}
+
+// ErrDefault408 is the default error type returned on a 408 HTTP response code.
+type ErrDefault408 struct {
+	ErrUnexpectedResponseCode
+}
+
+// ErrDefault429 is the default error type returned on a 429 HTTP response code.
+type ErrDefault429 struct {
+	ErrUnexpectedResponseCode
+}
+
+// ErrDefault500 is the default error type returned on a 500 HTTP response code.
+type ErrDefault500 struct {
+	ErrUnexpectedResponseCode
+}
+
+// ErrDefault503 is the default error type returned on a 503 HTTP response code.
+type ErrDefault503 struct {
+	ErrUnexpectedResponseCode
+}
+
+func (e ErrDefault400) Error() string {
+	return "Invalid request due to incorrect syntax or missing required parameters."
+}
+func (e ErrDefault401) Error() string {
+	return "Authentication failed"
+}
+func (e ErrDefault404) Error() string {
+	return "Resource not found"
+}
+func (e ErrDefault405) Error() string {
+	return "Method not allowed"
+}
+func (e ErrDefault408) Error() string {
+	return "The server timed out waiting for the request"
+}
+func (e ErrDefault429) Error() string {
+	return "Too many requests have been sent in a given amount of time. Pause" +
+		" requests, wait up to one minute, and try again."
+}
+func (e ErrDefault500) Error() string {
+	return "Internal Server Error"
+}
+func (e ErrDefault503) Error() string {
+	return "The service is currently unable to handle the request due to a temporary" +
+		" overloading or maintenance. This is a temporary condition. Try again later."
+}
+
+// Err400er is the interface resource error types implement to override the error message
+// from a 400 error.
+type Err400er interface {
+	Error400(ErrUnexpectedResponseCode) error
+}
+
+// Err401er is the interface resource error types implement to override the error message
+// from a 401 error.
+type Err401er interface {
+	Error401(ErrUnexpectedResponseCode) error
+}
+
+// Err404er is the interface resource error types implement to override the error message
+// from a 404 error.
+type Err404er interface {
+	Error404(ErrUnexpectedResponseCode) error
+}
+
+// Err405er is the interface resource error types implement to override the error message
+// from a 405 error.
+type Err405er interface {
+	Error405(ErrUnexpectedResponseCode) error
+}
+
+// Err408er is the interface resource error types implement to override the error message
+// from a 408 error.
+type Err408er interface {
+	Error408(ErrUnexpectedResponseCode) error
+}
+
+// Err429er is the interface resource error types implement to override the error message
+// from a 429 error.
+type Err429er interface {
+	Error429(ErrUnexpectedResponseCode) error
+}
+
+// Err500er is the interface resource error types implement to override the error message
+// from a 500 error.
+type Err500er interface {
+	Error500(ErrUnexpectedResponseCode) error
+}
+
+// Err503er is the interface resource error types implement to override the error message
+// from a 503 error.
+type Err503er interface {
+	Error503(ErrUnexpectedResponseCode) error
+}
+
+// ErrTimeOut is the error type returned when an operations times out.
+type ErrTimeOut struct {
+	BaseError
+}
+
+func (e ErrTimeOut) Error() string {
+	e.DefaultErrString = "A time out occurred"
+	return e.choseErrString()
+}
+
+// ErrUnableToReauthenticate is the error type returned when reauthentication fails.
+type ErrUnableToReauthenticate struct {
+	BaseError
+	ErrOriginal error
+}
+
+func (e ErrUnableToReauthenticate) Error() string {
+	e.DefaultErrString = fmt.Sprintf("Unable to re-authenticate: %s", e.ErrOriginal)
+	return e.choseErrString()
+}
+
+// ErrErrorAfterReauthentication is the error type returned when reauthentication
+// succeeds, but an error occurs afterword (usually an HTTP error).
+type ErrErrorAfterReauthentication struct {
+	BaseError
+	ErrOriginal error
+}
+
+func (e ErrErrorAfterReauthentication) Error() string {
+	e.DefaultErrString = fmt.Sprintf("Successfully re-authenticated, but got error executing request: %s", e.ErrOriginal)
+	return e.choseErrString()
+}
+
+// ErrServiceNotFound is returned when no service in a service catalog matches
+// the provided EndpointOpts. This is generally returned by provider service
+// factory methods like "NewComputeV2()" and can mean that a service is not
+// enabled for your account.
+type ErrServiceNotFound struct {
+	BaseError
+}
+
+func (e ErrServiceNotFound) Error() string {
+	e.DefaultErrString = "No suitable service could be found in the service catalog."
+	return e.choseErrString()
+}
+
+// ErrEndpointNotFound is returned when no available endpoints match the
+// provided EndpointOpts. This is also generally returned by provider service
+// factory methods, and usually indicates that a region was specified
+// incorrectly.
+type ErrEndpointNotFound struct {
+	BaseError
+}
+
+func (e ErrEndpointNotFound) Error() string {
+	e.DefaultErrString = "No suitable endpoint could be found in the service catalog."
+	return e.choseErrString()
+}
+
+// ErrResourceNotFound is the error when trying to retrieve a resource's
+// ID by name and the resource doesn't exist.
+type ErrResourceNotFound struct {
+	BaseError
+	Name         string
+	ResourceType string
+}
+
+func (e ErrResourceNotFound) Error() string {
+	e.DefaultErrString = fmt.Sprintf("Unable to find %s with name %s", e.ResourceType, e.Name)
+	return e.choseErrString()
+}
+
+// ErrMultipleResourcesFound is the error when trying to retrieve a resource's
+// ID by name and multiple resources have the user-provided name.
+type ErrMultipleResourcesFound struct {
+	BaseError
+	Name         string
+	Count        int
+	ResourceType string
+}
+
+func (e ErrMultipleResourcesFound) Error() string {
+	e.DefaultErrString = fmt.Sprintf("Found %d %ss matching %s", e.Count, e.ResourceType, e.Name)
+	return e.choseErrString()
+}
+
+// ErrUnexpectedType is the error when an unexpected type is encountered
+type ErrUnexpectedType struct {
+	BaseError
+	Expected string
+	Actual   string
+}
+
+func (e ErrUnexpectedType) Error() string {
+	e.DefaultErrString = fmt.Sprintf("Expected %s but got %s", e.Expected, e.Actual)
+	return e.choseErrString()
+}
diff --git a/openstack/auth_env.go b/openstack/auth_env.go
index a4402b6..f6d2eb1 100644
--- a/openstack/auth_env.go
+++ b/openstack/auth_env.go
@@ -1,23 +1,14 @@
 package openstack
 
 import (
-	"fmt"
 	"os"
 
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 var nilOptions = gophercloud.AuthOptions{}
 
-// ErrNoAuthUrl, ErrNoUsername, and ErrNoPassword errors indicate of the required OS_AUTH_URL, OS_USERNAME, or OS_PASSWORD
-// environment variables, respectively, remain undefined.  See the AuthOptions() function for more details.
-var (
-	ErrNoAuthURL  = fmt.Errorf("Environment variable OS_AUTH_URL needs to be set.")
-	ErrNoUsername = fmt.Errorf("Environment variable OS_USERNAME needs to be set.")
-	ErrNoPassword = fmt.Errorf("Environment variable OS_PASSWORD needs to be set.")
-)
-
-// AuthOptions fills out an identity.AuthOptions structure with the settings found on the various OpenStack
+// AuthOptionsFromEnv fills out an identity.AuthOptions structure with the settings found on the various OpenStack
 // OS_* environment variables.  The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME,
 // OS_PASSWORD, OS_TENANT_ID, and OS_TENANT_NAME.  Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must
 // have settings, or an error will result.  OS_TENANT_ID and OS_TENANT_NAME are optional.
@@ -32,15 +23,18 @@
 	domainName := os.Getenv("OS_DOMAIN_NAME")
 
 	if authURL == "" {
-		return nilOptions, ErrNoAuthURL
+		err := gophercloud.ErrMissingInput{Argument: "authURL"}
+		return nilOptions, err
 	}
 
 	if username == "" && userID == "" {
-		return nilOptions, ErrNoUsername
+		err := gophercloud.ErrMissingInput{Argument: "username"}
+		return nilOptions, err
 	}
 
 	if password == "" {
-		return nilOptions, ErrNoPassword
+		err := gophercloud.ErrMissingInput{Argument: "password"}
+		return nilOptions, err
 	}
 
 	ao := gophercloud.AuthOptions{
diff --git a/openstack/blockstorage/v1/apiversions/requests.go b/openstack/blockstorage/v1/apiversions/requests.go
index bb2c259..725c13a 100644
--- a/openstack/blockstorage/v1/apiversions/requests.go
+++ b/openstack/blockstorage/v1/apiversions/requests.go
@@ -1,8 +1,8 @@
 package apiversions
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // List lists all the Cinder API versions available to end-users.
@@ -14,8 +14,7 @@
 
 // Get will retrieve the volume type with the provided ID. To extract the volume
 // type from the result, call the Extract method on the GetResult.
-func Get(client *gophercloud.ServiceClient, v string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, v), &res.Body, nil)
-	return res
+func Get(client *gophercloud.ServiceClient, v string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, v), &r.Body, nil)
+	return
 }
diff --git a/openstack/blockstorage/v1/apiversions/requests_test.go b/openstack/blockstorage/v1/apiversions/requests_test.go
deleted file mode 100644
index 56b5e4f..0000000
--- a/openstack/blockstorage/v1/apiversions/requests_test.go
+++ /dev/null
@@ -1,145 +0,0 @@
-package apiversions
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListVersions(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `{
-			"versions": [
-				{
-					"status": "CURRENT",
-					"updated": "2012-01-04T11:33:21Z",
-					"id": "v1.0",
-					"links": [
-						{
-							"href": "http://23.253.228.211:8776/v1/",
-							"rel": "self"
-						}
-					]
-			    },
-				{
-					"status": "CURRENT",
-					"updated": "2012-11-21T11:33:21Z",
-					"id": "v2.0",
-					"links": [
-						{
-							"href": "http://23.253.228.211:8776/v2/",
-							"rel": "self"
-						}
-					]
-				}
-			]
-		}`)
-	})
-
-	count := 0
-
-	List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractAPIVersions(page)
-		if err != nil {
-			t.Errorf("Failed to extract API versions: %v", err)
-			return false, err
-		}
-
-		expected := []APIVersion{
-			APIVersion{
-				ID:      "v1.0",
-				Status:  "CURRENT",
-				Updated: "2012-01-04T11:33:21Z",
-			},
-			APIVersion{
-				ID:      "v2.0",
-				Status:  "CURRENT",
-				Updated: "2012-11-21T11:33:21Z",
-			},
-		}
-
-		th.AssertDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestAPIInfo(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v1/", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `{
-			"version": {
-				"status": "CURRENT",
-				"updated": "2012-01-04T11:33:21Z",
-				"media-types": [
-					{
-						"base": "application/xml",
-						"type": "application/vnd.openstack.volume+xml;version=1"
-					},
-					{
-						"base": "application/json",
-						"type": "application/vnd.openstack.volume+json;version=1"
-					}
-				],
-				"id": "v1.0",
-				"links": [
-					{
-						"href": "http://23.253.228.211:8776/v1/",
-						"rel": "self"
-					},
-					{
-						"href": "http://jorgew.github.com/block-storage-api/content/os-block-storage-1.0.pdf",
-						"type": "application/pdf",
-						"rel": "describedby"
-					},
-					{
-						"href": "http://docs.rackspacecloud.com/servers/api/v1.1/application.wadl",
-						"type": "application/vnd.sun.wadl+xml",
-						"rel": "describedby"
-					}
-				]
-			}
-		}`)
-	})
-
-	actual, err := Get(client.ServiceClient(), "v1").Extract()
-	if err != nil {
-		t.Errorf("Failed to extract version: %v", err)
-	}
-
-	expected := APIVersion{
-		ID:      "v1.0",
-		Status:  "CURRENT",
-		Updated: "2012-01-04T11:33:21Z",
-	}
-
-	th.AssertEquals(t, actual.ID, expected.ID)
-	th.AssertEquals(t, actual.Status, expected.Status)
-	th.AssertEquals(t, actual.Updated, expected.Updated)
-}
diff --git a/openstack/blockstorage/v1/apiversions/results.go b/openstack/blockstorage/v1/apiversions/results.go
index 7b0df11..f510c6d 100644
--- a/openstack/blockstorage/v1/apiversions/results.go
+++ b/openstack/blockstorage/v1/apiversions/results.go
@@ -1,17 +1,15 @@
 package apiversions
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // APIVersion represents an API version for Cinder.
 type APIVersion struct {
-	ID      string `json:"id" mapstructure:"id"`           // unique identifier
-	Status  string `json:"status" mapstructure:"status"`   // current status
-	Updated string `json:"updated" mapstructure:"updated"` // date last updated
+	ID      string `json:"id"`      // unique identifier
+	Status  string `json:"status"`  // current status
+	Updated string `json:"updated"` // date last updated
 }
 
 // APIVersionPage is the page returned by a pager when traversing over a
@@ -23,22 +21,17 @@
 // IsEmpty checks whether an APIVersionPage struct is empty.
 func (r APIVersionPage) IsEmpty() (bool, error) {
 	is, err := ExtractAPIVersions(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractAPIVersions takes a collection page, extracts all of the elements,
 // and returns them a slice of APIVersion structs. It is effectively a cast.
-func ExtractAPIVersions(page pagination.Page) ([]APIVersion, error) {
-	var resp struct {
-		Versions []APIVersion `mapstructure:"versions"`
+func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) {
+	var s struct {
+		Versions []APIVersion `json:"versions"`
 	}
-
-	err := mapstructure.Decode(page.(APIVersionPage).Body, &resp)
-
-	return resp.Versions, err
+	err := (r.(APIVersionPage)).ExtractInto(&s)
+	return s.Versions, err
 }
 
 // GetResult represents the result of a get operation.
@@ -48,11 +41,9 @@
 
 // Extract is a function that accepts a result and extracts an API version resource.
 func (r GetResult) Extract() (*APIVersion, error) {
-	var resp struct {
-		Version *APIVersion `mapstructure:"version"`
+	var s struct {
+		Version *APIVersion `json:"version"`
 	}
-
-	err := mapstructure.Decode(r.Body, &resp)
-
-	return resp.Version, err
+	err := r.ExtractInto(&s)
+	return s.Version, err
 }
diff --git a/openstack/blockstorage/v1/apiversions/testing/doc.go b/openstack/blockstorage/v1/apiversions/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/blockstorage/v1/apiversions/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/blockstorage/v1/apiversions/testing/fixtures.go b/openstack/blockstorage/v1/apiversions/testing/fixtures.go
new file mode 100644
index 0000000..885fdf6
--- /dev/null
+++ b/openstack/blockstorage/v1/apiversions/testing/fixtures.go
@@ -0,0 +1,91 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func MockListResponse(t *testing.T) {
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `{
+			"versions": [
+				{
+					"status": "CURRENT",
+					"updated": "2012-01-04T11:33:21Z",
+					"id": "v1.0",
+					"links": [
+						{
+							"href": "http://23.253.228.211:8776/v1/",
+							"rel": "self"
+						}
+					]
+			    },
+				{
+					"status": "CURRENT",
+					"updated": "2012-11-21T11:33:21Z",
+					"id": "v2.0",
+					"links": [
+						{
+							"href": "http://23.253.228.211:8776/v2/",
+							"rel": "self"
+						}
+					]
+				}
+			]
+		}`)
+	})
+}
+
+func MockGetResponse(t *testing.T) {
+	th.Mux.HandleFunc("/v1/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `{
+			"version": {
+				"status": "CURRENT",
+				"updated": "2012-01-04T11:33:21Z",
+				"media-types": [
+					{
+						"base": "application/xml",
+						"type": "application/vnd.openstack.volume+xml;version=1"
+					},
+					{
+						"base": "application/json",
+						"type": "application/vnd.openstack.volume+json;version=1"
+					}
+				],
+				"id": "v1.0",
+				"links": [
+					{
+						"href": "http://23.253.228.211:8776/v1/",
+						"rel": "self"
+					},
+					{
+						"href": "http://jorgew.github.com/block-storage-api/content/os-block-storage-1.0.pdf",
+						"type": "application/pdf",
+						"rel": "describedby"
+					},
+					{
+						"href": "http://docs.rackspacecloud.com/servers/api/v1.1/application.wadl",
+						"type": "application/vnd.sun.wadl+xml",
+						"rel": "describedby"
+					}
+				]
+			}
+		}`)
+	})
+}
diff --git a/openstack/blockstorage/v1/apiversions/testing/requests_test.go b/openstack/blockstorage/v1/apiversions/testing/requests_test.go
new file mode 100644
index 0000000..3103497
--- /dev/null
+++ b/openstack/blockstorage/v1/apiversions/testing/requests_test.go
@@ -0,0 +1,64 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestListVersions(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockListResponse(t)
+
+	count := 0
+
+	apiversions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := apiversions.ExtractAPIVersions(page)
+		th.AssertNoErr(t, err)
+
+		expected := []apiversions.APIVersion{
+			{
+				ID:      "v1.0",
+				Status:  "CURRENT",
+				Updated: "2012-01-04T11:33:21Z",
+			},
+			{
+				ID:      "v2.0",
+				Status:  "CURRENT",
+				Updated: "2012-11-21T11:33:21Z",
+			},
+		}
+
+		th.AssertDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	th.AssertEquals(t, 1, count)
+}
+
+func TestAPIInfo(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockGetResponse(t)
+
+	actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := apiversions.APIVersion{
+		ID:      "v1.0",
+		Status:  "CURRENT",
+		Updated: "2012-01-04T11:33:21Z",
+	}
+
+	th.AssertEquals(t, actual.ID, expected.ID)
+	th.AssertEquals(t, actual.Status, expected.Status)
+	th.AssertEquals(t, actual.Updated, expected.Updated)
+}
diff --git a/openstack/blockstorage/v1/apiversions/urls.go b/openstack/blockstorage/v1/apiversions/urls.go
index 78f9645..c9cf895 100644
--- a/openstack/blockstorage/v1/apiversions/urls.go
+++ b/openstack/blockstorage/v1/apiversions/urls.go
@@ -4,7 +4,7 @@
 	"strings"
 	"net/url"
 
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 func getURL(c *gophercloud.ServiceClient, version string) string {
diff --git a/openstack/blockstorage/v1/apiversions/urls_test.go b/openstack/blockstorage/v1/apiversions/urls_test.go
deleted file mode 100644
index 68cfb8c..0000000
--- a/openstack/blockstorage/v1/apiversions/urls_test.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package apiversions
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-const endpoint2 = "http://localhost:57909/v1/3a02ee0b5cf14816b41b17e851d29a94"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func endpointClient2() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint2}
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "v1")
-	expected := endpoint + "v1/"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient2())
-	expected := endpoint
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/blockstorage/v1/snapshots/fixtures.go b/openstack/blockstorage/v1/snapshots/fixtures.go
deleted file mode 100644
index eba5f82..0000000
--- a/openstack/blockstorage/v1/snapshots/fixtures.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// +build fixtures
-
-package snapshots
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func MockListResponse(t *testing.T) {
-	th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-    {
-      "snapshots": [
-        {
-          "id": "289da7f8-6440-407c-9fb4-7db01ec49164",
-          "display_name": "snapshot-001"
-        },
-        {
-          "id": "96c3bda7-c82a-4f50-be73-ca7621794835",
-          "display_name": "snapshot-002"
-        }
-      ]
-    }
-    `)
-	})
-}
-
-func MockGetResponse(t *testing.T) {
-	th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-{
-    "snapshot": {
-        "display_name": "snapshot-001",
-        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
-    }
-}
-      `)
-	})
-}
-
-func MockCreateResponse(t *testing.T) {
-	th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "snapshot": {
-        "volume_id": "1234",
-        "display_name": "snapshot-001"
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "snapshot": {
-        "volume_id": "1234",
-        "display_name": "snapshot-001",
-        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
-    }
-}
-    `)
-	})
-}
-
-func MockUpdateMetadataResponse(t *testing.T) {
-	th.Mux.HandleFunc("/snapshots/123/metadata", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestJSONRequest(t, r, `
-    {
-      "metadata": {
-        "key": "v1"
-      }
-    }
-    `)
-
-		fmt.Fprintf(w, `
-      {
-        "metadata": {
-          "key": "v1"
-        }
-      }
-    `)
-	})
-}
-
-func MockDeleteResponse(t *testing.T) {
-	th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
diff --git a/openstack/blockstorage/v1/snapshots/requests.go b/openstack/blockstorage/v1/snapshots/requests.go
index 71936e5..cb9d0d0 100644
--- a/openstack/blockstorage/v1/snapshots/requests.go
+++ b/openstack/blockstorage/v1/snapshots/requests.go
@@ -1,10 +1,8 @@
 package snapshots
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // CreateOptsBuilder allows extensions to add additional parameters to the
@@ -17,75 +15,45 @@
 // the snapshots.Create function. For more information about these parameters,
 // see the Snapshot object.
 type CreateOpts struct {
-	// OPTIONAL
-	Description string
-	// OPTIONAL
-	Force bool
-	// OPTIONAL
-	Metadata map[string]interface{}
-	// OPTIONAL
-	Name string
-	// REQUIRED
-	VolumeID string
+	VolumeID    string                 `json:"volume_id" required:"true"`
+	Description string                 `json:"display_description,omitempty"`
+	Force       bool                   `json:"force,omitempty"`
+	Metadata    map[string]interface{} `json:"metadata,omitempty"`
+	Name        string                 `json:"display_name,omitempty"`
 }
 
 // ToSnapshotCreateMap assembles a request body based on the contents of a
 // CreateOpts.
 func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-
-	if opts.VolumeID == "" {
-		return nil, fmt.Errorf("Required CreateOpts field 'VolumeID' not set.")
-	}
-	s["volume_id"] = opts.VolumeID
-
-	if opts.Description != "" {
-		s["display_description"] = opts.Description
-	}
-	if opts.Force == true {
-		s["force"] = opts.Force
-	}
-	if opts.Metadata != nil {
-		s["metadata"] = opts.Metadata
-	}
-	if opts.Name != "" {
-		s["display_name"] = opts.Name
-	}
-
-	return map[string]interface{}{"snapshot": s}, nil
+	return gophercloud.BuildRequestBody(opts, "snapshot")
 }
 
 // Create will create a new Snapshot based on the values in CreateOpts. To
 // extract the Snapshot object from the response, call the Extract method on the
 // CreateResult.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToSnapshotCreateMap()
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToSnapshotCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201},
 	})
-	return res
+	return
 }
 
 // Delete will delete the existing Snapshot with the provided ID.
-func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, id), nil)
-	return res
+func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = client.Delete(deleteURL(client, id), nil)
+	return
 }
 
 // Get retrieves the Snapshot with the provided ID. To extract the Snapshot
 // object from the response, call the Extract method on the GetResult.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return
 }
 
 // ListOptsBuilder allows extensions to add additional parameters to the List
@@ -105,10 +73,7 @@
 // ToSnapshotListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToSnapshotListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns Snapshots optionally limited by the conditions provided in
@@ -122,11 +87,9 @@
 		}
 		url += query
 	}
-
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return ListResult{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, url, createPage)
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
+		return SnapshotPage{pagination.SinglePageBase(r)}
+	})
 }
 
 // UpdateMetadataOptsBuilder allows extensions to add additional parameters to
@@ -139,68 +102,57 @@
 // object is passed to the snapshots.Update function. For more information
 // about the parameters, see the Snapshot object.
 type UpdateMetadataOpts struct {
-	Metadata map[string]interface{}
+	Metadata map[string]interface{} `json:"metadata,omitempty"`
 }
 
 // ToSnapshotUpdateMetadataMap assembles a request body based on the contents of
 // an UpdateMetadataOpts.
 func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) {
-	v := make(map[string]interface{})
-
-	if opts.Metadata != nil {
-		v["metadata"] = opts.Metadata
-	}
-
-	return v, nil
+	return gophercloud.BuildRequestBody(opts, "")
 }
 
 // UpdateMetadata will update the Snapshot with provided information. To
 // extract the updated Snapshot from the response, call the ExtractMetadata
 // method on the UpdateMetadataResult.
-func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) UpdateMetadataResult {
-	var res UpdateMetadataResult
-
-	reqBody, err := opts.ToSnapshotUpdateMetadataMap()
+func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) {
+	b, err := opts.ToSnapshotUpdateMetadataMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Put(updateMetadataURL(client, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // IDFromName is a convienience function that returns a snapshot's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
-	snapshotCount := 0
-	snapshotID := ""
-	if name == "" {
-		return "", fmt.Errorf("A snapshot name must be provided.")
+	count := 0
+	id := ""
+	pages, err := List(client, nil).AllPages()
+	if err != nil {
+		return "", err
 	}
-	pager := List(client, nil)
-	pager.EachPage(func(page pagination.Page) (bool, error) {
-		snapshotList, err := ExtractSnapshots(page)
-		if err != nil {
-			return false, err
-		}
 
-		for _, s := range snapshotList {
-			if s.Name == name {
-				snapshotCount++
-				snapshotID = s.ID
-			}
-		}
-		return true, nil
-	})
+	all, err := ExtractSnapshots(pages)
+	if err != nil {
+		return "", err
+	}
 
-	switch snapshotCount {
+	for _, s := range all {
+		if s.Name == name {
+			count++
+			id = s.ID
+		}
+	}
+
+	switch count {
 	case 0:
-		return "", fmt.Errorf("Unable to find snapshot: %s", name)
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"}
 	case 1:
-		return snapshotID, nil
+		return id, nil
 	default:
-		return "", fmt.Errorf("Found %d snapshots matching %s", snapshotCount, name)
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"}
 	}
 }
diff --git a/openstack/blockstorage/v1/snapshots/requests_test.go b/openstack/blockstorage/v1/snapshots/requests_test.go
deleted file mode 100644
index d0f9e88..0000000
--- a/openstack/blockstorage/v1/snapshots/requests_test.go
+++ /dev/null
@@ -1,104 +0,0 @@
-package snapshots
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockListResponse(t)
-
-	count := 0
-
-	List(client.ServiceClient(), &ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractSnapshots(page)
-		if err != nil {
-			t.Errorf("Failed to extract snapshots: %v", err)
-			return false, err
-		}
-
-		expected := []Snapshot{
-			Snapshot{
-				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
-				Name: "snapshot-001",
-			},
-			Snapshot{
-				ID:   "96c3bda7-c82a-4f50-be73-ca7621794835",
-				Name: "snapshot-002",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockGetResponse(t)
-
-	v, err := Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, v.Name, "snapshot-001")
-	th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockCreateResponse(t)
-
-	options := CreateOpts{VolumeID: "1234", Name: "snapshot-001"}
-	n, err := Create(client.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.VolumeID, "1234")
-	th.AssertEquals(t, n.Name, "snapshot-001")
-	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestUpdateMetadata(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockUpdateMetadataResponse(t)
-
-	expected := map[string]interface{}{"key": "v1"}
-
-	options := &UpdateMetadataOpts{
-		Metadata: map[string]interface{}{
-			"key": "v1",
-		},
-	}
-
-	actual, err := UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata()
-
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, actual, expected)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockDeleteResponse(t)
-
-	res := Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/blockstorage/v1/snapshots/results.go b/openstack/blockstorage/v1/snapshots/results.go
index e595798..195200d 100644
--- a/openstack/blockstorage/v1/snapshots/results.go
+++ b/openstack/blockstorage/v1/snapshots/results.go
@@ -1,52 +1,50 @@
 package snapshots
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Snapshot contains all the information associated with an OpenStack Snapshot.
 type Snapshot struct {
 	// Currect status of the Snapshot.
-	Status string `mapstructure:"status"`
+	Status string `json:"status"`
 
 	// Display name.
-	Name string `mapstructure:"display_name"`
+	Name string `json:"display_name"`
 
 	// Instances onto which the Snapshot is attached.
-	Attachments []string `mapstructure:"attachments"`
+	Attachments []string `json:"attachments"`
 
 	// Logical group.
-	AvailabilityZone string `mapstructure:"availability_zone"`
+	AvailabilityZone string `json:"availability_zone"`
 
 	// Is the Snapshot bootable?
-	Bootable string `mapstructure:"bootable"`
+	Bootable string `json:"bootable"`
 
 	// Date created.
-	CreatedAt string `mapstructure:"created_at"`
+	CreatedAt gophercloud.JSONRFC3339Milli `json:"created_at"`
 
 	// Display description.
-	Description string `mapstructure:"display_discription"`
+	Description string `json:"display_discription"`
 
 	// See VolumeType object for more information.
-	VolumeType string `mapstructure:"volume_type"`
+	VolumeType string `json:"volume_type"`
 
 	// ID of the Snapshot from which this Snapshot was created.
-	SnapshotID string `mapstructure:"snapshot_id"`
+	SnapshotID string `json:"snapshot_id"`
 
 	// ID of the Volume from which this Snapshot was created.
-	VolumeID string `mapstructure:"volume_id"`
+	VolumeID string `json:"volume_id"`
 
 	// User-defined key-value pairs.
-	Metadata map[string]string `mapstructure:"metadata"`
+	Metadata map[string]string `json:"metadata"`
 
 	// Unique identifier.
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Size of the Snapshot, in GB.
-	Size int `mapstructure:"size"`
+	Size int `json:"size"`
 }
 
 // CreateResult contains the response body and error from a Create request.
@@ -64,28 +62,24 @@
 	gophercloud.ErrResult
 }
 
-// ListResult is a pagination.Pager that is returned from a call to the List function.
-type ListResult struct {
+// SnapshotPage is a pagination.Pager that is returned from a call to the List function.
+type SnapshotPage struct {
 	pagination.SinglePageBase
 }
 
-// IsEmpty returns true if a ListResult contains no Snapshots.
-func (r ListResult) IsEmpty() (bool, error) {
+// IsEmpty returns true if a SnapshotPage contains no Snapshots.
+func (r SnapshotPage) IsEmpty() (bool, error) {
 	volumes, err := ExtractSnapshots(r)
-	if err != nil {
-		return true, err
-	}
-	return len(volumes) == 0, nil
+	return len(volumes) == 0, err
 }
 
 // ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call.
-func ExtractSnapshots(page pagination.Page) ([]Snapshot, error) {
-	var response struct {
+func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) {
+	var s struct {
 		Snapshots []Snapshot `json:"snapshots"`
 	}
-
-	err := mapstructure.Decode(page.(ListResult).Body, &response)
-	return response.Snapshots, err
+	err := (r.(SnapshotPage)).ExtractInto(&s)
+	return s.Snapshots, err
 }
 
 // UpdateMetadataResult contains the response body and error from an UpdateMetadata request.
@@ -98,7 +92,6 @@
 	if r.Err != nil {
 		return nil, r.Err
 	}
-
 	m := r.Body.(map[string]interface{})["metadata"]
 	return m.(map[string]interface{}), nil
 }
@@ -109,15 +102,9 @@
 
 // Extract will get the Snapshot object out of the commonResult object.
 func (r commonResult) Extract() (*Snapshot, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Snapshot *Snapshot `json:"snapshot"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Snapshot, err
+	err := r.ExtractInto(&s)
+	return s.Snapshot, err
 }
diff --git a/openstack/blockstorage/v1/snapshots/testing/doc.go b/openstack/blockstorage/v1/snapshots/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/blockstorage/v1/snapshots/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/blockstorage/v1/snapshots/testing/fixtures.go b/openstack/blockstorage/v1/snapshots/testing/fixtures.go
new file mode 100644
index 0000000..7bdc782
--- /dev/null
+++ b/openstack/blockstorage/v1/snapshots/testing/fixtures.go
@@ -0,0 +1,114 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func MockListResponse(t *testing.T) {
+	th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+    {
+      "snapshots": [
+        {
+          "id": "289da7f8-6440-407c-9fb4-7db01ec49164",
+          "display_name": "snapshot-001"
+        },
+        {
+          "id": "96c3bda7-c82a-4f50-be73-ca7621794835",
+          "display_name": "snapshot-002"
+        }
+      ]
+    }
+    `)
+	})
+}
+
+func MockGetResponse(t *testing.T) {
+	th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `
+{
+    "snapshot": {
+        "display_name": "snapshot-001",
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
+    }
+}
+      `)
+	})
+}
+
+func MockCreateResponse(t *testing.T) {
+	th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "snapshot": {
+        "volume_id": "1234",
+        "display_name": "snapshot-001"
+    }
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "snapshot": {
+        "volume_id": "1234",
+        "display_name": "snapshot-001",
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
+    }
+}
+    `)
+	})
+}
+
+func MockUpdateMetadataResponse(t *testing.T) {
+	th.Mux.HandleFunc("/snapshots/123/metadata", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `
+    {
+      "metadata": {
+        "key": "v1"
+      }
+    }
+    `)
+
+		fmt.Fprintf(w, `
+      {
+        "metadata": {
+          "key": "v1"
+        }
+      }
+    `)
+	})
+}
+
+func MockDeleteResponse(t *testing.T) {
+	th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
diff --git a/openstack/blockstorage/v1/snapshots/testing/requests_test.go b/openstack/blockstorage/v1/snapshots/testing/requests_test.go
new file mode 100644
index 0000000..cfb206b
--- /dev/null
+++ b/openstack/blockstorage/v1/snapshots/testing/requests_test.go
@@ -0,0 +1,105 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockListResponse(t)
+
+	count := 0
+
+	snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := snapshots.ExtractSnapshots(page)
+		if err != nil {
+			t.Errorf("Failed to extract snapshots: %v", err)
+			return false, err
+		}
+
+		expected := []snapshots.Snapshot{
+			{
+				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
+				Name: "snapshot-001",
+			},
+			{
+				ID:   "96c3bda7-c82a-4f50-be73-ca7621794835",
+				Name: "snapshot-002",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockGetResponse(t)
+
+	v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, v.Name, "snapshot-001")
+	th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockCreateResponse(t)
+
+	options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"}
+	n, err := snapshots.Create(client.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.VolumeID, "1234")
+	th.AssertEquals(t, n.Name, "snapshot-001")
+	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestUpdateMetadata(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockUpdateMetadataResponse(t)
+
+	expected := map[string]interface{}{"key": "v1"}
+
+	options := &snapshots.UpdateMetadataOpts{
+		Metadata: map[string]interface{}{
+			"key": "v1",
+		},
+	}
+
+	actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata()
+
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, actual, expected)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockDeleteResponse(t)
+
+	res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/blockstorage/v1/snapshots/urls.go b/openstack/blockstorage/v1/snapshots/urls.go
index 4d635e8..7780437 100644
--- a/openstack/blockstorage/v1/snapshots/urls.go
+++ b/openstack/blockstorage/v1/snapshots/urls.go
@@ -1,6 +1,6 @@
 package snapshots
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func createURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("snapshots")
diff --git a/openstack/blockstorage/v1/snapshots/urls_test.go b/openstack/blockstorage/v1/snapshots/urls_test.go
deleted file mode 100644
index feacf7f..0000000
--- a/openstack/blockstorage/v1/snapshots/urls_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package snapshots
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "snapshots"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "foo")
-	expected := endpoint + "snapshots/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "snapshots/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient())
-	expected := endpoint + "snapshots"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestMetadataURL(t *testing.T) {
-	actual := metadataURL(endpointClient(), "foo")
-	expected := endpoint + "snapshots/foo/metadata"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestUpdateMetadataURL(t *testing.T) {
-	actual := updateMetadataURL(endpointClient(), "foo")
-	expected := endpoint + "snapshots/foo/metadata"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/blockstorage/v1/snapshots/util.go b/openstack/blockstorage/v1/snapshots/util.go
index 64cdc60..40fbb82 100644
--- a/openstack/blockstorage/v1/snapshots/util.go
+++ b/openstack/blockstorage/v1/snapshots/util.go
@@ -1,7 +1,7 @@
 package snapshots
 
 import (
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 // WaitForStatus will continually poll the resource, checking for a particular
diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go
index 3e9243a..d668591 100644
--- a/openstack/blockstorage/v1/volumes/requests.go
+++ b/openstack/blockstorage/v1/volumes/requests.go
@@ -1,10 +1,8 @@
 package volumes
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // CreateOptsBuilder allows extensions to add additional parameters to the
@@ -17,91 +15,49 @@
 // the volumes.Create function. For more information about these parameters,
 // see the Volume object.
 type CreateOpts struct {
-	// OPTIONAL
-	Availability string
-	// OPTIONAL
-	Description string
-	// OPTIONAL
-	Metadata map[string]string
-	// OPTIONAL
-	Name string
-	// REQUIRED
-	Size int
-	// OPTIONAL
-	SnapshotID, SourceVolID, ImageID string
-	// OPTIONAL
-	VolumeType string
+	Size         int               `json:"size" required:"true"`
+	Availability string            `json:"availability,omitempty"`
+	Description  string            `json:"display_description,omitempty"`
+	Metadata     map[string]string `json:"metadata,omitempty"`
+	Name         string            `json:"display_name,omitempty"`
+	SnapshotID   string            `json:"snapshot_id,omitempty"`
+	SourceVolID  string            `json:"source_volid,omitempty"`
+	ImageID      string            `json:"imageRef,omitempty"`
+	VolumeType   string            `json:"volume_type,omitempty"`
 }
 
 // ToVolumeCreateMap assembles a request body based on the contents of a
 // CreateOpts.
 func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) {
-	v := make(map[string]interface{})
-
-	if opts.Size == 0 {
-		return nil, fmt.Errorf("Required CreateOpts field 'Size' not set.")
-	}
-	v["size"] = opts.Size
-
-	if opts.Availability != "" {
-		v["availability_zone"] = opts.Availability
-	}
-	if opts.Description != "" {
-		v["display_description"] = opts.Description
-	}
-	if opts.ImageID != "" {
-		v["imageRef"] = opts.ImageID
-	}
-	if opts.Metadata != nil {
-		v["metadata"] = opts.Metadata
-	}
-	if opts.Name != "" {
-		v["display_name"] = opts.Name
-	}
-	if opts.SourceVolID != "" {
-		v["source_volid"] = opts.SourceVolID
-	}
-	if opts.SnapshotID != "" {
-		v["snapshot_id"] = opts.SnapshotID
-	}
-	if opts.VolumeType != "" {
-		v["volume_type"] = opts.VolumeType
-	}
-
-	return map[string]interface{}{"volume": v}, nil
+	return gophercloud.BuildRequestBody(opts, "volume")
 }
 
 // Create will create a new Volume based on the values in CreateOpts. To extract
 // the Volume object from the response, call the Extract method on the
 // CreateResult.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToVolumeCreateMap()
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToVolumeCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201},
 	})
-	return res
+	return
 }
 
 // Delete will delete the existing Volume with the provided ID.
-func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, id), nil)
-	return res
+func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = client.Delete(deleteURL(client, id), nil)
+	return
 }
 
 // Get retrieves the Volume with the provided ID. To extract the Volume object
 // from the response, call the Extract method on the GetResult.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return
 }
 
 // ListOptsBuilder allows extensions to add additional parameters to the List
@@ -118,7 +74,7 @@
 	// List only volumes that contain Metadata.
 	Metadata map[string]string `q:"metadata"`
 	// List only volumes that have Name as the display name.
-	Name string `q:"name"`
+	Name string `q:"display_name"`
 	// List only volumes that have a status of Status.
 	Status string `q:"status"`
 }
@@ -126,10 +82,7 @@
 // ToVolumeListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToVolumeListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns Volumes optionally limited by the conditions provided in ListOpts.
@@ -142,11 +95,9 @@
 		}
 		url += query
 	}
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return ListResult{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, url, createPage)
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
+		return VolumePage{pagination.SinglePageBase(r)}
+	})
 }
 
 // UpdateOptsBuilder allows extensions to add additional parameters to the
@@ -159,78 +110,58 @@
 // to the volumes.Update function. For more information about the parameters, see
 // the Volume object.
 type UpdateOpts struct {
-	// OPTIONAL
-	Name string
-	// OPTIONAL
-	Description string
-	// OPTIONAL
-	Metadata map[string]string
+	Name        string            `json:"display_name,omitempty"`
+	Description string            `json:"display_description,omitempty"`
+	Metadata    map[string]string `json:"metadata,omitempty"`
 }
 
 // ToVolumeUpdateMap assembles a request body based on the contents of an
 // UpdateOpts.
 func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) {
-	v := make(map[string]interface{})
-
-	if opts.Description != "" {
-		v["display_description"] = opts.Description
-	}
-	if opts.Metadata != nil {
-		v["metadata"] = opts.Metadata
-	}
-	if opts.Name != "" {
-		v["display_name"] = opts.Name
-	}
-
-	return map[string]interface{}{"volume": v}, nil
+	return gophercloud.BuildRequestBody(opts, "volume")
 }
 
 // Update will update the Volume with provided information. To extract the updated
 // Volume from the response, call the Extract method on the UpdateResult.
-func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToVolumeUpdateMap()
+func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToVolumeUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Put(updateURL(client, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // IDFromName is a convienience function that returns a server's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
-	volumeCount := 0
-	volumeID := ""
-	if name == "" {
-		return "", fmt.Errorf("A volume name must be provided.")
+	count := 0
+	id := ""
+	pages, err := List(client, nil).AllPages()
+	if err != nil {
+		return "", err
 	}
-	pager := List(client, nil)
-	pager.EachPage(func(page pagination.Page) (bool, error) {
-		volumeList, err := ExtractVolumes(page)
-		if err != nil {
-			return false, err
-		}
 
-		for _, s := range volumeList {
-			if s.Name == name {
-				volumeCount++
-				volumeID = s.ID
-			}
-		}
-		return true, nil
-	})
+	all, err := ExtractVolumes(pages)
+	if err != nil {
+		return "", err
+	}
 
-	switch volumeCount {
+	for _, s := range all {
+		if s.Name == name {
+			count++
+			id = s.ID
+		}
+	}
+
+	switch count {
 	case 0:
-		return "", fmt.Errorf("Unable to find volume: %s", name)
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"}
 	case 1:
-		return volumeID, nil
+		return id, nil
 	default:
-		return "", fmt.Errorf("Found %d volumes matching %s", volumeCount, name)
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"}
 	}
 }
diff --git a/openstack/blockstorage/v1/volumes/requests_test.go b/openstack/blockstorage/v1/volumes/requests_test.go
deleted file mode 100644
index 75c2bbc..0000000
--- a/openstack/blockstorage/v1/volumes/requests_test.go
+++ /dev/null
@@ -1,123 +0,0 @@
-package volumes
-
-import (
-	"testing"
-
-	fixtures "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/testing"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	fixtures.MockListResponse(t)
-
-	count := 0
-
-	List(client.ServiceClient(), &ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractVolumes(page)
-		if err != nil {
-			t.Errorf("Failed to extract volumes: %v", err)
-			return false, err
-		}
-
-		expected := []Volume{
-			Volume{
-				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
-				Name: "vol-001",
-			},
-			Volume{
-				ID:   "96c3bda7-c82a-4f50-be73-ca7621794835",
-				Name: "vol-002",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestListAll(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	fixtures.MockListResponse(t)
-
-	allPages, err := List(client.ServiceClient(), &ListOpts{}).AllPages()
-	th.AssertNoErr(t, err)
-	actual, err := ExtractVolumes(allPages)
-	th.AssertNoErr(t, err)
-
-	expected := []Volume{
-		Volume{
-			ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
-			Name: "vol-001",
-		},
-		Volume{
-			ID:   "96c3bda7-c82a-4f50-be73-ca7621794835",
-			Name: "vol-002",
-		},
-	}
-
-	th.CheckDeepEquals(t, expected, actual)
-
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	fixtures.MockGetResponse(t)
-
-	v, err := Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, v.Name, "vol-001")
-	th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertEquals(t, v.Attachments[0]["device"], "/dev/vde")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	fixtures.MockCreateResponse(t)
-
-	options := &CreateOpts{Size: 75}
-	n, err := Create(client.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Size, 4)
-	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	fixtures.MockDeleteResponse(t)
-
-	res := Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	fixtures.MockUpdateResponse(t)
-
-	options := UpdateOpts{Name: "vol-002"}
-	v, err := Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, "vol-002", v.Name)
-}
diff --git a/openstack/blockstorage/v1/volumes/results.go b/openstack/blockstorage/v1/volumes/results.go
index 2fd4ef1..b056b8c 100644
--- a/openstack/blockstorage/v1/volumes/results.go
+++ b/openstack/blockstorage/v1/volumes/results.go
@@ -1,52 +1,38 @@
 package volumes
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Volume contains all the information associated with an OpenStack Volume.
 type Volume struct {
 	// Current status of the volume.
-	Status string `mapstructure:"status"`
-
+	Status string `json:"status"`
 	// Human-readable display name for the volume.
-	Name string `mapstructure:"display_name"`
-
+	Name string `json:"display_name"`
 	// Instances onto which the volume is attached.
-	Attachments []map[string]interface{} `mapstructure:"attachments"`
-
+	Attachments []map[string]interface{} `json:"attachments"`
 	// This parameter is no longer used.
-	AvailabilityZone string `mapstructure:"availability_zone"`
-
+	AvailabilityZone string `json:"availability_zone"`
 	// Indicates whether this is a bootable volume.
-	Bootable string `mapstructure:"bootable"`
-
+	Bootable string `json:"bootable"`
 	// The date when this volume was created.
-	CreatedAt string `mapstructure:"created_at"`
-
+	CreatedAt gophercloud.JSONRFC3339Milli `json:"created_at"`
 	// Human-readable description for the volume.
-	Description string `mapstructure:"display_description"`
-
+	Description string `json:"display_description"`
 	// The type of volume to create, either SATA or SSD.
-	VolumeType string `mapstructure:"volume_type"`
-
+	VolumeType string `json:"volume_type"`
 	// The ID of the snapshot from which the volume was created
-	SnapshotID string `mapstructure:"snapshot_id"`
-
+	SnapshotID string `json:"snapshot_id"`
 	// The ID of another block storage volume from which the current volume was created
-	SourceVolID string `mapstructure:"source_volid"`
-
+	SourceVolID string `json:"source_volid"`
 	// Arbitrary key-value pairs defined by the user.
-	Metadata map[string]string `mapstructure:"metadata"`
-
+	Metadata map[string]string `json:"metadata"`
 	// Unique identifier for the volume.
-	ID string `mapstructure:"id"`
-
+	ID string `json:"id"`
 	// Size of the volume in GB.
-	Size int `mapstructure:"size"`
+	Size int `json:"size"`
 }
 
 // CreateResult contains the response body and error from a Create request.
@@ -64,28 +50,24 @@
 	gophercloud.ErrResult
 }
 
-// ListResult is a pagination.pager that is returned from a call to the List function.
-type ListResult struct {
+// VolumePage is a pagination.pager that is returned from a call to the List function.
+type VolumePage struct {
 	pagination.SinglePageBase
 }
 
-// IsEmpty returns true if a ListResult contains no Volumes.
-func (r ListResult) IsEmpty() (bool, error) {
+// IsEmpty returns true if a VolumePage contains no Volumes.
+func (r VolumePage) IsEmpty() (bool, error) {
 	volumes, err := ExtractVolumes(r)
-	if err != nil {
-		return true, err
-	}
-	return len(volumes) == 0, nil
+	return len(volumes) == 0, err
 }
 
 // ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call.
-func ExtractVolumes(page pagination.Page) ([]Volume, error) {
-	var response struct {
+func ExtractVolumes(r pagination.Page) ([]Volume, error) {
+	var s struct {
 		Volumes []Volume `json:"volumes"`
 	}
-
-	err := mapstructure.Decode(page.(ListResult).Body, &response)
-	return response.Volumes, err
+	err := (r.(VolumePage)).ExtractInto(&s)
+	return s.Volumes, err
 }
 
 // UpdateResult contains the response body and error from an Update request.
@@ -99,15 +81,9 @@
 
 // Extract will get the Volume object out of the commonResult object.
 func (r commonResult) Extract() (*Volume, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Volume *Volume `json:"volume"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Volume, err
+	err := r.ExtractInto(&s)
+	return s.Volume, err
 }
diff --git a/openstack/blockstorage/v1/volumes/testing/fixtures.go b/openstack/blockstorage/v1/volumes/testing/fixtures.go
index 5d1e669..421cbf4 100644
--- a/openstack/blockstorage/v1/volumes/testing/fixtures.go
+++ b/openstack/blockstorage/v1/volumes/testing/fixtures.go
@@ -1,5 +1,3 @@
-// +build fixtures
-
 package testing
 
 import (
@@ -7,8 +5,8 @@
 	"net/http"
 	"testing"
 
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
 )
 
 func MockListResponse(t *testing.T) {
@@ -44,20 +42,33 @@
 		w.Header().Add("Content-Type", "application/json")
 		w.WriteHeader(http.StatusOK)
 		fmt.Fprintf(w, `
-{
-    "volume": {
-        "display_name": "vol-001",
-        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
- 	"attachments": [
-	  {
-            "device": "/dev/vde",
-            "server_id": "a740d24b-dc5b-4d59-ac75-53971c2920ba",
-            "id": "d6da11e5-2ed3-413e-88d8-b772ba62193d",
-            "volume_id": "d6da11e5-2ed3-413e-88d8-b772ba62193d"
-          }
-        ]
-   }
-}
+			{
+			    "volume": {
+			        "id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+			        "display_name": "vol-001",
+			        "display_description": "Another volume.",
+			        "status": "active",
+			        "size": 30,
+			        "volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164",
+			        "metadata": {
+			            "contents": "junk"
+			        },
+			        "availability_zone": "us-east1",
+			        "bootable": "false",
+			        "snapshot_id": null,
+			        "attachments": [
+			            {
+			                "attachment_id": "03987cd1-0ad5-40d1-9b2a-7cc48295d4fa",
+			                "id": "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf",
+			                "volume_id": "6c80f8ac-e3e2-480c-8e6e-f1db92fe4bfe",
+			                "server_id": "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
+			                "host_name": "mitaka",
+			                "device": "/"
+			            }
+			        ],
+			        "created_at": "2012-02-14T20:53:07Z"
+			    }
+			}
       `)
 	})
 }
diff --git a/openstack/blockstorage/v1/volumes/testing/requests_test.go b/openstack/blockstorage/v1/volumes/testing/requests_test.go
new file mode 100644
index 0000000..85a6cd8
--- /dev/null
+++ b/openstack/blockstorage/v1/volumes/testing/requests_test.go
@@ -0,0 +1,150 @@
+package testing
+
+import (
+	"testing"
+	"time"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockListResponse(t)
+
+	count := 0
+
+	volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := volumes.ExtractVolumes(page)
+		if err != nil {
+			t.Errorf("Failed to extract volumes: %v", err)
+			return false, err
+		}
+
+		expected := []volumes.Volume{
+			{
+				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
+				Name: "vol-001",
+			},
+			{
+				ID:   "96c3bda7-c82a-4f50-be73-ca7621794835",
+				Name: "vol-002",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestListAll(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockListResponse(t)
+
+	allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := volumes.ExtractVolumes(allPages)
+	th.AssertNoErr(t, err)
+
+	expected := []volumes.Volume{
+		{
+			ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
+			Name: "vol-001",
+		},
+		{
+			ID:   "96c3bda7-c82a-4f50-be73-ca7621794835",
+			Name: "vol-002",
+		},
+	}
+
+	th.CheckDeepEquals(t, expected, actual)
+
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockGetResponse(t)
+
+	actual, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &volumes.Volume{
+		Status: "active",
+		Name:   "vol-001",
+		Attachments: []map[string]interface{}{
+			{
+				"attachment_id": "03987cd1-0ad5-40d1-9b2a-7cc48295d4fa",
+				"id":            "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf",
+				"volume_id":     "6c80f8ac-e3e2-480c-8e6e-f1db92fe4bfe",
+				"server_id":     "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
+				"host_name":     "mitaka",
+				"device":        "/",
+			},
+		},
+		AvailabilityZone: "us-east1",
+		Bootable:         "false",
+		CreatedAt:        gophercloud.JSONRFC3339Milli(time.Date(2012, 2, 14, 20, 53, 07, 0, time.UTC)),
+		Description:      "Another volume.",
+		VolumeType:       "289da7f8-6440-407c-9fb4-7db01ec49164",
+		SnapshotID:       "",
+		SourceVolID:      "",
+		Metadata: map[string]string{
+			"contents": "junk",
+		},
+		ID:   "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+		Size: 30,
+	}
+
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockCreateResponse(t)
+
+	options := &volumes.CreateOpts{Size: 75}
+	n, err := volumes.Create(client.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Size, 4)
+	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockDeleteResponse(t)
+
+	res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockUpdateResponse(t)
+
+	options := volumes.UpdateOpts{Name: "vol-002"}
+	v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract()
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, "vol-002", v.Name)
+}
diff --git a/openstack/blockstorage/v1/volumes/urls.go b/openstack/blockstorage/v1/volumes/urls.go
index 29629a1..8a00f97 100644
--- a/openstack/blockstorage/v1/volumes/urls.go
+++ b/openstack/blockstorage/v1/volumes/urls.go
@@ -1,6 +1,6 @@
 package volumes
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func createURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("volumes")
diff --git a/openstack/blockstorage/v1/volumes/urls_test.go b/openstack/blockstorage/v1/volumes/urls_test.go
deleted file mode 100644
index a95270e..0000000
--- a/openstack/blockstorage/v1/volumes/urls_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package volumes
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "volumes"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient())
-	expected := endpoint + "volumes"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/blockstorage/v1/volumes/util.go b/openstack/blockstorage/v1/volumes/util.go
index 1dda695..e86c1b4 100644
--- a/openstack/blockstorage/v1/volumes/util.go
+++ b/openstack/blockstorage/v1/volumes/util.go
@@ -1,7 +1,7 @@
 package volumes
 
 import (
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 // WaitForStatus will continually poll the resource, checking for a particular
diff --git a/openstack/blockstorage/v1/volumetypes/fixtures.go b/openstack/blockstorage/v1/volumetypes/fixtures.go
deleted file mode 100644
index e78f137..0000000
--- a/openstack/blockstorage/v1/volumetypes/fixtures.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// +build fixtures
-
-package volumetypes
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func MockListResponse(t *testing.T) {
-	th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-    {
-      "volume_types": [
-        {
-          "id": "289da7f8-6440-407c-9fb4-7db01ec49164",
-          "name": "vol-type-001",
-          "extra_specs": {
-            "capabilities": "gpu"
-            }
-        },
-        {
-          "id": "96c3bda7-c82a-4f50-be73-ca7621794835",
-          "name": "vol-type-002",
-          "extra_specs": {}
-        }
-      ]
-    }
-    `)
-	})
-}
-
-func MockGetResponse(t *testing.T) {
-	th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-{
-    "volume_type": {
-        "name": "vol-type-001",
-        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-    "extra_specs": {
-      "serverNumber": "2"
-    }
-    }
-}
-      `)
-	})
-}
diff --git a/openstack/blockstorage/v1/volumetypes/requests.go b/openstack/blockstorage/v1/volumetypes/requests.go
index 1673d13..b95c09a 100644
--- a/openstack/blockstorage/v1/volumetypes/requests.go
+++ b/openstack/blockstorage/v1/volumetypes/requests.go
@@ -1,8 +1,8 @@
 package volumetypes
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // CreateOptsBuilder allows extensions to add additional parameters to the
@@ -13,64 +13,47 @@
 
 // CreateOpts are options for creating a volume type.
 type CreateOpts struct {
-	// OPTIONAL. See VolumeType.
-	ExtraSpecs map[string]interface{}
-	// OPTIONAL. See VolumeType.
-	Name string
+	// See VolumeType.
+	ExtraSpecs map[string]interface{} `json:"extra_specs,omitempty"`
+	// See VolumeType.
+	Name string `json:"name,omitempty"`
 }
 
 // ToVolumeTypeCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) {
-	vt := make(map[string]interface{})
-
-	if opts.ExtraSpecs != nil {
-		vt["extra_specs"] = opts.ExtraSpecs
-	}
-	if opts.Name != "" {
-		vt["name"] = opts.Name
-	}
-
-	return map[string]interface{}{"volume_type": vt}, nil
+	return gophercloud.BuildRequestBody(opts, "volume_type")
 }
 
 // Create will create a new volume. To extract the created volume type object,
 // call the Extract method on the CreateResult.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToVolumeTypeCreateMap()
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToVolumeTypeCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201},
 	})
-	return res
+	return
 }
 
 // Delete will delete the volume type with the provided ID.
-func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, id), nil)
-	return res
+func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = client.Delete(deleteURL(client, id), nil)
+	return
 }
 
 // Get will retrieve the volume type with the provided ID. To extract the volume
 // type from the result, call the Extract method on the GetResult.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, err := client.Get(getURL(client, id), &res.Body, nil)
-	res.Err = err
-	return res
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return
 }
 
 // List returns all volume types.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return ListResult{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, listURL(client), createPage)
+	return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
+		return VolumeTypePage{pagination.SinglePageBase(r)}
+	})
 }
diff --git a/openstack/blockstorage/v1/volumetypes/requests_test.go b/openstack/blockstorage/v1/volumetypes/requests_test.go
deleted file mode 100644
index 8d40bfe..0000000
--- a/openstack/blockstorage/v1/volumetypes/requests_test.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package volumetypes
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockListResponse(t)
-
-	count := 0
-
-	List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractVolumeTypes(page)
-		if err != nil {
-			t.Errorf("Failed to extract volume types: %v", err)
-			return false, err
-		}
-
-		expected := []VolumeType{
-			VolumeType{
-				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
-				Name: "vol-type-001",
-				ExtraSpecs: map[string]interface{}{
-					"capabilities": "gpu",
-				},
-			},
-			VolumeType{
-				ID:         "96c3bda7-c82a-4f50-be73-ca7621794835",
-				Name:       "vol-type-002",
-				ExtraSpecs: map[string]interface{}{},
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockGetResponse(t)
-
-	vt, err := Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertDeepEquals(t, vt.ExtraSpecs, map[string]interface{}{"serverNumber": "2"})
-	th.AssertEquals(t, vt.Name, "vol-type-001")
-	th.AssertEquals(t, vt.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "volume_type": {
-        "name": "vol-type-001"
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "volume_type": {
-        "name": "vol-type-001",
-        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
-    }
-}
-		`)
-	})
-
-	options := &CreateOpts{Name: "vol-type-001"}
-	n, err := Create(client.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Name, "vol-type-001")
-	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-
-	err := Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/blockstorage/v1/volumetypes/results.go b/openstack/blockstorage/v1/volumetypes/results.go
index c049a04..2c31238 100644
--- a/openstack/blockstorage/v1/volumetypes/results.go
+++ b/openstack/blockstorage/v1/volumetypes/results.go
@@ -1,16 +1,15 @@
 package volumetypes
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // VolumeType contains all information associated with an OpenStack Volume Type.
 type VolumeType struct {
-	ExtraSpecs map[string]interface{} `json:"extra_specs" mapstructure:"extra_specs"` // user-defined metadata
-	ID         string                 `json:"id" mapstructure:"id"`                   // unique identifier
-	Name       string                 `json:"name" mapstructure:"name"`               // display name
+	ExtraSpecs map[string]interface{} `json:"extra_specs"` // user-defined metadata
+	ID         string                 `json:"id"`          // unique identifier
+	Name       string                 `json:"name"`        // display name
 }
 
 // CreateResult contains the response body and error from a Create request.
@@ -28,28 +27,24 @@
 	gophercloud.ErrResult
 }
 
-// ListResult is a pagination.Pager that is returned from a call to the List function.
-type ListResult struct {
+// VolumeTypePage is a pagination.Pager that is returned from a call to the List function.
+type VolumeTypePage struct {
 	pagination.SinglePageBase
 }
 
-// IsEmpty returns true if a ListResult contains no Volume Types.
-func (r ListResult) IsEmpty() (bool, error) {
+// IsEmpty returns true if a VolumeTypePage contains no Volume Types.
+func (r VolumeTypePage) IsEmpty() (bool, error) {
 	volumeTypes, err := ExtractVolumeTypes(r)
-	if err != nil {
-		return true, err
-	}
-	return len(volumeTypes) == 0, nil
+	return len(volumeTypes) == 0, err
 }
 
 // ExtractVolumeTypes extracts and returns Volume Types.
-func ExtractVolumeTypes(page pagination.Page) ([]VolumeType, error) {
-	var response struct {
-		VolumeTypes []VolumeType `mapstructure:"volume_types"`
+func ExtractVolumeTypes(r pagination.Page) ([]VolumeType, error) {
+	var s struct {
+		VolumeTypes []VolumeType `json:"volume_types"`
 	}
-
-	err := mapstructure.Decode(page.(ListResult).Body, &response)
-	return response.VolumeTypes, err
+	err := (r.(VolumeTypePage)).ExtractInto(&s)
+	return s.VolumeTypes, err
 }
 
 type commonResult struct {
@@ -58,15 +53,9 @@
 
 // Extract will get the Volume Type object out of the commonResult object.
 func (r commonResult) Extract() (*VolumeType, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		VolumeType *VolumeType `json:"volume_type"`
 	}
-
-	var res struct {
-		VolumeType *VolumeType `json:"volume_type" mapstructure:"volume_type"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.VolumeType, err
+	err := r.ExtractInto(&s)
+	return s.VolumeType, err
 }
diff --git a/openstack/blockstorage/v1/volumetypes/testing/doc.go b/openstack/blockstorage/v1/volumetypes/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/blockstorage/v1/volumetypes/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/blockstorage/v1/volumetypes/testing/fixtures.go b/openstack/blockstorage/v1/volumetypes/testing/fixtures.go
new file mode 100644
index 0000000..0e2715a
--- /dev/null
+++ b/openstack/blockstorage/v1/volumetypes/testing/fixtures.go
@@ -0,0 +1,60 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func MockListResponse(t *testing.T) {
+	th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+    {
+      "volume_types": [
+        {
+          "id": "289da7f8-6440-407c-9fb4-7db01ec49164",
+          "name": "vol-type-001",
+          "extra_specs": {
+            "capabilities": "gpu"
+            }
+        },
+        {
+          "id": "96c3bda7-c82a-4f50-be73-ca7621794835",
+          "name": "vol-type-002",
+          "extra_specs": {}
+        }
+      ]
+    }
+    `)
+	})
+}
+
+func MockGetResponse(t *testing.T) {
+	th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `
+{
+    "volume_type": {
+        "name": "vol-type-001",
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+    "extra_specs": {
+      "serverNumber": "2"
+    }
+    }
+}
+      `)
+	})
+}
diff --git a/openstack/blockstorage/v1/volumetypes/testing/requests_test.go b/openstack/blockstorage/v1/volumetypes/testing/requests_test.go
new file mode 100644
index 0000000..4244615
--- /dev/null
+++ b/openstack/blockstorage/v1/volumetypes/testing/requests_test.go
@@ -0,0 +1,119 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockListResponse(t)
+
+	count := 0
+
+	volumetypes.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := volumetypes.ExtractVolumeTypes(page)
+		if err != nil {
+			t.Errorf("Failed to extract volume types: %v", err)
+			return false, err
+		}
+
+		expected := []volumetypes.VolumeType{
+			{
+				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
+				Name: "vol-type-001",
+				ExtraSpecs: map[string]interface{}{
+					"capabilities": "gpu",
+				},
+			},
+			{
+				ID:         "96c3bda7-c82a-4f50-be73-ca7621794835",
+				Name:       "vol-type-002",
+				ExtraSpecs: map[string]interface{}{},
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockGetResponse(t)
+
+	vt, err := volumetypes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertDeepEquals(t, vt.ExtraSpecs, map[string]interface{}{"serverNumber": "2"})
+	th.AssertEquals(t, vt.Name, "vol-type-001")
+	th.AssertEquals(t, vt.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "volume_type": {
+        "name": "vol-type-001"
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "volume_type": {
+        "name": "vol-type-001",
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
+    }
+}
+		`)
+	})
+
+	options := &volumetypes.CreateOpts{Name: "vol-type-001"}
+	n, err := volumetypes.Create(client.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Name, "vol-type-001")
+	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		w.WriteHeader(http.StatusAccepted)
+	})
+
+	err := volumetypes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/blockstorage/v1/volumetypes/urls.go b/openstack/blockstorage/v1/volumetypes/urls.go
index cf8367b..822c7dd 100644
--- a/openstack/blockstorage/v1/volumetypes/urls.go
+++ b/openstack/blockstorage/v1/volumetypes/urls.go
@@ -1,6 +1,6 @@
 package volumetypes
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func listURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("types")
diff --git a/openstack/blockstorage/v1/volumetypes/urls_test.go b/openstack/blockstorage/v1/volumetypes/urls_test.go
deleted file mode 100644
index 44016e2..0000000
--- a/openstack/blockstorage/v1/volumetypes/urls_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package volumetypes
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient())
-	expected := endpoint + "types"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "types"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "types/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "foo")
-	expected := endpoint + "types/foo"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/blockstorage/v2/extensions/volumeactions/fixtures.go b/openstack/blockstorage/v2/extensions/volumeactions/fixtures.go
deleted file mode 100644
index 905d6df..0000000
--- a/openstack/blockstorage/v2/extensions/volumeactions/fixtures.go
+++ /dev/null
@@ -1,183 +0,0 @@
-package volumeactions
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func MockAttachResponse(t *testing.T) {
-	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
-		func(w http.ResponseWriter, r *http.Request) {
-			th.TestMethod(t, r, "POST")
-			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-			th.TestHeader(t, r, "Content-Type", "application/json")
-			th.TestHeader(t, r, "Accept", "application/json")
-			th.TestJSONRequest(t, r, `
-{
-    "os-attach":
-    {
-        "mountpoint": "/mnt",
-        "mode": "rw",
-        "instance_uuid": "50902f4f-a974-46a0-85e9-7efc5e22dfdd"
-    }
-}
-          `)
-
-			w.Header().Add("Content-Type", "application/json")
-			w.WriteHeader(http.StatusAccepted)
-
-			fmt.Fprintf(w, `{}`)
-		})
-}
-
-func MockDetachResponse(t *testing.T) {
-	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
-		func(w http.ResponseWriter, r *http.Request) {
-			th.TestMethod(t, r, "POST")
-			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-			th.TestHeader(t, r, "Content-Type", "application/json")
-			th.TestHeader(t, r, "Accept", "application/json")
-			th.TestJSONRequest(t, r, `
-{
-    "os-detach": {}
-}
-          `)
-
-			w.Header().Add("Content-Type", "application/json")
-			w.WriteHeader(http.StatusAccepted)
-
-			fmt.Fprintf(w, `{}`)
-		})
-}
-
-func MockReserveResponse(t *testing.T) {
-	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
-		func(w http.ResponseWriter, r *http.Request) {
-			th.TestMethod(t, r, "POST")
-			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-			th.TestHeader(t, r, "Content-Type", "application/json")
-			th.TestHeader(t, r, "Accept", "application/json")
-			th.TestJSONRequest(t, r, `
-{
-    "os-reserve": {}
-}
-          `)
-
-			w.Header().Add("Content-Type", "application/json")
-			w.WriteHeader(http.StatusAccepted)
-
-			fmt.Fprintf(w, `{}`)
-		})
-}
-
-func MockUnreserveResponse(t *testing.T) {
-	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
-		func(w http.ResponseWriter, r *http.Request) {
-			th.TestMethod(t, r, "POST")
-			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-			th.TestHeader(t, r, "Content-Type", "application/json")
-			th.TestHeader(t, r, "Accept", "application/json")
-			th.TestJSONRequest(t, r, `
-{
-    "os-unreserve": {}
-}
-          `)
-
-			w.Header().Add("Content-Type", "application/json")
-			w.WriteHeader(http.StatusAccepted)
-
-			fmt.Fprintf(w, `{}`)
-		})
-}
-
-func MockInitializeConnectionResponse(t *testing.T) {
-	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
-		func(w http.ResponseWriter, r *http.Request) {
-			th.TestMethod(t, r, "POST")
-			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-			th.TestHeader(t, r, "Content-Type", "application/json")
-			th.TestHeader(t, r, "Accept", "application/json")
-			th.TestJSONRequest(t, r, `
-{
-    "os-initialize_connection":
-    {
-        "connector":
-        {
-        "ip":"127.0.0.1",
-        "host":"stack",
-        "initiator":"iqn.1994-05.com.redhat:17cf566367d2",
-        "multipath": false,
-        "platform": "x86_64",
-        "os_type": "linux2"
-        }
-    }
-}
-          `)
-
-			w.Header().Add("Content-Type", "application/json")
-			w.WriteHeader(http.StatusAccepted)
-
-			fmt.Fprintf(w, `{
-"connection_info": {
-    "data": {
-      "target_portals": [
-        "172.31.17.48:3260"
-      ],
-      "auth_method": "CHAP",
-      "auth_username": "5MLtcsTEmNN5jFVcT6ui",
-      "access_mode": "rw",
-      "target_lun": 0,
-      "volume_id": "cd281d77-8217-4830-be95-9528227c105c",
-      "target_luns": [
-        0
-      ],
-      "target_iqns": [
-        "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c"
-      ],
-      "auth_password": "x854ZY5Re3aCkdNL",
-      "target_discovered": false,
-      "encrypted": false,
-      "qos_specs": null,
-      "target_iqn": "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c",
-      "target_portal": "172.31.17.48:3260"
-    },
-    "driver_volume_type": "iscsi"
-  }
-            }`)
-		})
-}
-
-func MockTerminateConnectionResponse(t *testing.T) {
-	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
-		func(w http.ResponseWriter, r *http.Request) {
-			th.TestMethod(t, r, "POST")
-			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-			th.TestHeader(t, r, "Content-Type", "application/json")
-			th.TestHeader(t, r, "Accept", "application/json")
-			th.TestJSONRequest(t, r, `
-{
-    "os-terminate_connection":
-    {
-        "connector":
-        {
-        "ip":"127.0.0.1",
-        "host":"stack",
-        "initiator":"iqn.1994-05.com.redhat:17cf566367d2",
-        "multipath": false,
-        "platform": "x86_64",
-        "os_type": "linux2"
-        }
-    }
-}
-          `)
-
-			w.Header().Add("Content-Type", "application/json")
-			w.WriteHeader(http.StatusAccepted)
-
-			fmt.Fprintf(w, `{}`)
-		})
-}
diff --git a/openstack/blockstorage/v2/extensions/volumeactions/requests.go b/openstack/blockstorage/v2/extensions/volumeactions/requests.go
index 842a659..05a76f7 100644
--- a/openstack/blockstorage/v2/extensions/volumeactions/requests.go
+++ b/openstack/blockstorage/v2/extensions/volumeactions/requests.go
@@ -1,7 +1,7 @@
 package volumeactions
 
 import (
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 // AttachOptsBuilder allows extensions to add additional parameters to the
@@ -22,180 +22,153 @@
 // AttachOpts contains options for attaching a Volume.
 type AttachOpts struct {
 	// The mountpoint of this volume
-	MountPoint string
+	MountPoint string `json:"mountpoint,omitempty"`
 	// The nova instance ID, can't set simultaneously with HostName
-	InstanceUUID string
+	InstanceUUID string `json:"instance_uuid,omitempty"`
 	// The hostname of baremetal host, can't set simultaneously with InstanceUUID
-	HostName string
+	HostName string `json:"host_name,omitempty"`
 	// Mount mode of this volume
-	Mode AttachMode
+	Mode AttachMode `json:"mode,omitempty"`
 }
 
 // ToVolumeAttachMap assembles a request body based on the contents of a
 // AttachOpts.
 func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) {
-	v := make(map[string]interface{})
-
-	if opts.MountPoint != "" {
-		v["mountpoint"] = opts.MountPoint
-	}
-	if opts.Mode != "" {
-		v["mode"] = opts.Mode
-	}
-	if opts.InstanceUUID != "" {
-		v["instance_uuid"] = opts.InstanceUUID
-	}
-	if opts.HostName != "" {
-		v["host_name"] = opts.HostName
-	}
-
-	return map[string]interface{}{"os-attach": v}, nil
+	return gophercloud.BuildRequestBody(opts, "os-attach")
 }
 
 // Attach will attach a volume based on the values in AttachOpts.
-func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) AttachResult {
-	var res AttachResult
-
-	reqBody, err := opts.ToVolumeAttachMap()
+func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) {
+	b, err := opts.ToVolumeAttachMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Post(attachURL(client, id), reqBody, nil, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(attachURL(client, id), b, nil, &gophercloud.RequestOpts{
 		OkCodes: []int{202},
 	})
-
-	return res
+	return
 }
 
-// Attach will detach a volume based on volume id.
-func Detach(client *gophercloud.ServiceClient, id string) DetachResult {
-	var res DetachResult
+// DetachOptsBuilder allows extensions to add additional parameters to the
+// Detach request.
+type DetachOptsBuilder interface {
+	ToVolumeDetachMap() (map[string]interface{}, error)
+}
 
-	v := make(map[string]interface{})
-	reqBody := map[string]interface{}{"os-detach": v}
+type DetachOpts struct {
+	AttachmentID string `json:"attachment_id,omitempty"`
+}
 
-	_, res.Err = client.Post(detachURL(client, id), reqBody, nil, &gophercloud.RequestOpts{
+// ToVolumeDetachMap assembles a request body based on the contents of a
+// DetachOpts.
+func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "os-detach")
+}
+
+// Detach will detach a volume based on volume id.
+func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) {
+	b, err := opts.ToVolumeDetachMap()
+	if err != nil {
+		r.Err = err
+		return
+	}
+	_, r.Err = client.Post(detachURL(client, id), b, nil, &gophercloud.RequestOpts{
 		OkCodes: []int{202},
 	})
-
-	return res
+	return
 }
 
 // Reserve will reserve a volume based on volume id.
-func Reserve(client *gophercloud.ServiceClient, id string) ReserveResult {
-	var res ReserveResult
-
-	v := make(map[string]interface{})
-	reqBody := map[string]interface{}{"os-reserve": v}
-
-	_, res.Err = client.Post(reserveURL(client, id), reqBody, nil, &gophercloud.RequestOpts{
+func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) {
+	b := map[string]interface{}{"os-reserve": make(map[string]interface{})}
+	_, r.Err = client.Post(reserveURL(client, id), b, nil, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201, 202},
 	})
-
-	return res
+	return
 }
 
 // Unreserve will unreserve a volume based on volume id.
-func Unreserve(client *gophercloud.ServiceClient, id string) UnreserveResult {
-	var res UnreserveResult
-
-	v := make(map[string]interface{})
-	reqBody := map[string]interface{}{"os-unreserve": v}
-
-	_, res.Err = client.Post(unreserveURL(client, id), reqBody, nil, &gophercloud.RequestOpts{
+func Unreserve(client *gophercloud.ServiceClient, id string) (r UnreserveResult) {
+	b := map[string]interface{}{"os-unreserve": make(map[string]interface{})}
+	_, r.Err = client.Post(unreserveURL(client, id), b, nil, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201, 202},
 	})
-
-	return res
+	return
 }
 
-// ConnectorOptsBuilder allows extensions to add additional parameters to the
+// InitializeConnectionOptsBuilder allows extensions to add additional parameters to the
 // InitializeConnection request.
-type ConnectorOptsBuilder interface {
-	ToConnectorMap() (map[string]interface{}, error)
+type InitializeConnectionOptsBuilder interface {
+	ToVolumeInitializeConnectionMap() (map[string]interface{}, error)
 }
 
-// ConnectorOpts hosts options for InitializeConnection.
-type ConnectorOpts struct {
-	IP        string
-	Host      string
-	Initiator string
-	Wwpns     []string
-	Wwnns     string
-	Multipath bool
-	Platform  string
-	OSType    string
+// InitializeConnectionOpts hosts options for InitializeConnection.
+type InitializeConnectionOpts struct {
+	IP        string   `json:"ip,omitempty"`
+	Host      string   `json:"host,omitempty"`
+	Initiator string   `json:"initiator,omitempty"`
+	Wwpns     []string `json:"wwpns,omitempty"`
+	Wwnns     string   `json:"wwnns,omitempty"`
+	Multipath *bool    `json:"multipath,omitempty"`
+	Platform  string   `json:"platform,omitempty"`
+	OSType    string   `json:"os_type,omitempty"`
 }
 
-// ToConnectorMap assembles a request body based on the contents of a
-// ConnectorOpts.
-func (opts ConnectorOpts) ToConnectorMap() (map[string]interface{}, error) {
-	v := make(map[string]interface{})
-
-	if opts.IP != "" {
-		v["ip"] = opts.IP
-	}
-	if opts.Host != "" {
-		v["host"] = opts.Host
-	}
-	if opts.Initiator != "" {
-		v["initiator"] = opts.Initiator
-	}
-	if opts.Wwpns != nil {
-		v["wwpns"] = opts.Wwpns
-	}
-	if opts.Wwnns != "" {
-		v["wwnns"] = opts.Wwnns
-	}
-
-	v["multipath"] = opts.Multipath
-
-	if opts.Platform != "" {
-		v["platform"] = opts.Platform
-	}
-	if opts.OSType != "" {
-		v["os_type"] = opts.OSType
-	}
-
-	return map[string]interface{}{"connector": v}, nil
+// ToVolumeInitializeConnectionMap assembles a request body based on the contents of a
+// InitializeConnectionOpts.
+func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]interface{}, error) {
+	b, err := gophercloud.BuildRequestBody(opts, "connector")
+	return map[string]interface{}{"os-initialize_connection": b}, err
 }
 
 // InitializeConnection initializes iscsi connection.
-func InitializeConnection(client *gophercloud.ServiceClient, id string, opts *ConnectorOpts) InitializeConnectionResult {
-	var res InitializeConnectionResult
-
-	connctorMap, err := opts.ToConnectorMap()
+func InitializeConnection(client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) {
+	b, err := opts.ToVolumeInitializeConnectionMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	reqBody := map[string]interface{}{"os-initialize_connection": connctorMap}
-
-	_, res.Err = client.Post(initializeConnectionURL(client, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(initializeConnectionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201, 202},
 	})
+	return
+}
 
-	return res
+// TerminateConnectionOptsBuilder allows extensions to add additional parameters to the
+// TerminateConnection request.
+type TerminateConnectionOptsBuilder interface {
+	ToVolumeTerminateConnectionMap() (map[string]interface{}, error)
+}
+
+// TerminateConnectionOpts hosts options for TerminateConnection.
+type TerminateConnectionOpts struct {
+	IP        string   `json:"ip,omitempty"`
+	Host      string   `json:"host,omitempty"`
+	Initiator string   `json:"initiator,omitempty"`
+	Wwpns     []string `json:"wwpns,omitempty"`
+	Wwnns     string   `json:"wwnns,omitempty"`
+	Multipath *bool    `json:"multipath,omitempty"`
+	Platform  string   `json:"platform,omitempty"`
+	OSType    string   `json:"os_type,omitempty"`
+}
+
+// ToVolumeTerminateConnectionMap assembles a request body based on the contents of a
+// TerminateConnectionOpts.
+func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) {
+	b, err := gophercloud.BuildRequestBody(opts, "connector")
+	return map[string]interface{}{"os-terminate_connection": b}, err
 }
 
 // TerminateConnection terminates iscsi connection.
-func TerminateConnection(client *gophercloud.ServiceClient, id string, opts *ConnectorOpts) TerminateConnectionResult {
-	var res TerminateConnectionResult
-
-	connctorMap, err := opts.ToConnectorMap()
+func TerminateConnection(client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) {
+	b, err := opts.ToVolumeTerminateConnectionMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	reqBody := map[string]interface{}{"os-terminate_connection": connctorMap}
-
-	_, res.Err = client.Post(teminateConnectionURL(client, id), reqBody, nil, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(teminateConnectionURL(client, id), b, nil, &gophercloud.RequestOpts{
 		OkCodes: []int{202},
 	})
-
-	return res
+	return
 }
diff --git a/openstack/blockstorage/v2/extensions/volumeactions/requests_test.go b/openstack/blockstorage/v2/extensions/volumeactions/requests_test.go
deleted file mode 100644
index 5657287..0000000
--- a/openstack/blockstorage/v2/extensions/volumeactions/requests_test.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package volumeactions
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestAttach(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockAttachResponse(t)
-
-	options := &AttachOpts{
-		MountPoint:   "/mnt",
-		Mode:         "rw",
-		InstanceUUID: "50902f4f-a974-46a0-85e9-7efc5e22dfdd",
-	}
-	err := Attach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDetach(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockDetachResponse(t)
-
-	err := Detach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestReserve(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockReserveResponse(t)
-
-	err := Reserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestUnreserve(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockUnreserveResponse(t)
-
-	err := Unreserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestInitializeConnection(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockInitializeConnectionResponse(t)
-
-	options := &ConnectorOpts{
-		IP:        "127.0.0.1",
-		Host:      "stack",
-		Initiator: "iqn.1994-05.com.redhat:17cf566367d2",
-		Multipath: false,
-		Platform:  "x86_64",
-		OSType:    "linux2",
-	}
-	_, err := InitializeConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestTerminateConnection(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockTerminateConnectionResponse(t)
-
-	options := &ConnectorOpts{
-		IP:        "127.0.0.1",
-		Host:      "stack",
-		Initiator: "iqn.1994-05.com.redhat:17cf566367d2",
-		Multipath: false,
-		Platform:  "x86_64",
-		OSType:    "linux2",
-	}
-	err := TerminateConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/blockstorage/v2/extensions/volumeactions/results.go b/openstack/blockstorage/v2/extensions/volumeactions/results.go
index 65778e5..9bf6a7b 100644
--- a/openstack/blockstorage/v2/extensions/volumeactions/results.go
+++ b/openstack/blockstorage/v2/extensions/volumeactions/results.go
@@ -1,13 +1,6 @@
 package volumeactions
 
-import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-)
-
-type commonResult struct {
-	gophercloud.Result
-}
+import "github.com/gophercloud/gophercloud"
 
 // AttachResult contains the response body and error from a Get request.
 type AttachResult struct {
@@ -29,24 +22,25 @@
 	gophercloud.ErrResult
 }
 
-// InitializeConnectionResult contains the response body and error from a Get request.
-type InitializeConnectionResult struct {
-	commonResult
-}
-
 // TerminateConnectionResult contains the response body and error from a Get request.
 type TerminateConnectionResult struct {
 	gophercloud.ErrResult
 }
 
+type commonResult struct {
+	gophercloud.Result
+}
+
 // Extract will get the Volume object out of the commonResult object.
 func (r commonResult) Extract() (map[string]interface{}, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		ConnectionInfo map[string]interface{} `json:"connection_info"`
 	}
+	err := r.ExtractInto(&s)
+	return s.ConnectionInfo, err
+}
 
-	var res map[string]interface{}
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res, err
+// InitializeConnectionResult contains the response body and error from a Get request.
+type InitializeConnectionResult struct {
+	commonResult
 }
diff --git a/openstack/blockstorage/v2/extensions/volumeactions/testing/fixtures.go b/openstack/blockstorage/v2/extensions/volumeactions/testing/fixtures.go
new file mode 100644
index 0000000..da661a6
--- /dev/null
+++ b/openstack/blockstorage/v2/extensions/volumeactions/testing/fixtures.go
@@ -0,0 +1,183 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func MockAttachResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
+		func(w http.ResponseWriter, r *http.Request) {
+			th.TestMethod(t, r, "POST")
+			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+			th.TestHeader(t, r, "Content-Type", "application/json")
+			th.TestHeader(t, r, "Accept", "application/json")
+			th.TestJSONRequest(t, r, `
+{
+    "os-attach":
+    {
+        "mountpoint": "/mnt",
+        "mode": "rw",
+        "instance_uuid": "50902f4f-a974-46a0-85e9-7efc5e22dfdd"
+    }
+}
+          `)
+
+			w.Header().Add("Content-Type", "application/json")
+			w.WriteHeader(http.StatusAccepted)
+
+			fmt.Fprintf(w, `{}`)
+		})
+}
+
+func MockDetachResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
+		func(w http.ResponseWriter, r *http.Request) {
+			th.TestMethod(t, r, "POST")
+			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+			th.TestHeader(t, r, "Content-Type", "application/json")
+			th.TestHeader(t, r, "Accept", "application/json")
+			th.TestJSONRequest(t, r, `
+{
+    "os-detach": {}
+}
+          `)
+
+			w.Header().Add("Content-Type", "application/json")
+			w.WriteHeader(http.StatusAccepted)
+
+			fmt.Fprintf(w, `{}`)
+		})
+}
+
+func MockReserveResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
+		func(w http.ResponseWriter, r *http.Request) {
+			th.TestMethod(t, r, "POST")
+			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+			th.TestHeader(t, r, "Content-Type", "application/json")
+			th.TestHeader(t, r, "Accept", "application/json")
+			th.TestJSONRequest(t, r, `
+{
+    "os-reserve": {}
+}
+          `)
+
+			w.Header().Add("Content-Type", "application/json")
+			w.WriteHeader(http.StatusAccepted)
+
+			fmt.Fprintf(w, `{}`)
+		})
+}
+
+func MockUnreserveResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
+		func(w http.ResponseWriter, r *http.Request) {
+			th.TestMethod(t, r, "POST")
+			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+			th.TestHeader(t, r, "Content-Type", "application/json")
+			th.TestHeader(t, r, "Accept", "application/json")
+			th.TestJSONRequest(t, r, `
+{
+    "os-unreserve": {}
+}
+          `)
+
+			w.Header().Add("Content-Type", "application/json")
+			w.WriteHeader(http.StatusAccepted)
+
+			fmt.Fprintf(w, `{}`)
+		})
+}
+
+func MockInitializeConnectionResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
+		func(w http.ResponseWriter, r *http.Request) {
+			th.TestMethod(t, r, "POST")
+			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+			th.TestHeader(t, r, "Content-Type", "application/json")
+			th.TestHeader(t, r, "Accept", "application/json")
+			th.TestJSONRequest(t, r, `
+{
+    "os-initialize_connection":
+    {
+        "connector":
+        {
+        "ip":"127.0.0.1",
+        "host":"stack",
+        "initiator":"iqn.1994-05.com.redhat:17cf566367d2",
+        "multipath": false,
+        "platform": "x86_64",
+        "os_type": "linux2"
+        }
+    }
+}
+          `)
+
+			w.Header().Add("Content-Type", "application/json")
+			w.WriteHeader(http.StatusAccepted)
+
+			fmt.Fprintf(w, `{
+"connection_info": {
+    "data": {
+      "target_portals": [
+        "172.31.17.48:3260"
+      ],
+      "auth_method": "CHAP",
+      "auth_username": "5MLtcsTEmNN5jFVcT6ui",
+      "access_mode": "rw",
+      "target_lun": 0,
+      "volume_id": "cd281d77-8217-4830-be95-9528227c105c",
+      "target_luns": [
+        0
+      ],
+      "target_iqns": [
+        "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c"
+      ],
+      "auth_password": "x854ZY5Re3aCkdNL",
+      "target_discovered": false,
+      "encrypted": false,
+      "qos_specs": null,
+      "target_iqn": "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c",
+      "target_portal": "172.31.17.48:3260"
+    },
+    "driver_volume_type": "iscsi"
+  }
+            }`)
+		})
+}
+
+func MockTerminateConnectionResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
+		func(w http.ResponseWriter, r *http.Request) {
+			th.TestMethod(t, r, "POST")
+			th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+			th.TestHeader(t, r, "Content-Type", "application/json")
+			th.TestHeader(t, r, "Accept", "application/json")
+			th.TestJSONRequest(t, r, `
+{
+    "os-terminate_connection":
+    {
+        "connector":
+        {
+        "ip":"127.0.0.1",
+        "host":"stack",
+        "initiator":"iqn.1994-05.com.redhat:17cf566367d2",
+        "multipath": true,
+        "platform": "x86_64",
+        "os_type": "linux2"
+        }
+    }
+}
+          `)
+
+			w.Header().Add("Content-Type", "application/json")
+			w.WriteHeader(http.StatusAccepted)
+
+			fmt.Fprintf(w, `{}`)
+		})
+}
diff --git a/openstack/blockstorage/v2/extensions/volumeactions/testing/requests_test.go b/openstack/blockstorage/v2/extensions/volumeactions/testing/requests_test.go
new file mode 100644
index 0000000..ed047d5
--- /dev/null
+++ b/openstack/blockstorage/v2/extensions/volumeactions/testing/requests_test.go
@@ -0,0 +1,91 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/extensions/volumeactions"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+	"github.com/jrperritt/gophercloud"
+)
+
+func TestAttach(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockAttachResponse(t)
+
+	options := &volumeactions.AttachOpts{
+		MountPoint:   "/mnt",
+		Mode:         "rw",
+		InstanceUUID: "50902f4f-a974-46a0-85e9-7efc5e22dfdd",
+	}
+	err := volumeactions.Attach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestDetach(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockDetachResponse(t)
+
+	err := volumeactions.Detach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", &volumeactions.DetachOpts{}).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestReserve(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockReserveResponse(t)
+
+	err := volumeactions.Reserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestUnreserve(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockUnreserveResponse(t)
+
+	err := volumeactions.Unreserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestInitializeConnection(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockInitializeConnectionResponse(t)
+
+	options := &volumeactions.InitializeConnectionOpts{
+		IP:        "127.0.0.1",
+		Host:      "stack",
+		Initiator: "iqn.1994-05.com.redhat:17cf566367d2",
+		Multipath: gophercloud.Disabled,
+		Platform:  "x86_64",
+		OSType:    "linux2",
+	}
+	_, err := volumeactions.InitializeConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestTerminateConnection(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockTerminateConnectionResponse(t)
+
+	options := &volumeactions.TerminateConnectionOpts{
+		IP:        "127.0.0.1",
+		Host:      "stack",
+		Initiator: "iqn.1994-05.com.redhat:17cf566367d2",
+		Multipath: gophercloud.Enabled,
+		Platform:  "x86_64",
+		OSType:    "linux2",
+	}
+	err := volumeactions.TerminateConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/blockstorage/v2/extensions/volumeactions/urls.go b/openstack/blockstorage/v2/extensions/volumeactions/urls.go
index 4b3c5f2..4ddcca0 100644
--- a/openstack/blockstorage/v2/extensions/volumeactions/urls.go
+++ b/openstack/blockstorage/v2/extensions/volumeactions/urls.go
@@ -1,6 +1,6 @@
 package volumeactions
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func attachURL(c *gophercloud.ServiceClient, id string) string {
 	return c.ServiceURL("volumes", id, "action")
diff --git a/openstack/blockstorage/v2/extensions/volumeactions/urls_test.go b/openstack/blockstorage/v2/extensions/volumeactions/urls_test.go
deleted file mode 100644
index ff4a5d3..0000000
--- a/openstack/blockstorage/v2/extensions/volumeactions/urls_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package volumeactions
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestAttachURL(t *testing.T) {
-	actual := attachURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo/action"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDettachURL(t *testing.T) {
-	actual := detachURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo/action"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestReserveURL(t *testing.T) {
-	actual := reserveURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo/action"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestUnreserveURL(t *testing.T) {
-	actual := unreserveURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo/action"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestInitializeConnectionURL(t *testing.T) {
-	actual := initializeConnectionURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo/action"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestTeminateConnectionURL(t *testing.T) {
-	actual := teminateConnectionURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo/action"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/blockstorage/v2/volumes/fixtures.go b/openstack/blockstorage/v2/volumes/fixtures.go
deleted file mode 100644
index b70e298..0000000
--- a/openstack/blockstorage/v2/volumes/fixtures.go
+++ /dev/null
@@ -1,204 +0,0 @@
-package volumes
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func MockListResponse(t *testing.T) {
-	th.Mux.HandleFunc("/volumes/detail", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-  {
-  "volumes": [
-    {
-      "volume_type": "lvmdriver-1",
-      "created_at": "2015-09-17T03:35:03.000000",
-      "bootable": "false",
-      "name": "vol-001",
-      "os-vol-mig-status-attr:name_id": null,
-      "consistencygroup_id": null,
-      "source_volid": null,
-      "os-volume-replication:driver_data": null,
-      "multiattach": false,
-      "snapshot_id": null,
-      "replication_status": "disabled",
-      "os-volume-replication:extended_status": null,
-      "encrypted": false,
-      "os-vol-host-attr:host": null,
-      "availability_zone": "nova",
-      "attachments": [
-        {
-        "attachment_id": "03987cd1-0ad5-40d1-9b2a-7cc48295d4fa",
-        "id": "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf",
-        "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164",
-        "server_id": "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
-        "host_name": "stack",
-        "device": "/dev/vdc"
-        }
-      ],
-      "id": "289da7f8-6440-407c-9fb4-7db01ec49164",
-      "size": 75,
-      "user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
-      "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459",
-      "os-vol-mig-status-attr:migstat": null,
-      "metadata": {"foo": "bar"},
-      "status": "available",
-      "description": null
-    },
-    {
-      "volume_type": "lvmdriver-1",
-      "created_at": "2015-09-17T03:32:29.000000",
-      "bootable": "false",
-      "name": "vol-002",
-      "os-vol-mig-status-attr:name_id": null,
-      "consistencygroup_id": null,
-      "source_volid": null,
-      "os-volume-replication:driver_data": null,
-      "multiattach": false,
-      "snapshot_id": null,
-      "replication_status": "disabled",
-      "os-volume-replication:extended_status": null,
-      "encrypted": false,
-      "os-vol-host-attr:host": null,
-      "availability_zone": "nova",
-      "attachments": [],
-      "id": "96c3bda7-c82a-4f50-be73-ca7621794835",
-      "size": 75,
-      "user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
-      "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459",
-      "os-vol-mig-status-attr:migstat": null,
-      "metadata": {},
-      "status": "available",
-      "description": null
-    }
-  ]
-}
-
-  `)
-	})
-}
-
-func MockGetResponse(t *testing.T) {
-	th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-{
-  "volume": {
-    "volume_type": "lvmdriver-1",
-    "created_at": "2015-09-17T03:32:29.000000",
-    "bootable": "false",
-    "name": "vol-001",
-    "os-vol-mig-status-attr:name_id": null,
-    "consistencygroup_id": null,
-    "source_volid": null,
-    "os-volume-replication:driver_data": null,
-    "multiattach": false,
-    "snapshot_id": null,
-    "replication_status": "disabled",
-    "os-volume-replication:extended_status": null,
-    "encrypted": false,
-    "os-vol-host-attr:host": null,
-    "availability_zone": "nova",
-    "attachments": [{
-        "attachment_id": "dbce64e3-f3b9-4423-a44f-a2b15deffa1b",
-        "id": "3eafc6f5-ed74-456d-90fb-f253f594dbae",
-        "volume_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-        "server_id": "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
-        "host_name": "stack",
-        "device": "/dev/vdd"
-        }],
-    "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-    "size": 75,
-    "user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
-    "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459",
-    "os-vol-mig-status-attr:migstat": null,
-    "metadata": {},
-    "status": "available",
-    "description": null
-  }
-}
-      `)
-	})
-}
-
-func MockCreateResponse(t *testing.T) {
-	th.Mux.HandleFunc("/volumes", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "volume": {
-    	"name": "vol-001",
-        "size": 75
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-
-		fmt.Fprintf(w, `
-{
-  "volume": {
-    "size": 75,
-    "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-    "metadata": {},
-    "created_at": "2015-09-17T03:32:29.044216",
-    "encrypted": false,
-    "bootable": "false",
-    "availability_zone": "nova",
-    "attachments": [],
-    "user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
-    "status": "creating",
-    "description": null,
-    "volume_type": "lvmdriver-1",
-    "name": "vol-001",
-    "replication_status": "disabled",
-    "consistencygroup_id": null,
-    "source_volid": null,
-    "snapshot_id": null,
-    "multiattach": false
-  }
-}
-    `)
-	})
-}
-
-func MockDeleteResponse(t *testing.T) {
-	th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func MockUpdateResponse(t *testing.T) {
-	th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-{
-  "volume": {
-    "name": "vol-002"
-  }
-}
-        `)
-	})
-}
diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go
index 4c60936..18c9cb2 100644
--- a/openstack/blockstorage/v2/volumes/requests.go
+++ b/openstack/blockstorage/v2/volumes/requests.go
@@ -1,10 +1,8 @@
 package volumes
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // CreateOptsBuilder allows extensions to add additional parameters to the
@@ -17,106 +15,63 @@
 // the volumes.Create function. For more information about these parameters,
 // see the Volume object.
 type CreateOpts struct {
-	// The availability zone [OPTIONAL]
-	AvailabilityZone string
-	// ConsistencyGroupID is the ID of a consistency group [OPTINAL]
-	ConsistencyGroupID string
-	// The volume description [OPTIONAL]
-	Description string
-	// One or more metadata key and value pairs to associate with the volume [OPTIONAL]
-	Metadata map[string]string
-	// The volume name [OPTIONAL]
-	Name string
-	// The size of the volume, in gibibytes (GiB) [REQUIRED]
-	Size int
-	// the ID of the existing volume snapshot [OPTIONAL]
-	SnapshotID string
-	// SourceReplica is a UUID of an existing volume to replicate with [OPTIONAL]
-	SourceReplica string
-	// the ID of the existing volume [OPTIONAL]
-	SourceVolID string
+	// The size of the volume, in GB
+	Size int `json:"size" required:"true"`
+	// The availability zone
+	AvailabilityZone string `json:"availability_zone,omitempty"`
+	// ConsistencyGroupID is the ID of a consistency group
+	ConsistencyGroupID string `json:"consistencygroup_id,omitempty"`
+	// The volume description
+	Description string `json:"description,omitempty"`
+	// One or more metadata key and value pairs to associate with the volume
+	Metadata map[string]string `json:"metadata,omitempty"`
+	// The volume name
+	Name string `json:"name,omitempty"`
+	// the ID of the existing volume snapshot
+	SnapshotID string `json:"snapshot_id,omitempty"`
+	// SourceReplica is a UUID of an existing volume to replicate with
+	SourceReplica string `json:"source_replica,omitempty"`
+	// the ID of the existing volume
+	SourceVolID string `json:"source_volid,omitempty"`
 	// The ID of the image from which you want to create the volume.
 	// Required to create a bootable volume.
-	ImageID string
-	// The associated volume type [OPTIONAL]
-	VolumeType string
+	ImageID string `json:"imageRef,omitempty"`
+	// The associated volume type
+	VolumeType string `json:"volume_type,omitempty"`
 }
 
 // ToVolumeCreateMap assembles a request body based on the contents of a
 // CreateOpts.
 func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) {
-	v := make(map[string]interface{})
-
-	if opts.Size == 0 {
-		return nil, fmt.Errorf("Required CreateOpts field 'Size' not set.")
-	}
-	v["size"] = opts.Size
-
-	if opts.AvailabilityZone != "" {
-		v["availability_zone"] = opts.AvailabilityZone
-	}
-	if opts.ConsistencyGroupID != "" {
-		v["consistencygroup_id"] = opts.ConsistencyGroupID
-	}
-	if opts.Description != "" {
-		v["description"] = opts.Description
-	}
-	if opts.ImageID != "" {
-		v["imageRef"] = opts.ImageID
-	}
-	if opts.Metadata != nil {
-		v["metadata"] = opts.Metadata
-	}
-	if opts.Name != "" {
-		v["name"] = opts.Name
-	}
-	if opts.SourceReplica != "" {
-		v["source_replica"] = opts.SourceReplica
-	}
-	if opts.SourceVolID != "" {
-		v["source_volid"] = opts.SourceVolID
-	}
-	if opts.SnapshotID != "" {
-		v["snapshot_id"] = opts.SnapshotID
-	}
-	if opts.VolumeType != "" {
-		v["volume_type"] = opts.VolumeType
-	}
-
-	return map[string]interface{}{"volume": v}, nil
+	return gophercloud.BuildRequestBody(opts, "volume")
 }
 
 // Create will create a new Volume based on the values in CreateOpts. To extract
 // the Volume object from the response, call the Extract method on the
 // CreateResult.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToVolumeCreateMap()
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToVolumeCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{202},
 	})
-	return res
+	return
 }
 
 // Delete will delete the existing Volume with the provided ID.
-func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, id), nil)
-	return res
+func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = client.Delete(deleteURL(client, id), nil)
+	return
 }
 
 // Get retrieves the Volume with the provided ID. To extract the Volume object
 // from the response, call the Extract method on the GetResult.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return
 }
 
 // ListOptsBuilder allows extensions to add additional parameters to the List
@@ -141,10 +96,7 @@
 // ToVolumeListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToVolumeListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns Volumes optionally limited by the conditions provided in ListOpts.
@@ -157,11 +109,10 @@
 		}
 		url += query
 	}
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return ListResult{pagination.SinglePageBase(r)}
-	}
 
-	return pagination.NewPager(client, url, createPage)
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
+		return VolumePage{pagination.SinglePageBase(r)}
+	})
 }
 
 // UpdateOptsBuilder allows extensions to add additional parameters to the
@@ -174,78 +125,58 @@
 // to the volumes.Update function. For more information about the parameters, see
 // the Volume object.
 type UpdateOpts struct {
-	// OPTIONAL
-	Name string
-	// OPTIONAL
-	Description string
-	// OPTIONAL
-	Metadata map[string]string
+	Name        string            `json:"name,omitempty"`
+	Description string            `json:"description,omitempty"`
+	Metadata    map[string]string `json:"metadata,omitempty"`
 }
 
 // ToVolumeUpdateMap assembles a request body based on the contents of an
 // UpdateOpts.
 func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) {
-	v := make(map[string]interface{})
-
-	if opts.Description != "" {
-		v["description"] = opts.Description
-	}
-	if opts.Metadata != nil {
-		v["metadata"] = opts.Metadata
-	}
-	if opts.Name != "" {
-		v["name"] = opts.Name
-	}
-
-	return map[string]interface{}{"volume": v}, nil
+	return gophercloud.BuildRequestBody(opts, "volume")
 }
 
 // Update will update the Volume with provided information. To extract the updated
 // Volume from the response, call the Extract method on the UpdateResult.
-func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToVolumeUpdateMap()
+func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToVolumeUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Put(updateURL(client, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // IDFromName is a convienience function that returns a server's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
-	volumeCount := 0
-	volumeID := ""
-	if name == "" {
-		return "", fmt.Errorf("A volume name must be provided.")
+	count := 0
+	id := ""
+	pages, err := List(client, nil).AllPages()
+	if err != nil {
+		return "", err
 	}
-	pager := List(client, nil)
-	pager.EachPage(func(page pagination.Page) (bool, error) {
-		volumeList, err := ExtractVolumes(page)
-		if err != nil {
-			return false, err
-		}
 
-		for _, s := range volumeList {
-			if s.Name == name {
-				volumeCount++
-				volumeID = s.ID
-			}
-		}
-		return true, nil
-	})
+	all, err := ExtractVolumes(pages)
+	if err != nil {
+		return "", err
+	}
 
-	switch volumeCount {
+	for _, s := range all {
+		if s.Name == name {
+			count++
+			id = s.ID
+		}
+	}
+
+	switch count {
 	case 0:
-		return "", fmt.Errorf("Unable to find volume: %s", name)
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"}
 	case 1:
-		return volumeID, nil
+		return id, nil
 	default:
-		return "", fmt.Errorf("Found %d volumes matching %s", volumeCount, name)
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"}
 	}
 }
diff --git a/openstack/blockstorage/v2/volumes/requests_test.go b/openstack/blockstorage/v2/volumes/requests_test.go
deleted file mode 100644
index 962d94b..0000000
--- a/openstack/blockstorage/v2/volumes/requests_test.go
+++ /dev/null
@@ -1,211 +0,0 @@
-package volumes
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockListResponse(t)
-
-	count := 0
-
-	List(client.ServiceClient(), &ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractVolumes(page)
-		if err != nil {
-			t.Errorf("Failed to extract volumes: %v", err)
-			return false, err
-		}
-
-		expected := []Volume{
-			{
-				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
-				Name: "vol-001",
-				Attachments: []map[string]interface{}{{
-					"AttachmentID": "03987cd1-0ad5-40d1-9b2a-7cc48295d4fa",
-					"ID":           "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf",
-					"VolumeID":     "289da7f8-6440-407c-9fb4-7db01ec49164",
-					"ServerID":     "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
-					"HostName":     "stack",
-					"Device":       "/dev/vdc",
-				}},
-				AvailabilityZone:          "nova",
-				Bootable:                  "false",
-				ConsistencyGroupID:        "",
-				CreatedAt:                 "2015-09-17T03:35:03.000000",
-				Description:               "",
-				Encrypted:                 false,
-				Metadata:                  map[string]string{"foo": "bar"},
-				Multiattach:               false,
-				TenantID:                  "304dc00909ac4d0da6c62d816bcb3459",
-				ReplicationDriverData:     "",
-				ReplicationExtendedStatus: "",
-				ReplicationStatus:         "disabled",
-				Size:                      75,
-				SnapshotID:                "",
-				SourceVolID:               "",
-				Status:                    "available",
-				UserID:                    "ff1ce52c03ab433aaba9108c2e3ef541",
-				VolumeType:                "lvmdriver-1",
-			},
-			{
-				ID:                        "96c3bda7-c82a-4f50-be73-ca7621794835",
-				Name:                      "vol-002",
-				Attachments:               []map[string]interface{}{},
-				AvailabilityZone:          "nova",
-				Bootable:                  "false",
-				ConsistencyGroupID:        "",
-				CreatedAt:                 "2015-09-17T03:32:29.000000",
-				Description:               "",
-				Encrypted:                 false,
-				Metadata:                  map[string]string{},
-				Multiattach:               false,
-				TenantID:                  "304dc00909ac4d0da6c62d816bcb3459",
-				ReplicationDriverData:     "",
-				ReplicationExtendedStatus: "",
-				ReplicationStatus:         "disabled",
-				Size:                      75,
-				SnapshotID:                "",
-				SourceVolID:               "",
-				Status:                    "available",
-				UserID:                    "ff1ce52c03ab433aaba9108c2e3ef541",
-				VolumeType:                "lvmdriver-1",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestListAll(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockListResponse(t)
-
-	allPages, err := List(client.ServiceClient(), &ListOpts{}).AllPages()
-	th.AssertNoErr(t, err)
-	actual, err := ExtractVolumes(allPages)
-	th.AssertNoErr(t, err)
-
-	expected := []Volume{
-		{
-			ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
-			Name: "vol-001",
-			Attachments: []map[string]interface{}{{
-				"AttachmentID": "03987cd1-0ad5-40d1-9b2a-7cc48295d4fa",
-				"ID":           "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf",
-				"VolumeID":     "289da7f8-6440-407c-9fb4-7db01ec49164",
-				"ServerID":     "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
-				"HostName":     "stack",
-				"Device":       "/dev/vdc",
-			}},
-			AvailabilityZone:          "nova",
-			Bootable:                  "false",
-			ConsistencyGroupID:        "",
-			CreatedAt:                 "2015-09-17T03:35:03.000000",
-			Description:               "",
-			Encrypted:                 false,
-			Metadata:                  map[string]string{"foo": "bar"},
-			Multiattach:               false,
-			TenantID:                  "304dc00909ac4d0da6c62d816bcb3459",
-			ReplicationDriverData:     "",
-			ReplicationExtendedStatus: "",
-			ReplicationStatus:         "disabled",
-			Size:                      75,
-			SnapshotID:                "",
-			SourceVolID:               "",
-			Status:                    "available",
-			UserID:                    "ff1ce52c03ab433aaba9108c2e3ef541",
-			VolumeType:                "lvmdriver-1",
-		},
-		{
-			ID:                        "96c3bda7-c82a-4f50-be73-ca7621794835",
-			Name:                      "vol-002",
-			Attachments:               []map[string]interface{}{},
-			AvailabilityZone:          "nova",
-			Bootable:                  "false",
-			ConsistencyGroupID:        "",
-			CreatedAt:                 "2015-09-17T03:32:29.000000",
-			Description:               "",
-			Encrypted:                 false,
-			Metadata:                  map[string]string{},
-			Multiattach:               false,
-			TenantID:                  "304dc00909ac4d0da6c62d816bcb3459",
-			ReplicationDriverData:     "",
-			ReplicationExtendedStatus: "",
-			ReplicationStatus:         "disabled",
-			Size:                      75,
-			SnapshotID:                "",
-			SourceVolID:               "",
-			Status:                    "available",
-			UserID:                    "ff1ce52c03ab433aaba9108c2e3ef541",
-			VolumeType:                "lvmdriver-1",
-		},
-	}
-
-	th.CheckDeepEquals(t, expected, actual)
-
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockGetResponse(t)
-
-	v, err := Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, v.Name, "vol-001")
-	th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockCreateResponse(t)
-
-	options := &CreateOpts{Size: 75, Name: "vol-001"}
-	n, err := Create(client.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Size, 75)
-	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockDeleteResponse(t)
-
-	res := Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockUpdateResponse(t)
-
-	options := UpdateOpts{Name: "vol-002"}
-	v, err := Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, "vol-002", v.Name)
-}
diff --git a/openstack/blockstorage/v2/volumes/results.go b/openstack/blockstorage/v2/volumes/results.go
index 59fa530..96864ae 100644
--- a/openstack/blockstorage/v2/volumes/results.go
+++ b/openstack/blockstorage/v2/volumes/results.go
@@ -1,76 +1,102 @@
 package volumes
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
+type Attachment struct {
+	ID         string                          `json:"id"`
+	VolumeID   string                          `json:"volume_id"`
+	ServerID   string                          `json:"instance_uuid"`
+	HostName   string                          `json:"attached_host"`
+	Device     string                          `json:"mountpoint"`
+	AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attach_time"`
+}
+
 // Volume contains all the information associated with an OpenStack Volume.
 type Volume struct {
-	// Instances onto which the volume is attached.
-	Attachments []map[string]interface{} `mapstructure:"attachments"`
-
-	// AvailabilityZone is which availability zone the volume is in.
-	AvailabilityZone string `mapstructure:"availability_zone"`
-
-	// Indicates whether this is a bootable volume.
-	Bootable string `mapstructure:"bootable"`
-
-	// ConsistencyGroupID is the consistency group ID.
-	ConsistencyGroupID string `mapstructure:"consistencygroup_id"`
-
-	// The date when this volume was created.
-	CreatedAt string `mapstructure:"created_at"`
-
-	// Human-readable description for the volume.
-	Description string `mapstructure:"description"`
-
-	// Encrypted denotes if the volume is encrypted.
-	Encrypted bool `mapstructure:"encrypted"`
-
-	// Human-readable display name for the volume.
-	Name string `mapstructure:"name"`
-
-	// The type of volume to create, either SATA or SSD.
-	VolumeType string `mapstructure:"volume_type"`
-
-	// ReplicationDriverData contains data about the replication driver.
-	ReplicationDriverData string `mapstructure:"os-volume-replication:driver_data"`
-
-	// ReplicationExtendedStatus contains extended status about replication.
-	ReplicationExtendedStatus string `mapstructure:"os-volume-replication:extended_status"`
-
-	// ReplicationStatus is the status of replication.
-	ReplicationStatus string `mapstructure:"replication_status"`
-
-	// The ID of the snapshot from which the volume was created
-	SnapshotID string `mapstructure:"snapshot_id"`
-
-	// The ID of another block storage volume from which the current volume was created
-	SourceVolID string `mapstructure:"source_volid"`
-
-	// Current status of the volume.
-	Status string `mapstructure:"status"`
-
-	// TenantID is the id of the project that owns the volume.
-	TenantID string `mapstructure:"os-vol-tenant-attr:tenant_id"`
-
-	// Arbitrary key-value pairs defined by the user.
-	Metadata map[string]string `mapstructure:"metadata"`
-
-	// Multiattach denotes if the volume is multi-attach capable.
-	Multiattach bool `mapstructure:"multiattach"`
-
 	// Unique identifier for the volume.
-	ID string `mapstructure:"id"`
-
+	ID string `json:"id"`
+	// Current status of the volume.
+	Status string `json:"status"`
 	// Size of the volume in GB.
-	Size int `mapstructure:"size"`
-
+	Size int `json:"size"`
+	// AvailabilityZone is which availability zone the volume is in.
+	AvailabilityZone string `json:"availability_zone"`
+	// The date when this volume was created.
+	CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
+	// The date when this volume was last updated
+	UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
+	// Instances onto which the volume is attached.
+	Attachments []Attachment `json:"attachments"`
+	// Human-readable display name for the volume.
+	Name string `json:"name"`
+	// Human-readable description for the volume.
+	Description string `json:"description"`
+	// The type of volume to create, either SATA or SSD.
+	VolumeType string `json:"volume_type"`
+	// The ID of the snapshot from which the volume was created
+	SnapshotID string `json:"snapshot_id"`
+	// The ID of another block storage volume from which the current volume was created
+	SourceVolID string `json:"source_volid"`
+	// Arbitrary key-value pairs defined by the user.
+	Metadata map[string]string `json:"metadata"`
 	// UserID is the id of the user who created the volume.
-	UserID string `mapstructure:"user_id"`
+	UserID string `json:"user_id"`
+	// Indicates whether this is a bootable volume.
+	Bootable string `json:"bootable"`
+	// Encrypted denotes if the volume is encrypted.
+	Encrypted bool `json:"encrypted"`
+	// ReplicationStatus is the status of replication.
+	ReplicationStatus string `json:"replication_status"`
+	// ConsistencyGroupID is the consistency group ID.
+	ConsistencyGroupID string `json:"consistencygroup_id"`
+	// Multiattach denotes if the volume is multi-attach capable.
+	Multiattach bool `json:"multiattach"`
+}
+
+/*
+THESE BELONG IN EXTENSIONS:
+// ReplicationDriverData contains data about the replication driver.
+ReplicationDriverData string `json:"os-volume-replication:driver_data"`
+// ReplicationExtendedStatus contains extended status about replication.
+ReplicationExtendedStatus string `json:"os-volume-replication:extended_status"`
+// TenantID is the id of the project that owns the volume.
+TenantID string `json:"os-vol-tenant-attr:tenant_id"`
+*/
+
+// VolumePage is a pagination.pager that is returned from a call to the List function.
+type VolumePage struct {
+	pagination.SinglePageBase
+}
+
+// IsEmpty returns true if a ListResult contains no Volumes.
+func (r VolumePage) IsEmpty() (bool, error) {
+	volumes, err := ExtractVolumes(r)
+	return len(volumes) == 0, err
+}
+
+// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call.
+func ExtractVolumes(r pagination.Page) ([]Volume, error) {
+	var s struct {
+		Volumes []Volume `json:"volumes"`
+	}
+	err := (r.(VolumePage)).ExtractInto(&s)
+	return s.Volumes, err
+}
+
+type commonResult struct {
+	gophercloud.Result
+}
+
+// Extract will get the Volume object out of the commonResult object.
+func (r commonResult) Extract() (*Volume, error) {
+	var s struct {
+		Volume *Volume `json:"volume"`
+	}
+	err := r.ExtractInto(&s)
+	return s.Volume, err
 }
 
 // CreateResult contains the response body and error from a Create request.
@@ -83,55 +109,12 @@
 	commonResult
 }
 
-// DeleteResult contains the response body and error from a Delete request.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-// ListResult is a pagination.pager that is returned from a call to the List function.
-type ListResult struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty returns true if a ListResult contains no Volumes.
-func (r ListResult) IsEmpty() (bool, error) {
-	volumes, err := ExtractVolumes(r)
-	if err != nil {
-		return true, err
-	}
-	return len(volumes) == 0, nil
-}
-
-// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call.
-func ExtractVolumes(page pagination.Page) ([]Volume, error) {
-	var response struct {
-		Volumes []Volume `json:"volumes"`
-	}
-
-	err := mapstructure.Decode(page.(ListResult).Body, &response)
-	return response.Volumes, err
-}
-
 // UpdateResult contains the response body and error from an Update request.
 type UpdateResult struct {
 	commonResult
 }
 
-type commonResult struct {
-	gophercloud.Result
-}
-
-// Extract will get the Volume object out of the commonResult object.
-func (r commonResult) Extract() (*Volume, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
-		Volume *Volume `json:"volume"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Volume, err
+// DeleteResult contains the response body and error from a Delete request.
+type DeleteResult struct {
+	gophercloud.ErrResult
 }
diff --git a/openstack/blockstorage/v2/volumes/testing/fixtures.go b/openstack/blockstorage/v2/volumes/testing/fixtures.go
new file mode 100644
index 0000000..d4b9da0
--- /dev/null
+++ b/openstack/blockstorage/v2/volumes/testing/fixtures.go
@@ -0,0 +1,202 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func MockListResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes/detail", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+  {
+  "volumes": [
+    {
+      "volume_type": "lvmdriver-1",
+      "created_at": "2015-09-17T03:35:03.000000",
+      "bootable": "false",
+      "name": "vol-001",
+      "os-vol-mig-status-attr:name_id": null,
+      "consistencygroup_id": null,
+      "source_volid": null,
+      "os-volume-replication:driver_data": null,
+      "multiattach": false,
+      "snapshot_id": null,
+      "replication_status": "disabled",
+      "os-volume-replication:extended_status": null,
+      "encrypted": false,
+      "os-vol-host-attr:host": null,
+      "availability_zone": "nova",
+      "attachments": [
+        {
+        "id": "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf",
+        "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164",
+        "instance_uuid": "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
+        "attached_host": "stack",
+        "mountpoint": "/dev/vdc"
+        }
+      ],
+      "id": "289da7f8-6440-407c-9fb4-7db01ec49164",
+      "size": 75,
+      "user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
+      "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459",
+      "os-vol-mig-status-attr:migstat": null,
+      "metadata": {"foo": "bar"},
+      "status": "available",
+      "description": null
+    },
+    {
+      "volume_type": "lvmdriver-1",
+      "created_at": "2015-09-17T03:32:29.000000",
+      "bootable": "false",
+      "name": "vol-002",
+      "os-vol-mig-status-attr:name_id": null,
+      "consistencygroup_id": null,
+      "source_volid": null,
+      "os-volume-replication:driver_data": null,
+      "multiattach": false,
+      "snapshot_id": null,
+      "replication_status": "disabled",
+      "os-volume-replication:extended_status": null,
+      "encrypted": false,
+      "os-vol-host-attr:host": null,
+      "availability_zone": "nova",
+      "attachments": [],
+      "id": "96c3bda7-c82a-4f50-be73-ca7621794835",
+      "size": 75,
+      "user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
+      "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459",
+      "os-vol-mig-status-attr:migstat": null,
+      "metadata": {},
+      "status": "available",
+      "description": null
+    }
+  ]
+}
+  `)
+	})
+}
+
+func MockGetResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `
+{
+  "volume": {
+    "volume_type": "lvmdriver-1",
+    "created_at": "2015-09-17T03:32:29.000000",
+    "bootable": "false",
+    "name": "vol-001",
+    "os-vol-mig-status-attr:name_id": null,
+    "consistencygroup_id": null,
+    "source_volid": null,
+    "os-volume-replication:driver_data": null,
+    "multiattach": false,
+    "snapshot_id": null,
+    "replication_status": "disabled",
+    "os-volume-replication:extended_status": null,
+    "encrypted": false,
+    "os-vol-host-attr:host": null,
+    "availability_zone": "nova",
+    "attachments": [{
+        "attachment_id": "dbce64e3-f3b9-4423-a44f-a2b15deffa1b",
+        "id": "3eafc6f5-ed74-456d-90fb-f253f594dbae",
+        "volume_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+        "server_id": "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
+        "host_name": "stack",
+        "device": "/dev/vdd"
+        }],
+    "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+    "size": 75,
+    "user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
+    "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459",
+    "os-vol-mig-status-attr:migstat": null,
+    "metadata": {},
+    "status": "available",
+    "description": null
+  }
+}
+      `)
+	})
+}
+
+func MockCreateResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "volume": {
+    	"name": "vol-001",
+        "size": 75
+    }
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusAccepted)
+
+		fmt.Fprintf(w, `
+{
+  "volume": {
+    "size": 75,
+    "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+    "metadata": {},
+    "created_at": "2015-09-17T03:32:29.044216",
+    "encrypted": false,
+    "bootable": "false",
+    "availability_zone": "nova",
+    "attachments": [],
+    "user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
+    "status": "creating",
+    "description": null,
+    "volume_type": "lvmdriver-1",
+    "name": "vol-001",
+    "replication_status": "disabled",
+    "consistencygroup_id": null,
+    "source_volid": null,
+    "snapshot_id": null,
+    "multiattach": false
+  }
+}
+    `)
+	})
+}
+
+func MockDeleteResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+func MockUpdateResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `
+{
+  "volume": {
+    "name": "vol-002"
+  }
+}
+        `)
+	})
+}
diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go
new file mode 100644
index 0000000..147beb5
--- /dev/null
+++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go
@@ -0,0 +1,212 @@
+package testing
+
+import (
+	"testing"
+	"time"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockListResponse(t)
+
+	count := 0
+
+	volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := volumes.ExtractVolumes(page)
+		if err != nil {
+			t.Errorf("Failed to extract volumes: %v", err)
+			return false, err
+		}
+
+		expected := []volumes.Volume{
+			{
+				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
+				Name: "vol-001",
+				Attachments: []volumes.Attachment{{
+					ID:       "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf",
+					VolumeID: "289da7f8-6440-407c-9fb4-7db01ec49164",
+					ServerID: "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
+					HostName: "stack",
+					Device:   "/dev/vdc",
+				}},
+				AvailabilityZone:   "nova",
+				Bootable:           "false",
+				ConsistencyGroupID: "",
+				CreatedAt:          gophercloud.JSONRFC3339MilliNoZ(time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC)),
+				Description:        "",
+				Encrypted:          false,
+				Metadata:           map[string]string{"foo": "bar"},
+				Multiattach:        false,
+				//TenantID:                  "304dc00909ac4d0da6c62d816bcb3459",
+				//ReplicationDriverData:     "",
+				//ReplicationExtendedStatus: "",
+				ReplicationStatus: "disabled",
+				Size:              75,
+				SnapshotID:        "",
+				SourceVolID:       "",
+				Status:            "available",
+				UserID:            "ff1ce52c03ab433aaba9108c2e3ef541",
+				VolumeType:        "lvmdriver-1",
+			},
+			{
+				ID:                 "96c3bda7-c82a-4f50-be73-ca7621794835",
+				Name:               "vol-002",
+				Attachments:        []volumes.Attachment{},
+				AvailabilityZone:   "nova",
+				Bootable:           "false",
+				ConsistencyGroupID: "",
+				CreatedAt:          gophercloud.JSONRFC3339MilliNoZ(time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC)),
+				Description:        "",
+				Encrypted:          false,
+				Metadata:           map[string]string{},
+				Multiattach:        false,
+				//TenantID:                  "304dc00909ac4d0da6c62d816bcb3459",
+				//ReplicationDriverData:     "",
+				//ReplicationExtendedStatus: "",
+				ReplicationStatus: "disabled",
+				Size:              75,
+				SnapshotID:        "",
+				SourceVolID:       "",
+				Status:            "available",
+				UserID:            "ff1ce52c03ab433aaba9108c2e3ef541",
+				VolumeType:        "lvmdriver-1",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestListAll(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockListResponse(t)
+
+	allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := volumes.ExtractVolumes(allPages)
+	th.AssertNoErr(t, err)
+
+	expected := []volumes.Volume{
+		{
+			ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
+			Name: "vol-001",
+			Attachments: []volumes.Attachment{{
+				ID:       "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf",
+				VolumeID: "289da7f8-6440-407c-9fb4-7db01ec49164",
+				ServerID: "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
+				HostName: "stack",
+				Device:   "/dev/vdc",
+			}},
+			AvailabilityZone:   "nova",
+			Bootable:           "false",
+			ConsistencyGroupID: "",
+			CreatedAt:          gophercloud.JSONRFC3339MilliNoZ(time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC)),
+			Description:        "",
+			Encrypted:          false,
+			Metadata:           map[string]string{"foo": "bar"},
+			Multiattach:        false,
+			//TenantID:                  "304dc00909ac4d0da6c62d816bcb3459",
+			//ReplicationDriverData:     "",
+			//ReplicationExtendedStatus: "",
+			ReplicationStatus: "disabled",
+			Size:              75,
+			SnapshotID:        "",
+			SourceVolID:       "",
+			Status:            "available",
+			UserID:            "ff1ce52c03ab433aaba9108c2e3ef541",
+			VolumeType:        "lvmdriver-1",
+		},
+		{
+			ID:                 "96c3bda7-c82a-4f50-be73-ca7621794835",
+			Name:               "vol-002",
+			Attachments:        []volumes.Attachment{},
+			AvailabilityZone:   "nova",
+			Bootable:           "false",
+			ConsistencyGroupID: "",
+			CreatedAt:          gophercloud.JSONRFC3339MilliNoZ(time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC)),
+			Description:        "",
+			Encrypted:          false,
+			Metadata:           map[string]string{},
+			Multiattach:        false,
+			//TenantID:                  "304dc00909ac4d0da6c62d816bcb3459",
+			//ReplicationDriverData:     "",
+			//ReplicationExtendedStatus: "",
+			ReplicationStatus: "disabled",
+			Size:              75,
+			SnapshotID:        "",
+			SourceVolID:       "",
+			Status:            "available",
+			UserID:            "ff1ce52c03ab433aaba9108c2e3ef541",
+			VolumeType:        "lvmdriver-1",
+		},
+	}
+
+	th.CheckDeepEquals(t, expected, actual)
+
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockGetResponse(t)
+
+	v, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, v.Name, "vol-001")
+	th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockCreateResponse(t)
+
+	options := &volumes.CreateOpts{Size: 75, Name: "vol-001"}
+	n, err := volumes.Create(client.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Size, 75)
+	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockDeleteResponse(t)
+
+	res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockUpdateResponse(t)
+
+	options := volumes.UpdateOpts{Name: "vol-002"}
+	v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract()
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, "vol-002", v.Name)
+}
diff --git a/openstack/blockstorage/v2/volumes/urls.go b/openstack/blockstorage/v2/volumes/urls.go
index 2523ec6..1707249 100644
--- a/openstack/blockstorage/v2/volumes/urls.go
+++ b/openstack/blockstorage/v2/volumes/urls.go
@@ -1,6 +1,6 @@
 package volumes
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func createURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("volumes")
diff --git a/openstack/blockstorage/v2/volumes/urls_test.go b/openstack/blockstorage/v2/volumes/urls_test.go
deleted file mode 100644
index 792b19b..0000000
--- a/openstack/blockstorage/v2/volumes/urls_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package volumes
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "volumes"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient())
-	expected := endpoint + "volumes/detail"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient(), "foo")
-	expected := endpoint + "volumes/foo"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/blockstorage/v2/volumes/util.go b/openstack/blockstorage/v2/volumes/util.go
index 1dda695..e86c1b4 100644
--- a/openstack/blockstorage/v2/volumes/util.go
+++ b/openstack/blockstorage/v2/volumes/util.go
@@ -1,7 +1,7 @@
 package volumes
 
 import (
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 // WaitForStatus will continually poll the resource, checking for a particular
diff --git a/openstack/cdn/v1/base/fixtures.go b/openstack/cdn/v1/base/fixtures.go
deleted file mode 100644
index 5568074..0000000
--- a/openstack/cdn/v1/base/fixtures.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// +build fixtures
-
-package base
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// HandleGetSuccessfully creates an HTTP handler at `/` on the test handler mux
-// that responds with a `Get` response.
-func HandleGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-    {
-        "resources": {
-            "rel/cdn": {
-                "href-template": "services{?marker,limit}",
-                "href-vars": {
-                    "marker": "param/marker",
-                    "limit": "param/limit"
-                },
-                "hints": {
-                    "allow": [
-                        "GET"
-                    ],
-                    "formats": {
-                        "application/json": {}
-                    }
-                }
-            }
-        }
-    }
-    `)
-
-	})
-}
-
-// HandlePingSuccessfully creates an HTTP handler at `/ping` on the test handler
-// mux that responds with a `Ping` response.
-func HandlePingSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
diff --git a/openstack/cdn/v1/base/requests.go b/openstack/cdn/v1/base/requests.go
index dd221bc..34d3b72 100644
--- a/openstack/cdn/v1/base/requests.go
+++ b/openstack/cdn/v1/base/requests.go
@@ -1,21 +1,19 @@
 package base
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 // Get retrieves the home document, allowing the user to discover the
 // entire API.
-func Get(c *gophercloud.ServiceClient) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient) (r GetResult) {
+	_, r.Err = c.Get(getURL(c), &r.Body, nil)
+	return
 }
 
 // Ping retrieves a ping to the server.
-func Ping(c *gophercloud.ServiceClient) PingResult {
-	var res PingResult
-	_, res.Err = c.Get(pingURL(c), nil, &gophercloud.RequestOpts{
+func Ping(c *gophercloud.ServiceClient) (r PingResult) {
+	_, r.Err = c.Get(pingURL(c), nil, &gophercloud.RequestOpts{
 		OkCodes:     []int{204},
 		MoreHeaders: map[string]string{"Accept": ""},
 	})
-	return res
+	return
 }
diff --git a/openstack/cdn/v1/base/requests_test.go b/openstack/cdn/v1/base/requests_test.go
deleted file mode 100644
index 2c20a71..0000000
--- a/openstack/cdn/v1/base/requests_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package base
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestGetHomeDocument(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t)
-
-	actual, err := Get(fake.ServiceClient()).Extract()
-	th.CheckNoErr(t, err)
-
-	expected := HomeDocument{
-		"rel/cdn": map[string]interface{}{
-			"href-template": "services{?marker,limit}",
-			"href-vars": map[string]interface{}{
-				"marker": "param/marker",
-				"limit":  "param/limit",
-			},
-			"hints": map[string]interface{}{
-				"allow": []string{"GET"},
-				"formats": map[string]interface{}{
-					"application/json": map[string]interface{}{},
-				},
-			},
-		},
-	}
-	th.CheckDeepEquals(t, expected, *actual)
-}
-
-func TestPing(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePingSuccessfully(t)
-
-	err := Ping(fake.ServiceClient()).ExtractErr()
-	th.CheckNoErr(t, err)
-}
diff --git a/openstack/cdn/v1/base/results.go b/openstack/cdn/v1/base/results.go
index bef1da8..2dfde7d 100644
--- a/openstack/cdn/v1/base/results.go
+++ b/openstack/cdn/v1/base/results.go
@@ -1,10 +1,6 @@
 package base
 
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-)
+import "github.com/gophercloud/gophercloud"
 
 // HomeDocument is a resource that contains all the resources for the CDN API.
 type HomeDocument map[string]interface{}
@@ -16,17 +12,9 @@
 
 // Extract is a function that accepts a result and extracts a home document resource.
 func (r GetResult) Extract() (*HomeDocument, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	submap, ok := r.Body.(map[string]interface{})["resources"]
-	if !ok {
-		return nil, errors.New("Unexpected HomeDocument structure")
-	}
-	casted := HomeDocument(submap.(map[string]interface{}))
-
-	return &casted, nil
+	var s HomeDocument
+	err := r.ExtractInto(&s)
+	return &s, err
 }
 
 // PingResult represents the result of a Ping operation.
diff --git a/openstack/cdn/v1/base/testing/doc.go b/openstack/cdn/v1/base/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/cdn/v1/base/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/cdn/v1/base/testing/fixtures.go b/openstack/cdn/v1/base/testing/fixtures.go
new file mode 100644
index 0000000..f1f4ac0
--- /dev/null
+++ b/openstack/cdn/v1/base/testing/fixtures.go
@@ -0,0 +1,53 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// HandleGetSuccessfully creates an HTTP handler at `/` on the test handler mux
+// that responds with a `Get` response.
+func HandleGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `
+    {
+        "resources": {
+            "rel/cdn": {
+                "href-template": "services{?marker,limit}",
+                "href-vars": {
+                    "marker": "param/marker",
+                    "limit": "param/limit"
+                },
+                "hints": {
+                    "allow": [
+                        "GET"
+                    ],
+                    "formats": {
+                        "application/json": {}
+                    }
+                }
+            }
+        }
+    }
+    `)
+
+	})
+}
+
+// HandlePingSuccessfully creates an HTTP handler at `/ping` on the test handler
+// mux that responds with a `Ping` response.
+func HandlePingSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
diff --git a/openstack/cdn/v1/base/testing/requests_test.go b/openstack/cdn/v1/base/testing/requests_test.go
new file mode 100644
index 0000000..cd1209b
--- /dev/null
+++ b/openstack/cdn/v1/base/testing/requests_test.go
@@ -0,0 +1,44 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/cdn/v1/base"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestGetHomeDocument(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t)
+
+	actual, err := base.Get(fake.ServiceClient()).Extract()
+	th.CheckNoErr(t, err)
+
+	expected := base.HomeDocument{
+		"rel/cdn": map[string]interface{}{
+			"href-template": "services{?marker,limit}",
+			"href-vars": map[string]interface{}{
+				"marker": "param/marker",
+				"limit":  "param/limit",
+			},
+			"hints": map[string]interface{}{
+				"allow": []string{"GET"},
+				"formats": map[string]interface{}{
+					"application/json": map[string]interface{}{},
+				},
+			},
+		},
+	}
+	th.CheckDeepEquals(t, expected, *actual)
+}
+
+func TestPing(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePingSuccessfully(t)
+
+	err := base.Ping(fake.ServiceClient()).ExtractErr()
+	th.CheckNoErr(t, err)
+}
diff --git a/openstack/cdn/v1/base/urls.go b/openstack/cdn/v1/base/urls.go
index a95e18b..07d892b 100644
--- a/openstack/cdn/v1/base/urls.go
+++ b/openstack/cdn/v1/base/urls.go
@@ -1,6 +1,6 @@
 package base
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func getURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL()
diff --git a/openstack/cdn/v1/flavors/fixtures.go b/openstack/cdn/v1/flavors/fixtures.go
deleted file mode 100644
index 166b2c8..0000000
--- a/openstack/cdn/v1/flavors/fixtures.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// +build fixtures
-
-package flavors
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// HandleListCDNFlavorsSuccessfully creates an HTTP handler at `/flavors` on the test handler mux
-// that responds with a `List` response.
-func HandleListCDNFlavorsSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/flavors", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-      {
-        "flavors": [
-            {
-                "id": "europe",
-                "providers": [
-                    {
-                        "provider": "Fastly",
-                        "links": [
-                            {
-                                "href": "http://www.fastly.com",
-                                "rel": "provider_url"
-                            }
-                        ]
-                    }
-                ],
-                "links": [
-                    {
-                        "href": "https://www.poppycdn.io/v1.0/flavors/europe",
-                        "rel": "self"
-                    }
-                ]
-            }
-        ]
-    }
-    `)
-	})
-}
-
-// HandleGetCDNFlavorSuccessfully creates an HTTP handler at `/flavors/{id}` on the test handler mux
-// that responds with a `Get` response.
-func HandleGetCDNFlavorSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/flavors/asia", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-      {
-          "id" : "asia",
-          "providers" : [
-              {
-                  "provider" : "ChinaCache",
-                  "links": [
-                      {
-                          "href": "http://www.chinacache.com",
-                          "rel": "provider_url"
-                      }
-                  ]
-              }
-          ],
-          "links": [
-              {
-                  "href": "https://www.poppycdn.io/v1.0/flavors/asia",
-                  "rel": "self"
-              }
-          ]
-      }
-    `)
-	})
-}
diff --git a/openstack/cdn/v1/flavors/requests.go b/openstack/cdn/v1/flavors/requests.go
index 8755a95..1977fe3 100644
--- a/openstack/cdn/v1/flavors/requests.go
+++ b/openstack/cdn/v1/flavors/requests.go
@@ -1,22 +1,19 @@
 package flavors
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // List returns a single page of CDN flavors.
 func List(c *gophercloud.ServiceClient) pagination.Pager {
-	url := listURL(c)
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page {
 		return FlavorPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(c, url, createPage)
+	})
 }
 
 // Get retrieves a specific flavor based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(getURL(c, id), &r.Body, nil)
+	return
 }
diff --git a/openstack/cdn/v1/flavors/requests_test.go b/openstack/cdn/v1/flavors/requests_test.go
deleted file mode 100644
index f731738..0000000
--- a/openstack/cdn/v1/flavors/requests_test.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package flavors
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleListCDNFlavorsSuccessfully(t)
-
-	count := 0
-
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractFlavors(page)
-		if err != nil {
-			t.Errorf("Failed to extract flavors: %v", err)
-			return false, err
-		}
-
-		expected := []Flavor{
-			Flavor{
-				ID: "europe",
-				Providers: []Provider{
-					Provider{
-						Provider: "Fastly",
-						Links: []gophercloud.Link{
-							gophercloud.Link{
-								Href: "http://www.fastly.com",
-								Rel:  "provider_url",
-							},
-						},
-					},
-				},
-				Links: []gophercloud.Link{
-					gophercloud.Link{
-						Href: "https://www.poppycdn.io/v1.0/flavors/europe",
-						Rel:  "self",
-					},
-				},
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleGetCDNFlavorSuccessfully(t)
-
-	expected := &Flavor{
-		ID: "asia",
-		Providers: []Provider{
-			Provider{
-				Provider: "ChinaCache",
-				Links: []gophercloud.Link{
-					gophercloud.Link{
-						Href: "http://www.chinacache.com",
-						Rel:  "provider_url",
-					},
-				},
-			},
-		},
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "https://www.poppycdn.io/v1.0/flavors/asia",
-				Rel:  "self",
-			},
-		},
-	}
-
-	actual, err := Get(fake.ServiceClient(), "asia").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/openstack/cdn/v1/flavors/results.go b/openstack/cdn/v1/flavors/results.go
index 8cab48b..02c2851 100644
--- a/openstack/cdn/v1/flavors/results.go
+++ b/openstack/cdn/v1/flavors/results.go
@@ -1,29 +1,28 @@
 package flavors
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Provider represents a provider for a particular flavor.
 type Provider struct {
 	// Specifies the name of the provider. The name must not exceed 64 bytes in
 	// length and is limited to unicode, digits, underscores, and hyphens.
-	Provider string `mapstructure:"provider"`
+	Provider string `json:"provider"`
 	// Specifies a list with an href where rel is provider_url.
-	Links []gophercloud.Link `mapstructure:"links"`
+	Links []gophercloud.Link `json:"links"`
 }
 
 // Flavor represents a mapping configuration to a CDN provider.
 type Flavor struct {
 	// Specifies the name of the flavor. The name must not exceed 64 bytes in
 	// length and is limited to unicode, digits, underscores, and hyphens.
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 	// Specifies the list of providers mapped to this flavor.
-	Providers []Provider `mapstructure:"providers"`
+	Providers []Provider `json:"providers"`
 	// Specifies the self-navigating JSON document paths.
-	Links []gophercloud.Link `mapstructure:"links"`
+	Links []gophercloud.Link `json:"links"`
 }
 
 // FlavorPage is the page returned by a pager when traversing over a
@@ -35,21 +34,17 @@
 // IsEmpty returns true if a FlavorPage contains no Flavors.
 func (r FlavorPage) IsEmpty() (bool, error) {
 	flavors, err := ExtractFlavors(r)
-	if err != nil {
-		return true, err
-	}
-	return len(flavors) == 0, nil
+	return len(flavors) == 0, err
 }
 
 // ExtractFlavors extracts and returns Flavors. It is used while iterating over
 // a flavors.List call.
-func ExtractFlavors(page pagination.Page) ([]Flavor, error) {
-	var response struct {
+func ExtractFlavors(r pagination.Page) ([]Flavor, error) {
+	var s struct {
 		Flavors []Flavor `json:"flavors"`
 	}
-
-	err := mapstructure.Decode(page.(FlavorPage).Body, &response)
-	return response.Flavors, err
+	err := (r.(FlavorPage)).ExtractInto(&s)
+	return s.Flavors, err
 }
 
 // GetResult represents the result of a get operation.
@@ -59,13 +54,7 @@
 
 // Extract is a function that extracts a flavor from a GetResult.
 func (r GetResult) Extract() (*Flavor, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res Flavor
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return &res, err
+	var s *Flavor
+	err := r.ExtractInto(&s)
+	return s, err
 }
diff --git a/openstack/cdn/v1/flavors/testing/doc.go b/openstack/cdn/v1/flavors/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/cdn/v1/flavors/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/cdn/v1/flavors/testing/fixtures.go b/openstack/cdn/v1/flavors/testing/fixtures.go
new file mode 100644
index 0000000..ed97247
--- /dev/null
+++ b/openstack/cdn/v1/flavors/testing/fixtures.go
@@ -0,0 +1,82 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// HandleListCDNFlavorsSuccessfully creates an HTTP handler at `/flavors` on the test handler mux
+// that responds with a `List` response.
+func HandleListCDNFlavorsSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/flavors", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `
+      {
+        "flavors": [
+            {
+                "id": "europe",
+                "providers": [
+                    {
+                        "provider": "Fastly",
+                        "links": [
+                            {
+                                "href": "http://www.fastly.com",
+                                "rel": "provider_url"
+                            }
+                        ]
+                    }
+                ],
+                "links": [
+                    {
+                        "href": "https://www.poppycdn.io/v1.0/flavors/europe",
+                        "rel": "self"
+                    }
+                ]
+            }
+        ]
+    }
+    `)
+	})
+}
+
+// HandleGetCDNFlavorSuccessfully creates an HTTP handler at `/flavors/{id}` on the test handler mux
+// that responds with a `Get` response.
+func HandleGetCDNFlavorSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/flavors/asia", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `
+      {
+          "id" : "asia",
+          "providers" : [
+              {
+                  "provider" : "ChinaCache",
+                  "links": [
+                      {
+                          "href": "http://www.chinacache.com",
+                          "rel": "provider_url"
+                      }
+                  ]
+              }
+          ],
+          "links": [
+              {
+                  "href": "https://www.poppycdn.io/v1.0/flavors/asia",
+                  "rel": "self"
+              }
+          ]
+      }
+    `)
+	})
+}
diff --git a/openstack/cdn/v1/flavors/testing/requests_test.go b/openstack/cdn/v1/flavors/testing/requests_test.go
new file mode 100644
index 0000000..bc4b1a5
--- /dev/null
+++ b/openstack/cdn/v1/flavors/testing/requests_test.go
@@ -0,0 +1,90 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/cdn/v1/flavors"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleListCDNFlavorsSuccessfully(t)
+
+	count := 0
+
+	err := flavors.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := flavors.ExtractFlavors(page)
+		if err != nil {
+			t.Errorf("Failed to extract flavors: %v", err)
+			return false, err
+		}
+
+		expected := []flavors.Flavor{
+			{
+				ID: "europe",
+				Providers: []flavors.Provider{
+					{
+						Provider: "Fastly",
+						Links: []gophercloud.Link{
+							gophercloud.Link{
+								Href: "http://www.fastly.com",
+								Rel:  "provider_url",
+							},
+						},
+					},
+				},
+				Links: []gophercloud.Link{
+					gophercloud.Link{
+						Href: "https://www.poppycdn.io/v1.0/flavors/europe",
+						Rel:  "self",
+					},
+				},
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, 1, count)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleGetCDNFlavorSuccessfully(t)
+
+	expected := &flavors.Flavor{
+		ID: "asia",
+		Providers: []flavors.Provider{
+			{
+				Provider: "ChinaCache",
+				Links: []gophercloud.Link{
+					gophercloud.Link{
+						Href: "http://www.chinacache.com",
+						Rel:  "provider_url",
+					},
+				},
+			},
+		},
+		Links: []gophercloud.Link{
+			gophercloud.Link{
+				Href: "https://www.poppycdn.io/v1.0/flavors/asia",
+				Rel:  "self",
+			},
+		},
+	}
+
+	actual, err := flavors.Get(fake.ServiceClient(), "asia").Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, expected, actual)
+}
diff --git a/openstack/cdn/v1/flavors/urls.go b/openstack/cdn/v1/flavors/urls.go
index 6eb38d2..a8540a2 100644
--- a/openstack/cdn/v1/flavors/urls.go
+++ b/openstack/cdn/v1/flavors/urls.go
@@ -1,6 +1,6 @@
 package flavors
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func listURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("flavors")
diff --git a/openstack/cdn/v1/serviceassets/fixtures.go b/openstack/cdn/v1/serviceassets/fixtures.go
deleted file mode 100644
index c4739ae..0000000
--- a/openstack/cdn/v1/serviceassets/fixtures.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// +build fixtures
-
-package serviceassets
-
-import (
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// HandleDeleteCDNAssetSuccessfully creates an HTTP handler at `/services/{id}/assets` on the test handler mux
-// that responds with a `Delete` response.
-func HandleDeleteCDNAssetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0/assets", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/openstack/cdn/v1/serviceassets/requests.go b/openstack/cdn/v1/serviceassets/requests.go
index 1ddc65f..80c908f 100644
--- a/openstack/cdn/v1/serviceassets/requests.go
+++ b/openstack/cdn/v1/serviceassets/requests.go
@@ -3,7 +3,7 @@
 import (
 	"strings"
 
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 // DeleteOptsBuilder allows extensions to add additional parameters to the Delete
@@ -24,25 +24,28 @@
 // ToCDNAssetDeleteParams formats a DeleteOpts into a query string.
 func (opts DeleteOpts) ToCDNAssetDeleteParams() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // Delete accepts a unique service ID or URL and deletes the CDN service asset associated with
 // it. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and
 // "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
 // are valid options for idOrURL.
-func Delete(c *gophercloud.ServiceClient, idOrURL string, opts DeleteOptsBuilder) DeleteResult {
+func Delete(c *gophercloud.ServiceClient, idOrURL string, opts DeleteOptsBuilder) (r DeleteResult) {
 	var url string
 	if strings.Contains(idOrURL, "/") {
 		url = idOrURL
 	} else {
 		url = deleteURL(c, idOrURL)
 	}
-
-	var res DeleteResult
-	_, res.Err = c.Delete(url, nil)
-	return res
+	if opts != nil {
+		q, err := opts.ToCDNAssetDeleteParams()
+		if err != nil {
+			r.Err = err
+			return
+		}
+		url += q
+	}
+	_, r.Err = c.Delete(url, nil)
+	return
 }
diff --git a/openstack/cdn/v1/serviceassets/requests_test.go b/openstack/cdn/v1/serviceassets/requests_test.go
deleted file mode 100644
index dde7bc1..0000000
--- a/openstack/cdn/v1/serviceassets/requests_test.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package serviceassets
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleDeleteCDNAssetSuccessfully(t)
-
-	err := Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", nil).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/cdn/v1/serviceassets/results.go b/openstack/cdn/v1/serviceassets/results.go
index 1d8734b..b6114c6 100644
--- a/openstack/cdn/v1/serviceassets/results.go
+++ b/openstack/cdn/v1/serviceassets/results.go
@@ -1,6 +1,6 @@
 package serviceassets
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 // DeleteResult represents the result of a Delete operation.
 type DeleteResult struct {
diff --git a/openstack/cdn/v1/serviceassets/testing/doc.go b/openstack/cdn/v1/serviceassets/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/cdn/v1/serviceassets/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/cdn/v1/serviceassets/testing/fixtures.go b/openstack/cdn/v1/serviceassets/testing/fixtures.go
new file mode 100644
index 0000000..3172d30
--- /dev/null
+++ b/openstack/cdn/v1/serviceassets/testing/fixtures.go
@@ -0,0 +1,19 @@
+package testing
+
+import (
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// HandleDeleteCDNAssetSuccessfully creates an HTTP handler at `/services/{id}/assets` on the test handler mux
+// that responds with a `Delete` response.
+func HandleDeleteCDNAssetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0/assets", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
diff --git a/openstack/cdn/v1/serviceassets/testing/requests_test.go b/openstack/cdn/v1/serviceassets/testing/requests_test.go
new file mode 100644
index 0000000..ff2073b
--- /dev/null
+++ b/openstack/cdn/v1/serviceassets/testing/requests_test.go
@@ -0,0 +1,19 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/cdn/v1/serviceassets"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleDeleteCDNAssetSuccessfully(t)
+
+	err := serviceassets.Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", nil).ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/cdn/v1/serviceassets/urls.go b/openstack/cdn/v1/serviceassets/urls.go
index cb0aea8..ce17418 100644
--- a/openstack/cdn/v1/serviceassets/urls.go
+++ b/openstack/cdn/v1/serviceassets/urls.go
@@ -1,6 +1,6 @@
 package serviceassets
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func deleteURL(c *gophercloud.ServiceClient, id string) string {
 	return c.ServiceURL("services", id, "assets")
diff --git a/openstack/cdn/v1/services/fixtures.go b/openstack/cdn/v1/services/fixtures.go
deleted file mode 100644
index 3d53900..0000000
--- a/openstack/cdn/v1/services/fixtures.go
+++ /dev/null
@@ -1,374 +0,0 @@
-// +build fixtures
-
-package services
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// HandleListCDNServiceSuccessfully creates an HTTP handler at `/services` on the test handler mux
-// that responds with a `List` response.
-func HandleListCDNServiceSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, `
-    {
-        "links": [
-            {
-                "rel": "next",
-                "href": "https://www.poppycdn.io/v1.0/services?marker=96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0&limit=20"
-            }
-        ],
-        "services": [
-            {
-                "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-                "name": "mywebsite.com",
-                "domains": [
-                    {
-                        "domain": "www.mywebsite.com"
-                    }
-                ],
-                "origins": [
-                    {
-                        "origin": "mywebsite.com",
-                        "port": 80,
-                        "ssl": false
-                    }
-                ],
-                "caching": [
-                    {
-                        "name": "default",
-                        "ttl": 3600
-                    },
-                    {
-                        "name": "home",
-                        "ttl": 17200,
-                        "rules": [
-                            {
-                                "name": "index",
-                                "request_url": "/index.htm"
-                            }
-                        ]
-                    },
-                    {
-                        "name": "images",
-                        "ttl": 12800,
-                        "rules": [
-                            {
-                                "name": "images",
-                                "request_url": "*.png"
-                            }
-                        ]
-                    }
-                ],
-                "restrictions": [
-                    {
-                        "name": "website only",
-                        "rules": [
-                            {
-                                "name": "mywebsite.com",
-                                "referrer": "www.mywebsite.com"
-                            }
-                        ]
-                    }
-                ],
-                "flavor_id": "asia",
-                "status": "deployed",
-                "errors" : [],
-                "links": [
-                    {
-                        "href": "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-                        "rel": "self"
-                    },
-                    {
-                        "href": "mywebsite.com.cdn123.poppycdn.net",
-                        "rel": "access_url"
-                    },
-                    {
-                        "href": "https://www.poppycdn.io/v1.0/flavors/asia",
-                        "rel": "flavor"
-                    }
-                ]
-            },
-            {
-                "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
-                "name": "myothersite.com",
-                "domains": [
-                    {
-                        "domain": "www.myothersite.com"
-                    }
-                ],
-                "origins": [
-                    {
-                        "origin": "44.33.22.11",
-                        "port": 80,
-                        "ssl": false
-                    },
-                    {
-                        "origin": "77.66.55.44",
-                        "port": 80,
-                        "ssl": false,
-                        "rules": [
-                            {
-                                "name": "videos",
-                                "request_url": "^/videos/*.m3u"
-                            }
-                        ]
-                    }
-                ],
-                "caching": [
-                    {
-                        "name": "default",
-                        "ttl": 3600
-                    }
-                ],
-                "restrictions": [
-                    {}
-                ],
-                "flavor_id": "europe",
-                "status": "deployed",
-                "links": [
-                    {
-                        "href": "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
-                        "rel": "self"
-                    },
-                    {
-                        "href": "myothersite.com.poppycdn.net",
-                        "rel": "access_url"
-                    },
-                    {
-                        "href": "https://www.poppycdn.io/v1.0/flavors/europe",
-                        "rel": "flavor"
-                    }
-                ]
-            }
-        ]
-    }
-    `)
-		case "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1":
-			fmt.Fprintf(w, `{
-        "services": []
-    }`)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-}
-
-// HandleCreateCDNServiceSuccessfully creates an HTTP handler at `/services` on the test handler mux
-// that responds with a `Create` response.
-func HandleCreateCDNServiceSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestJSONRequest(t, r, `
-      {
-        "name": "mywebsite.com",
-        "domains": [
-            {
-                "domain": "www.mywebsite.com"
-            },
-            {
-                "domain": "blog.mywebsite.com"
-            }
-        ],
-        "origins": [
-            {
-                "origin": "mywebsite.com",
-                "port": 80,
-                "ssl": false
-            }
-        ],
-        "restrictions": [
-                         {
-                         "name": "website only",
-                         "rules": [
-                                   {
-                                   "name": "mywebsite.com",
-                                   "referrer": "www.mywebsite.com"
-                    }
-                ]
-            }
-        ],
-        "caching": [
-            {
-                "name": "default",
-                "ttl": 3600
-            }
-        ],
-
-        "flavor_id": "cdn"
-      }
-   `)
-		w.Header().Add("Location", "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0")
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-// HandleGetCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux
-// that responds with a `Get` response.
-func HandleGetCDNServiceSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-    {
-        "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-        "name": "mywebsite.com",
-        "domains": [
-            {
-                "domain": "www.mywebsite.com",
-                "protocol": "http"
-            }
-        ],
-        "origins": [
-            {
-                "origin": "mywebsite.com",
-                "port": 80,
-                "ssl": false
-            }
-        ],
-        "caching": [
-            {
-                "name": "default",
-                "ttl": 3600
-            },
-            {
-                "name": "home",
-                "ttl": 17200,
-                "rules": [
-                    {
-                        "name": "index",
-                        "request_url": "/index.htm"
-                    }
-                ]
-            },
-            {
-                "name": "images",
-                "ttl": 12800,
-                "rules": [
-                    {
-                        "name": "images",
-                        "request_url": "*.png"
-                    }
-                ]
-            }
-        ],
-        "restrictions": [
-            {
-                "name": "website only",
-                "rules": [
-                    {
-                        "name": "mywebsite.com",
-                        "referrer": "www.mywebsite.com"
-                    }
-                ]
-            }
-        ],
-        "flavor_id": "cdn",
-        "status": "deployed",
-        "errors" : [],
-        "links": [
-            {
-                "href": "https://global.cdn.api.rackspacecloud.com/v1.0/110011/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-                "rel": "self"
-            },
-            {
-                "href": "blog.mywebsite.com.cdn1.raxcdn.com",
-                "rel": "access_url"
-            },
-            {
-                "href": "https://global.cdn.api.rackspacecloud.com/v1.0/110011/flavors/cdn",
-                "rel": "flavor"
-            }
-        ]
-    }
-    `)
-	})
-}
-
-// HandleUpdateCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux
-// that responds with a `Update` response.
-func HandleUpdateCDNServiceSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PATCH")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestJSONRequest(t, r, `
-      [
-				{
-					"op": "add",
-					"path": "/domains/-",
-					"value": {"domain": "appended.mocksite4.com"}
-				},
-				{
-					"op": "add",
-					"path": "/domains/4",
-					"value": {"domain": "inserted.mocksite4.com"}
-				},
-				{
-					"op": "add",
-					"path": "/domains",
-					"value": [
-						{"domain": "bulkadded1.mocksite4.com"},
-						{"domain": "bulkadded2.mocksite4.com"}
-					]
-				},
-				{
-					"op": "replace",
-					"path": "/origins/2",
-					"value": {"origin": "44.33.22.11", "port": 80, "ssl": false}
-				},
-				{
-					"op": "replace",
-					"path": "/origins",
-					"value": [
-						{"origin": "44.33.22.11", "port": 80, "ssl": false},
-						{"origin": "55.44.33.22", "port": 443, "ssl": true}
-					]
-				},
-				{
-					"op": "remove",
-					"path": "/caching/8"
-				},
-				{
-					"op": "remove",
-					"path": "/caching"
-				},
-				{
-					"op": "replace",
-					"path": "/name",
-					"value": "differentServiceName"
-				}
-    ]
-   `)
-		w.Header().Add("Location", "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0")
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-// HandleDeleteCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux
-// that responds with a `Delete` response.
-func HandleDeleteCDNServiceSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/openstack/cdn/v1/services/requests.go b/openstack/cdn/v1/services/requests.go
index 8b37928..4c0c626 100644
--- a/openstack/cdn/v1/services/requests.go
+++ b/openstack/cdn/v1/services/requests.go
@@ -4,8 +4,8 @@
 	"fmt"
 	"strings"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -24,10 +24,7 @@
 // ToCDNServiceListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToCDNServiceListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -42,15 +39,11 @@
 		}
 		url += query
 	}
-
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		p := ServicePage{pagination.MarkerPageBase{PageResult: r}}
 		p.MarkerPageBase.Owner = p
 		return p
-	}
-
-	pager := pagination.NewPager(c, url, createPage)
-	return pager
+	})
 }
 
 // CreateOptsBuilder is the interface options structs have to satisfy in order
@@ -64,140 +57,56 @@
 // CreateOpts is the common options struct used in this package's Create
 // operation.
 type CreateOpts struct {
-	// REQUIRED. Specifies the name of the service. The minimum length for name is
+	// Specifies the name of the service. The minimum length for name is
 	// 3. The maximum length is 256.
-	Name string
-	// REQUIRED. Specifies a list of domains used by users to access their website.
-	Domains []Domain
-	// REQUIRED. Specifies a list of origin domains or IP addresses where the
+	Name string `json:"name" required:"true"`
+	// Specifies a list of domains used by users to access their website.
+	Domains []Domain `json:"domains" required:"true"`
+	// Specifies a list of origin domains or IP addresses where the
 	// original assets are stored.
-	Origins []Origin
-	// REQUIRED. Specifies the CDN provider flavor ID to use. For a list of
+	Origins []Origin `json:"origins" required:"true"`
+	// Specifies the CDN provider flavor ID to use. For a list of
 	// flavors, see the operation to list the available flavors. The minimum
 	// length for flavor_id is 1. The maximum length is 256.
-	FlavorID string
-	// OPTIONAL. Specifies the TTL rules for the assets under this service. Supports wildcards for fine-grained control.
-	Caching []CacheRule
-	// OPTIONAL. Specifies the restrictions that define who can access assets (content from the CDN cache).
-	Restrictions []Restriction
+	FlavorID string `json:"flavor_id" required:"true"`
+	// Specifies the TTL rules for the assets under this service. Supports wildcards for fine-grained control.
+	Caching []CacheRule `json:"caching,omitempty"`
+	// Specifies the restrictions that define who can access assets (content from the CDN cache).
+	Restrictions []Restriction `json:"restrictions,omitempty"`
 }
 
 // ToCDNServiceCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToCDNServiceCreateMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-
-	if opts.Name == "" {
-		return nil, no("Name")
-	}
-	s["name"] = opts.Name
-
-	if opts.Domains == nil {
-		return nil, no("Domains")
-	}
-	for _, domain := range opts.Domains {
-		if domain.Domain == "" {
-			return nil, no("Domains[].Domain")
-		}
-	}
-	s["domains"] = opts.Domains
-
-	if opts.Origins == nil {
-		return nil, no("Origins")
-	}
-	for _, origin := range opts.Origins {
-		if origin.Origin == "" {
-			return nil, no("Origins[].Origin")
-		}
-		if origin.Rules == nil && len(opts.Origins) > 1 {
-			return nil, no("Origins[].Rules")
-		}
-		for _, rule := range origin.Rules {
-			if rule.Name == "" {
-				return nil, no("Origins[].Rules[].Name")
-			}
-			if rule.RequestURL == "" {
-				return nil, no("Origins[].Rules[].RequestURL")
-			}
-		}
-	}
-	s["origins"] = opts.Origins
-
-	if opts.FlavorID == "" {
-		return nil, no("FlavorID")
-	}
-	s["flavor_id"] = opts.FlavorID
-
-	if opts.Caching != nil {
-		for _, cache := range opts.Caching {
-			if cache.Name == "" {
-				return nil, no("Caching[].Name")
-			}
-			if cache.Rules != nil {
-				for _, rule := range cache.Rules {
-					if rule.Name == "" {
-						return nil, no("Caching[].Rules[].Name")
-					}
-					if rule.RequestURL == "" {
-						return nil, no("Caching[].Rules[].RequestURL")
-					}
-				}
-			}
-		}
-		s["caching"] = opts.Caching
-	}
-
-	if opts.Restrictions != nil {
-		for _, restriction := range opts.Restrictions {
-			if restriction.Name == "" {
-				return nil, no("Restrictions[].Name")
-			}
-			if restriction.Rules != nil {
-				for _, rule := range restriction.Rules {
-					if rule.Name == "" {
-						return nil, no("Restrictions[].Rules[].Name")
-					}
-				}
-			}
-		}
-		s["restrictions"] = opts.Restrictions
-	}
-
-	return s, nil
+	return gophercloud.BuildRequestBody(opts, "")
 }
 
 // Create accepts a CreateOpts struct and creates a new CDN service using the
 // values provided.
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToCDNServiceCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToCDNServiceCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	// Send request to API
-	resp, err := c.Post(createURL(c), &reqBody, nil, nil)
-	res.Header = resp.Header
-	res.Err = err
-	return res
+	resp, err := c.Post(createURL(c), &b, nil, nil)
+	r.Header = resp.Header
+	r.Err = err
+	return
 }
 
 // Get retrieves a specific service based on its URL or its unique ID. For
 // example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and
 // "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
 // are valid options for idOrURL.
-func Get(c *gophercloud.ServiceClient, idOrURL string) GetResult {
+func Get(c *gophercloud.ServiceClient, idOrURL string) (r GetResult) {
 	var url string
 	if strings.Contains(idOrURL, "/") {
 		url = idOrURL
 	} else {
 		url = getURL(c, idOrURL)
 	}
-
-	var res GetResult
-	_, res.Err = c.Get(url, &res.Body, nil)
-	return res
+	_, r.Err = c.Get(url, &r.Body, nil)
+	return
 }
 
 // Path is a JSON pointer location that indicates which service parameter is being added, replaced,
@@ -251,11 +160,11 @@
 
 // ToCDNServiceUpdateMap converts an Insertion into a request body fragment suitable for the
 // Update call.
-func (i Insertion) ToCDNServiceUpdateMap() map[string]interface{} {
+func (opts Insertion) ToCDNServiceUpdateMap() map[string]interface{} {
 	return map[string]interface{}{
 		"op":    "add",
-		"path":  i.Value.renderRootOr(func(p Path) string { return p.renderIndex(i.Index) }),
-		"value": i.Value.toPatchValue(),
+		"path":  opts.Value.renderRootOr(func(p Path) string { return p.renderIndex(opts.Index) }),
+		"value": opts.Value.toPatchValue(),
 	}
 }
 
@@ -320,16 +229,17 @@
 
 // ToCDNServiceUpdateMap converts a Removal into a request body fragment suitable for the
 // Update call.
-func (r Removal) ToCDNServiceUpdateMap() map[string]interface{} {
-	result := map[string]interface{}{"op": "remove"}
-	if r.All {
-		result["path"] = r.Path.renderRoot()
+func (opts Removal) ToCDNServiceUpdateMap() map[string]interface{} {
+	b := map[string]interface{}{"op": "remove"}
+	if opts.All {
+		b["path"] = opts.Path.renderRoot()
 	} else {
-		result["path"] = r.Path.renderIndex(r.Index)
+		b["path"] = opts.Path.renderIndex(opts.Index)
 	}
-	return result
+	return b
 }
 
+// UpdateOpts is a slice of Patches used to update a CDN service
 type UpdateOpts []Patch
 
 // Update accepts a slice of Patch operations (Insertion, Append, Replacement or Removal) and
@@ -337,7 +247,7 @@
 // URL or its ID. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and
 // "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
 // are valid options for idOrURL.
-func Update(c *gophercloud.ServiceClient, idOrURL string, opts UpdateOpts) UpdateResult {
+func Update(c *gophercloud.ServiceClient, idOrURL string, opts UpdateOpts) (r UpdateResult) {
 	var url string
 	if strings.Contains(idOrURL, "/") {
 		url = idOrURL
@@ -345,34 +255,31 @@
 		url = updateURL(c, idOrURL)
 	}
 
-	reqBody := make([]map[string]interface{}, len(opts))
+	b := make([]map[string]interface{}, len(opts))
 	for i, patch := range opts {
-		reqBody[i] = patch.ToCDNServiceUpdateMap()
+		b[i] = patch.ToCDNServiceUpdateMap()
 	}
 
-	resp, err := c.Request("PATCH", url, gophercloud.RequestOpts{
-		JSONBody: &reqBody,
+	resp, err := c.Request("PATCH", url, &gophercloud.RequestOpts{
+		JSONBody: &b,
 		OkCodes:  []int{202},
 	})
-	var result UpdateResult
-	result.Header = resp.Header
-	result.Err = err
-	return result
+	r.Header = resp.Header
+	r.Err = err
+	return
 }
 
 // Delete accepts a service's ID or its URL and deletes the CDN service
 // associated with it. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and
 // "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
 // are valid options for idOrURL.
-func Delete(c *gophercloud.ServiceClient, idOrURL string) DeleteResult {
+func Delete(c *gophercloud.ServiceClient, idOrURL string) (r DeleteResult) {
 	var url string
 	if strings.Contains(idOrURL, "/") {
 		url = idOrURL
 	} else {
 		url = deleteURL(c, idOrURL)
 	}
-
-	var res DeleteResult
-	_, res.Err = c.Delete(url, nil)
-	return res
+	_, r.Err = c.Delete(url, nil)
+	return
 }
diff --git a/openstack/cdn/v1/services/requests_test.go b/openstack/cdn/v1/services/requests_test.go
deleted file mode 100644
index 59e826f..0000000
--- a/openstack/cdn/v1/services/requests_test.go
+++ /dev/null
@@ -1,358 +0,0 @@
-package services
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleListCDNServiceSuccessfully(t)
-
-	count := 0
-
-	err := List(fake.ServiceClient(), &ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractServices(page)
-		if err != nil {
-			t.Errorf("Failed to extract services: %v", err)
-			return false, err
-		}
-
-		expected := []Service{
-			Service{
-				ID:   "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-				Name: "mywebsite.com",
-				Domains: []Domain{
-					Domain{
-						Domain: "www.mywebsite.com",
-					},
-				},
-				Origins: []Origin{
-					Origin{
-						Origin: "mywebsite.com",
-						Port:   80,
-						SSL:    false,
-					},
-				},
-				Caching: []CacheRule{
-					CacheRule{
-						Name: "default",
-						TTL:  3600,
-					},
-					CacheRule{
-						Name: "home",
-						TTL:  17200,
-						Rules: []TTLRule{
-							TTLRule{
-								Name:       "index",
-								RequestURL: "/index.htm",
-							},
-						},
-					},
-					CacheRule{
-						Name: "images",
-						TTL:  12800,
-						Rules: []TTLRule{
-							TTLRule{
-								Name:       "images",
-								RequestURL: "*.png",
-							},
-						},
-					},
-				},
-				Restrictions: []Restriction{
-					Restriction{
-						Name: "website only",
-						Rules: []RestrictionRule{
-							RestrictionRule{
-								Name:     "mywebsite.com",
-								Referrer: "www.mywebsite.com",
-							},
-						},
-					},
-				},
-				FlavorID: "asia",
-				Status:   "deployed",
-				Errors:   []Error{},
-				Links: []gophercloud.Link{
-					gophercloud.Link{
-						Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-						Rel:  "self",
-					},
-					gophercloud.Link{
-						Href: "mywebsite.com.cdn123.poppycdn.net",
-						Rel:  "access_url",
-					},
-					gophercloud.Link{
-						Href: "https://www.poppycdn.io/v1.0/flavors/asia",
-						Rel:  "flavor",
-					},
-				},
-			},
-			Service{
-				ID:   "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
-				Name: "myothersite.com",
-				Domains: []Domain{
-					Domain{
-						Domain: "www.myothersite.com",
-					},
-				},
-				Origins: []Origin{
-					Origin{
-						Origin: "44.33.22.11",
-						Port:   80,
-						SSL:    false,
-					},
-					Origin{
-						Origin: "77.66.55.44",
-						Port:   80,
-						SSL:    false,
-						Rules: []OriginRule{
-							OriginRule{
-								Name:       "videos",
-								RequestURL: "^/videos/*.m3u",
-							},
-						},
-					},
-				},
-				Caching: []CacheRule{
-					CacheRule{
-						Name: "default",
-						TTL:  3600,
-					},
-				},
-				Restrictions: []Restriction{},
-				FlavorID:     "europe",
-				Status:       "deployed",
-				Links: []gophercloud.Link{
-					gophercloud.Link{
-						Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
-						Rel:  "self",
-					},
-					gophercloud.Link{
-						Href: "myothersite.com.poppycdn.net",
-						Rel:  "access_url",
-					},
-					gophercloud.Link{
-						Href: "https://www.poppycdn.io/v1.0/flavors/europe",
-						Rel:  "flavor",
-					},
-				},
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleCreateCDNServiceSuccessfully(t)
-
-	createOpts := CreateOpts{
-		Name: "mywebsite.com",
-		Domains: []Domain{
-			Domain{
-				Domain: "www.mywebsite.com",
-			},
-			Domain{
-				Domain: "blog.mywebsite.com",
-			},
-		},
-		Origins: []Origin{
-			Origin{
-				Origin: "mywebsite.com",
-				Port:   80,
-				SSL:    false,
-			},
-		},
-		Restrictions: []Restriction{
-			Restriction{
-				Name: "website only",
-				Rules: []RestrictionRule{
-					RestrictionRule{
-						Name:     "mywebsite.com",
-						Referrer: "www.mywebsite.com",
-					},
-				},
-			},
-		},
-		Caching: []CacheRule{
-			CacheRule{
-				Name: "default",
-				TTL:  3600,
-			},
-		},
-		FlavorID: "cdn",
-	}
-
-	expected := "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
-	actual, err := Create(fake.ServiceClient(), createOpts).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleGetCDNServiceSuccessfully(t)
-
-	expected := &Service{
-		ID:   "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-		Name: "mywebsite.com",
-		Domains: []Domain{
-			Domain{
-				Domain:   "www.mywebsite.com",
-				Protocol: "http",
-			},
-		},
-		Origins: []Origin{
-			Origin{
-				Origin: "mywebsite.com",
-				Port:   80,
-				SSL:    false,
-			},
-		},
-		Caching: []CacheRule{
-			CacheRule{
-				Name: "default",
-				TTL:  3600,
-			},
-			CacheRule{
-				Name: "home",
-				TTL:  17200,
-				Rules: []TTLRule{
-					TTLRule{
-						Name:       "index",
-						RequestURL: "/index.htm",
-					},
-				},
-			},
-			CacheRule{
-				Name: "images",
-				TTL:  12800,
-				Rules: []TTLRule{
-					TTLRule{
-						Name:       "images",
-						RequestURL: "*.png",
-					},
-				},
-			},
-		},
-		Restrictions: []Restriction{
-			Restriction{
-				Name: "website only",
-				Rules: []RestrictionRule{
-					RestrictionRule{
-						Name:     "mywebsite.com",
-						Referrer: "www.mywebsite.com",
-					},
-				},
-			},
-		},
-		FlavorID: "cdn",
-		Status:   "deployed",
-		Errors:   []Error{},
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-				Rel:  "self",
-			},
-			gophercloud.Link{
-				Href: "blog.mywebsite.com.cdn1.raxcdn.com",
-				Rel:  "access_url",
-			},
-			gophercloud.Link{
-				Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/flavors/cdn",
-				Rel:  "flavor",
-			},
-		},
-	}
-
-	actual, err := Get(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestSuccessfulUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleUpdateCDNServiceSuccessfully(t)
-
-	expected := "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
-	ops := UpdateOpts{
-		// Append a single Domain
-		Append{Value: Domain{Domain: "appended.mocksite4.com"}},
-		// Insert a single Domain
-		Insertion{
-			Index: 4,
-			Value: Domain{Domain: "inserted.mocksite4.com"},
-		},
-		// Bulk addition
-		Append{
-			Value: DomainList{
-				Domain{Domain: "bulkadded1.mocksite4.com"},
-				Domain{Domain: "bulkadded2.mocksite4.com"},
-			},
-		},
-		// Replace a single Origin
-		Replacement{
-			Index: 2,
-			Value: Origin{Origin: "44.33.22.11", Port: 80, SSL: false},
-		},
-		// Bulk replace Origins
-		Replacement{
-			Index: 0, // Ignored
-			Value: OriginList{
-				Origin{Origin: "44.33.22.11", Port: 80, SSL: false},
-				Origin{Origin: "55.44.33.22", Port: 443, SSL: true},
-			},
-		},
-		// Remove a single CacheRule
-		Removal{
-			Index: 8,
-			Path:  PathCaching,
-		},
-		// Bulk removal
-		Removal{
-			All:  true,
-			Path: PathCaching,
-		},
-		// Service name replacement
-		NameReplacement{
-			NewName: "differentServiceName",
-		},
-	}
-
-	actual, err := Update(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", ops).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleDeleteCDNServiceSuccessfully(t)
-
-	err := Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/cdn/v1/services/results.go b/openstack/cdn/v1/services/results.go
index 33406c4..f9a1caa 100644
--- a/openstack/cdn/v1/services/results.go
+++ b/openstack/cdn/v1/services/results.go
@@ -1,20 +1,18 @@
 package services
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Domain represents a domain used by users to access their website.
 type Domain struct {
 	// Specifies the domain used to access the assets on their website, for which
 	// a CNAME is given to the CDN provider.
-	Domain string `mapstructure:"domain" json:"domain"`
+	Domain string `json:"domain" required:"true"`
 	// Specifies the protocol used to access the assets on this domain. Only "http"
 	// or "https" are currently allowed. The default is "http".
-	Protocol string `mapstructure:"protocol" json:"protocol,omitempty"`
+	Protocol string `json:"protocol,omitempty"`
 }
 
 func (d Domain) toPatchValue() interface{} {
@@ -56,23 +54,23 @@
 // OriginRule represents a rule that defines when an origin should be accessed.
 type OriginRule struct {
 	// Specifies the name of this rule.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name" required:"true"`
 	// Specifies the request URL this rule should match for this origin to be used. Regex is supported.
-	RequestURL string `mapstructure:"request_url" json:"request_url"`
+	RequestURL string `json:"request_url" required:"true"`
 }
 
 // Origin specifies a list of origin domains or IP addresses where the original assets are stored.
 type Origin struct {
 	// Specifies the URL or IP address to pull origin content from.
-	Origin string `mapstructure:"origin" json:"origin"`
+	Origin string `json:"origin" required:"true"`
 	// Specifies the port used to access the origin. The default is port 80.
-	Port int `mapstructure:"port" json:"port,omitempty"`
+	Port int `json:"port,omitempty"`
 	// Specifies whether or not to use HTTPS to access the origin. The default
 	// is false.
-	SSL bool `mapstructure:"ssl" json:"ssl"`
+	SSL bool `json:"ssl"`
 	// Specifies a collection of rules that define the conditions when this origin
 	// should be accessed. If there is more than one origin, the rules parameter is required.
-	Rules []OriginRule `mapstructure:"rules" json:"rules,omitempty"`
+	Rules []OriginRule `json:"rules,omitempty"`
 }
 
 func (o Origin) toPatchValue() interface{} {
@@ -121,19 +119,19 @@
 // TTLRule specifies a rule that determines if a TTL should be applied to an asset.
 type TTLRule struct {
 	// Specifies the name of this rule.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name" required:"true"`
 	// Specifies the request URL this rule should match for this TTL to be used. Regex is supported.
-	RequestURL string `mapstructure:"request_url" json:"request_url"`
+	RequestURL string `json:"request_url" required:"true"`
 }
 
 // CacheRule specifies the TTL rules for the assets under this service.
 type CacheRule struct {
 	// Specifies the name of this caching rule. Note: 'default' is a reserved name used for the default TTL setting.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name" required:"true"`
 	// Specifies the TTL to apply.
-	TTL int `mapstructure:"ttl" json:"ttl"`
+	TTL int `json:"ttl,omitempty"`
 	// Specifies a collection of rules that determine if this TTL should be applied to an asset.
-	Rules []TTLRule `mapstructure:"rules" json:"rules,omitempty"`
+	Rules []TTLRule `json:"rules,omitempty"`
 }
 
 func (c CacheRule) toPatchValue() interface{} {
@@ -179,48 +177,48 @@
 // RestrictionRule specifies a rule that determines if this restriction should be applied to an asset.
 type RestrictionRule struct {
 	// Specifies the name of this rule.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name" required:"true"`
 	// Specifies the http host that requests must come from.
-	Referrer string `mapstructure:"referrer" json:"referrer,omitempty"`
+	Referrer string `json:"referrer,omitempty"`
 }
 
 // Restriction specifies a restriction that defines who can access assets (content from the CDN cache).
 type Restriction struct {
 	// Specifies the name of this restriction.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name" required:"true"`
 	// Specifies a collection of rules that determine if this TTL should be applied to an asset.
-	Rules []RestrictionRule `mapstructure:"rules" json:"rules"`
+	Rules []RestrictionRule `json:"rules,omitempty"`
 }
 
 // Error specifies an error that occurred during the previous service action.
 type Error struct {
 	// Specifies an error message detailing why there is an error.
-	Message string `mapstructure:"message"`
+	Message string `json:"message"`
 }
 
 // Service represents a CDN service resource.
 type Service struct {
 	// Specifies the service ID that represents distributed content. The value is
 	// a UUID, such as 96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0, that is generated by the server.
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 	// Specifies the name of the service.
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 	// Specifies a list of domains used by users to access their website.
-	Domains []Domain `mapstructure:"domains"`
+	Domains []Domain `json:"domains"`
 	// Specifies a list of origin domains or IP addresses where the original assets are stored.
-	Origins []Origin `mapstructure:"origins"`
+	Origins []Origin `json:"origins"`
 	// Specifies the TTL rules for the assets under this service. Supports wildcards for fine grained control.
-	Caching []CacheRule `mapstructure:"caching"`
+	Caching []CacheRule `json:"caching"`
 	// Specifies the restrictions that define who can access assets (content from the CDN cache).
-	Restrictions []Restriction `mapstructure:"restrictions" json:"restrictions,omitempty"`
+	Restrictions []Restriction `json:"restrictions"`
 	// Specifies the CDN provider flavor ID to use. For a list of flavors, see the operation to list the available flavors.
-	FlavorID string `mapstructure:"flavor_id"`
+	FlavorID string `json:"flavor_id"`
 	// Specifies the current status of the service.
-	Status string `mapstructure:"status"`
+	Status string `json:"status"`
 	// Specifies the list of errors that occurred during the previous service action.
-	Errors []Error `mapstructure:"errors"`
+	Errors []Error `json:"errors"`
 	// Specifies the self-navigating JSON document paths.
-	Links []gophercloud.Link `mapstructure:"links"`
+	Links []gophercloud.Link `json:"links"`
 }
 
 // ServicePage is the page returned by a pager when traversing over a
@@ -232,10 +230,7 @@
 // IsEmpty returns true if a ListResult contains no services.
 func (r ServicePage) IsEmpty() (bool, error) {
 	services, err := ExtractServices(r)
-	if err != nil {
-		return true, err
-	}
-	return len(services) == 0, nil
+	return len(services) == 0, err
 }
 
 // LastMarker returns the last service in a ListResult.
@@ -251,13 +246,12 @@
 }
 
 // ExtractServices is a function that takes a ListResult and returns the services' information.
-func ExtractServices(page pagination.Page) ([]Service, error) {
-	var response struct {
-		Services []Service `mapstructure:"services"`
+func ExtractServices(r pagination.Page) ([]Service, error) {
+	var s struct {
+		Services []Service `json:"services"`
 	}
-
-	err := mapstructure.Decode(page.(ServicePage).Body, &response)
-	return response.Services, err
+	err := (r.(ServicePage)).ExtractInto(&s)
+	return s.Services, err
 }
 
 // CreateResult represents the result of a Create operation.
@@ -283,15 +277,9 @@
 
 // Extract is a function that extracts a service from a GetResult.
 func (r GetResult) Extract() (*Service, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res Service
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return &res, err
+	var s Service
+	err := r.ExtractInto(&s)
+	return &s, err
 }
 
 // UpdateResult represents the result of a Update operation.
diff --git a/openstack/cdn/v1/services/testing/doc.go b/openstack/cdn/v1/services/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/cdn/v1/services/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/cdn/v1/services/testing/fixtures.go b/openstack/cdn/v1/services/testing/fixtures.go
new file mode 100644
index 0000000..d4093e0
--- /dev/null
+++ b/openstack/cdn/v1/services/testing/fixtures.go
@@ -0,0 +1,372 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// HandleListCDNServiceSuccessfully creates an HTTP handler at `/services` on the test handler mux
+// that responds with a `List` response.
+func HandleListCDNServiceSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, `
+    {
+        "links": [
+            {
+                "rel": "next",
+                "href": "https://www.poppycdn.io/v1.0/services?marker=96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0&limit=20"
+            }
+        ],
+        "services": [
+            {
+                "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
+                "name": "mywebsite.com",
+                "domains": [
+                    {
+                        "domain": "www.mywebsite.com"
+                    }
+                ],
+                "origins": [
+                    {
+                        "origin": "mywebsite.com",
+                        "port": 80,
+                        "ssl": false
+                    }
+                ],
+                "caching": [
+                    {
+                        "name": "default",
+                        "ttl": 3600
+                    },
+                    {
+                        "name": "home",
+                        "ttl": 17200,
+                        "rules": [
+                            {
+                                "name": "index",
+                                "request_url": "/index.htm"
+                            }
+                        ]
+                    },
+                    {
+                        "name": "images",
+                        "ttl": 12800,
+                        "rules": [
+                            {
+                                "name": "images",
+                                "request_url": "*.png"
+                            }
+                        ]
+                    }
+                ],
+                "restrictions": [
+                    {
+                        "name": "website only",
+                        "rules": [
+                            {
+                                "name": "mywebsite.com",
+                                "referrer": "www.mywebsite.com"
+                            }
+                        ]
+                    }
+                ],
+                "flavor_id": "asia",
+                "status": "deployed",
+                "errors" : [],
+                "links": [
+                    {
+                        "href": "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
+                        "rel": "self"
+                    },
+                    {
+                        "href": "mywebsite.com.cdn123.poppycdn.net",
+                        "rel": "access_url"
+                    },
+                    {
+                        "href": "https://www.poppycdn.io/v1.0/flavors/asia",
+                        "rel": "flavor"
+                    }
+                ]
+            },
+            {
+                "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
+                "name": "myothersite.com",
+                "domains": [
+                    {
+                        "domain": "www.myothersite.com"
+                    }
+                ],
+                "origins": [
+                    {
+                        "origin": "44.33.22.11",
+                        "port": 80,
+                        "ssl": false
+                    },
+                    {
+                        "origin": "77.66.55.44",
+                        "port": 80,
+                        "ssl": false,
+                        "rules": [
+                            {
+                                "name": "videos",
+                                "request_url": "^/videos/*.m3u"
+                            }
+                        ]
+                    }
+                ],
+                "caching": [
+                    {
+                        "name": "default",
+                        "ttl": 3600
+                    }
+                ],
+                "restrictions": [
+                    {}
+                ],
+                "flavor_id": "europe",
+                "status": "deployed",
+                "links": [
+                    {
+                        "href": "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
+                        "rel": "self"
+                    },
+                    {
+                        "href": "myothersite.com.poppycdn.net",
+                        "rel": "access_url"
+                    },
+                    {
+                        "href": "https://www.poppycdn.io/v1.0/flavors/europe",
+                        "rel": "flavor"
+                    }
+                ]
+            }
+        ]
+    }
+    `)
+		case "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1":
+			fmt.Fprintf(w, `{
+        "services": []
+    }`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+}
+
+// HandleCreateCDNServiceSuccessfully creates an HTTP handler at `/services` on the test handler mux
+// that responds with a `Create` response.
+func HandleCreateCDNServiceSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestJSONRequest(t, r, `
+      {
+        "name": "mywebsite.com",
+        "domains": [
+            {
+                "domain": "www.mywebsite.com"
+            },
+            {
+                "domain": "blog.mywebsite.com"
+            }
+        ],
+        "origins": [
+            {
+                "origin": "mywebsite.com",
+                "port": 80,
+                "ssl": false
+            }
+        ],
+        "restrictions": [
+                         {
+                         "name": "website only",
+                         "rules": [
+                                   {
+                                   "name": "mywebsite.com",
+                                   "referrer": "www.mywebsite.com"
+                    }
+                ]
+            }
+        ],
+        "caching": [
+            {
+                "name": "default",
+                "ttl": 3600
+            }
+        ],
+
+        "flavor_id": "cdn"
+      }
+   `)
+		w.Header().Add("Location", "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0")
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+// HandleGetCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux
+// that responds with a `Get` response.
+func HandleGetCDNServiceSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `
+    {
+        "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
+        "name": "mywebsite.com",
+        "domains": [
+            {
+                "domain": "www.mywebsite.com",
+                "protocol": "http"
+            }
+        ],
+        "origins": [
+            {
+                "origin": "mywebsite.com",
+                "port": 80,
+                "ssl": false
+            }
+        ],
+        "caching": [
+            {
+                "name": "default",
+                "ttl": 3600
+            },
+            {
+                "name": "home",
+                "ttl": 17200,
+                "rules": [
+                    {
+                        "name": "index",
+                        "request_url": "/index.htm"
+                    }
+                ]
+            },
+            {
+                "name": "images",
+                "ttl": 12800,
+                "rules": [
+                    {
+                        "name": "images",
+                        "request_url": "*.png"
+                    }
+                ]
+            }
+        ],
+        "restrictions": [
+            {
+                "name": "website only",
+                "rules": [
+                    {
+                        "name": "mywebsite.com",
+                        "referrer": "www.mywebsite.com"
+                    }
+                ]
+            }
+        ],
+        "flavor_id": "cdn",
+        "status": "deployed",
+        "errors" : [],
+        "links": [
+            {
+                "href": "https://global.cdn.api.rackspacecloud.com/v1.0/110011/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
+                "rel": "self"
+            },
+            {
+                "href": "blog.mywebsite.com.cdn1.raxcdn.com",
+                "rel": "access_url"
+            },
+            {
+                "href": "https://global.cdn.api.rackspacecloud.com/v1.0/110011/flavors/cdn",
+                "rel": "flavor"
+            }
+        ]
+    }
+    `)
+	})
+}
+
+// HandleUpdateCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux
+// that responds with a `Update` response.
+func HandleUpdateCDNServiceSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PATCH")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestJSONRequest(t, r, `
+      [
+				{
+					"op": "add",
+					"path": "/domains/-",
+					"value": {"domain": "appended.mocksite4.com"}
+				},
+				{
+					"op": "add",
+					"path": "/domains/4",
+					"value": {"domain": "inserted.mocksite4.com"}
+				},
+				{
+					"op": "add",
+					"path": "/domains",
+					"value": [
+						{"domain": "bulkadded1.mocksite4.com"},
+						{"domain": "bulkadded2.mocksite4.com"}
+					]
+				},
+				{
+					"op": "replace",
+					"path": "/origins/2",
+					"value": {"origin": "44.33.22.11", "port": 80, "ssl": false}
+				},
+				{
+					"op": "replace",
+					"path": "/origins",
+					"value": [
+						{"origin": "44.33.22.11", "port": 80, "ssl": false},
+						{"origin": "55.44.33.22", "port": 443, "ssl": true}
+					]
+				},
+				{
+					"op": "remove",
+					"path": "/caching/8"
+				},
+				{
+					"op": "remove",
+					"path": "/caching"
+				},
+				{
+					"op": "replace",
+					"path": "/name",
+					"value": "differentServiceName"
+				}
+    ]
+   `)
+		w.Header().Add("Location", "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0")
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+// HandleDeleteCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux
+// that responds with a `Delete` response.
+func HandleDeleteCDNServiceSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
diff --git a/openstack/cdn/v1/services/testing/requests_test.go b/openstack/cdn/v1/services/testing/requests_test.go
new file mode 100644
index 0000000..0abc98e
--- /dev/null
+++ b/openstack/cdn/v1/services/testing/requests_test.go
@@ -0,0 +1,359 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/cdn/v1/services"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleListCDNServiceSuccessfully(t)
+
+	count := 0
+
+	err := services.List(fake.ServiceClient(), &services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := services.ExtractServices(page)
+		if err != nil {
+			t.Errorf("Failed to extract services: %v", err)
+			return false, err
+		}
+
+		expected := []services.Service{
+			{
+				ID:   "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
+				Name: "mywebsite.com",
+				Domains: []services.Domain{
+					{
+						Domain: "www.mywebsite.com",
+					},
+				},
+				Origins: []services.Origin{
+					{
+						Origin: "mywebsite.com",
+						Port:   80,
+						SSL:    false,
+					},
+				},
+				Caching: []services.CacheRule{
+					{
+						Name: "default",
+						TTL:  3600,
+					},
+					{
+						Name: "home",
+						TTL:  17200,
+						Rules: []services.TTLRule{
+							{
+								Name:       "index",
+								RequestURL: "/index.htm",
+							},
+						},
+					},
+					{
+						Name: "images",
+						TTL:  12800,
+						Rules: []services.TTLRule{
+							{
+								Name:       "images",
+								RequestURL: "*.png",
+							},
+						},
+					},
+				},
+				Restrictions: []services.Restriction{
+					{
+						Name: "website only",
+						Rules: []services.RestrictionRule{
+							{
+								Name:     "mywebsite.com",
+								Referrer: "www.mywebsite.com",
+							},
+						},
+					},
+				},
+				FlavorID: "asia",
+				Status:   "deployed",
+				Errors:   []services.Error{},
+				Links: []gophercloud.Link{
+					{
+						Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
+						Rel:  "self",
+					},
+					{
+						Href: "mywebsite.com.cdn123.poppycdn.net",
+						Rel:  "access_url",
+					},
+					{
+						Href: "https://www.poppycdn.io/v1.0/flavors/asia",
+						Rel:  "flavor",
+					},
+				},
+			},
+			{
+				ID:   "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
+				Name: "myothersite.com",
+				Domains: []services.Domain{
+					{
+						Domain: "www.myothersite.com",
+					},
+				},
+				Origins: []services.Origin{
+					{
+						Origin: "44.33.22.11",
+						Port:   80,
+						SSL:    false,
+					},
+					{
+						Origin: "77.66.55.44",
+						Port:   80,
+						SSL:    false,
+						Rules: []services.OriginRule{
+							{
+								Name:       "videos",
+								RequestURL: "^/videos/*.m3u",
+							},
+						},
+					},
+				},
+				Caching: []services.CacheRule{
+					{
+						Name: "default",
+						TTL:  3600,
+					},
+				},
+				Restrictions: []services.Restriction{},
+				FlavorID:     "europe",
+				Status:       "deployed",
+				Links: []gophercloud.Link{
+					gophercloud.Link{
+						Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
+						Rel:  "self",
+					},
+					gophercloud.Link{
+						Href: "myothersite.com.poppycdn.net",
+						Rel:  "access_url",
+					},
+					gophercloud.Link{
+						Href: "https://www.poppycdn.io/v1.0/flavors/europe",
+						Rel:  "flavor",
+					},
+				},
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleCreateCDNServiceSuccessfully(t)
+
+	createOpts := services.CreateOpts{
+		Name: "mywebsite.com",
+		Domains: []services.Domain{
+			{
+				Domain: "www.mywebsite.com",
+			},
+			{
+				Domain: "blog.mywebsite.com",
+			},
+		},
+		Origins: []services.Origin{
+			{
+				Origin: "mywebsite.com",
+				Port:   80,
+				SSL:    false,
+			},
+		},
+		Restrictions: []services.Restriction{
+			{
+				Name: "website only",
+				Rules: []services.RestrictionRule{
+					{
+						Name:     "mywebsite.com",
+						Referrer: "www.mywebsite.com",
+					},
+				},
+			},
+		},
+		Caching: []services.CacheRule{
+			{
+				Name: "default",
+				TTL:  3600,
+			},
+		},
+		FlavorID: "cdn",
+	}
+
+	expected := "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
+	actual, err := services.Create(fake.ServiceClient(), createOpts).Extract()
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, expected, actual)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleGetCDNServiceSuccessfully(t)
+
+	expected := &services.Service{
+		ID:   "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
+		Name: "mywebsite.com",
+		Domains: []services.Domain{
+			{
+				Domain:   "www.mywebsite.com",
+				Protocol: "http",
+			},
+		},
+		Origins: []services.Origin{
+			{
+				Origin: "mywebsite.com",
+				Port:   80,
+				SSL:    false,
+			},
+		},
+		Caching: []services.CacheRule{
+			{
+				Name: "default",
+				TTL:  3600,
+			},
+			{
+				Name: "home",
+				TTL:  17200,
+				Rules: []services.TTLRule{
+					{
+						Name:       "index",
+						RequestURL: "/index.htm",
+					},
+				},
+			},
+			{
+				Name: "images",
+				TTL:  12800,
+				Rules: []services.TTLRule{
+					{
+						Name:       "images",
+						RequestURL: "*.png",
+					},
+				},
+			},
+		},
+		Restrictions: []services.Restriction{
+			{
+				Name: "website only",
+				Rules: []services.RestrictionRule{
+					{
+						Name:     "mywebsite.com",
+						Referrer: "www.mywebsite.com",
+					},
+				},
+			},
+		},
+		FlavorID: "cdn",
+		Status:   "deployed",
+		Errors:   []services.Error{},
+		Links: []gophercloud.Link{
+			{
+				Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
+				Rel:  "self",
+			},
+			{
+				Href: "blog.mywebsite.com.cdn1.raxcdn.com",
+				Rel:  "access_url",
+			},
+			{
+				Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/flavors/cdn",
+				Rel:  "flavor",
+			},
+		},
+	}
+
+	actual, err := services.Get(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestSuccessfulUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleUpdateCDNServiceSuccessfully(t)
+
+	expected := "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
+	ops := services.UpdateOpts{
+		// Append a single Domain
+		services.Append{Value: services.Domain{Domain: "appended.mocksite4.com"}},
+		// Insert a single Domain
+		services.Insertion{
+			Index: 4,
+			Value: services.Domain{Domain: "inserted.mocksite4.com"},
+		},
+		// Bulk addition
+		services.Append{
+			Value: services.DomainList{
+				{Domain: "bulkadded1.mocksite4.com"},
+				{Domain: "bulkadded2.mocksite4.com"},
+			},
+		},
+		// Replace a single Origin
+		services.Replacement{
+			Index: 2,
+			Value: services.Origin{Origin: "44.33.22.11", Port: 80, SSL: false},
+		},
+		// Bulk replace Origins
+		services.Replacement{
+			Index: 0, // Ignored
+			Value: services.OriginList{
+				{Origin: "44.33.22.11", Port: 80, SSL: false},
+				{Origin: "55.44.33.22", Port: 443, SSL: true},
+			},
+		},
+		// Remove a single CacheRule
+		services.Removal{
+			Index: 8,
+			Path:  services.PathCaching,
+		},
+		// Bulk removal
+		services.Removal{
+			All:  true,
+			Path: services.PathCaching,
+		},
+		// Service name replacement
+		services.NameReplacement{
+			NewName: "differentServiceName",
+		},
+	}
+
+	actual, err := services.Update(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", ops).Extract()
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, expected, actual)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleDeleteCDNServiceSuccessfully(t)
+
+	err := services.Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/cdn/v1/services/urls.go b/openstack/cdn/v1/services/urls.go
index d953d4c..5bb3ca9 100644
--- a/openstack/cdn/v1/services/urls.go
+++ b/openstack/cdn/v1/services/urls.go
@@ -1,6 +1,6 @@
 package services
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func listURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("services")
diff --git a/openstack/client.go b/openstack/client.go
index 0585211..680a782 100644
--- a/openstack/client.go
+++ b/openstack/client.go
@@ -3,12 +3,11 @@
 import (
 	"fmt"
 	"net/url"
-	"strings"
 
-	"github.com/rackspace/gophercloud"
-	tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
-	tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
-	"github.com/rackspace/gophercloud/openstack/utils"
+	"github.com/gophercloud/gophercloud"
+	tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
+	tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
+	"github.com/gophercloud/gophercloud/openstack/utils"
 )
 
 const (
@@ -76,9 +75,9 @@
 
 	switch chosen.ID {
 	case v20:
-		return v2auth(client, endpoint, options)
+		return v2auth(client, endpoint, options, gophercloud.EndpointOpts{})
 	case v30:
-		return v3auth(client, endpoint, options)
+		return v3auth(client, endpoint, options, gophercloud.EndpointOpts{})
 	default:
 		// The switch statement must be out of date from the versions list.
 		return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
@@ -86,17 +85,31 @@
 }
 
 // AuthenticateV2 explicitly authenticates against the identity v2 endpoint.
-func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
-	return v2auth(client, "", options)
+func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
+	return v2auth(client, "", options, eo)
 }
 
-func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error {
-	v2Client := NewIdentityV2(client)
+func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
+	v2Client, err := NewIdentityV2(client, eo)
+	if err != nil {
+		return err
+	}
+
 	if endpoint != "" {
 		v2Client.Endpoint = endpoint
 	}
 
-	result := tokens2.Create(v2Client, tokens2.AuthOptions{AuthOptions: options})
+	v2Opts := tokens2.AuthOptions{
+		IdentityEndpoint: options.IdentityEndpoint,
+		Username:         options.Username,
+		Password:         options.Password,
+		TenantID:         options.TenantID,
+		TenantName:       options.TenantName,
+		AllowReauth:      options.AllowReauth,
+		TokenID:          options.TokenID,
+	}
+
+	result := tokens2.Create(v2Client, v2Opts)
 
 	token, err := result.ExtractToken()
 	if err != nil {
@@ -111,7 +124,7 @@
 	if options.AllowReauth {
 		client.ReauthFunc = func() error {
 			client.TokenID = ""
-			return v2auth(client, endpoint, options)
+			return v2auth(client, endpoint, options, eo)
 		}
 	}
 	client.TokenID = token.ID
@@ -123,13 +136,17 @@
 }
 
 // AuthenticateV3 explicitly authenticates against the identity v3 service.
-func AuthenticateV3(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
-	return v3auth(client, "", options)
+func AuthenticateV3(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
+	return v3auth(client, "", options, eo)
 }
 
-func v3auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error {
+func v3auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
 	// Override the generated service endpoint with the one returned by the version endpoint.
-	v3Client := NewIdentityV3(client)
+	v3Client, err := NewIdentityV3(client, eo)
+	if err != nil {
+		return err
+	}
+
 	if endpoint != "" {
 		v3Client.Endpoint = endpoint
 	}
@@ -156,7 +173,20 @@
 		}
 	}
 
-	result := tokens3.Create(v3Client, v3Options, scope)
+	v3Opts := tokens3.AuthOptions{
+		IdentityEndpoint: options.IdentityEndpoint,
+		Username:         options.Username,
+		UserID:           options.UserID,
+		Password:         options.Password,
+		DomainID:         options.DomainID,
+		DomainName:       options.DomainName,
+		TenantID:         options.TenantID,
+		TenantName:       options.TenantName,
+		AllowReauth:      options.AllowReauth,
+		TokenID:          options.TokenID,
+	}
+
+	result := tokens3.Create(v3Client, v3Opts, scope)
 
 	token, err := result.ExtractToken()
 	if err != nil {
@@ -173,7 +203,7 @@
 	if options.AllowReauth {
 		client.ReauthFunc = func() error {
 			client.TokenID = ""
-			return v3auth(client, endpoint, options)
+			return v3auth(client, endpoint, options, eo)
 		}
 	}
 	client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
@@ -184,57 +214,39 @@
 }
 
 // NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
-func NewIdentityV2(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
+func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
 	v2Endpoint := client.IdentityBase + "v2.0/"
+	/*
+		eo.ApplyDefaults("identity")
+		url, err := client.EndpointLocator(eo)
+		if err != nil {
+			return nil, err
+		}
+	*/
 
 	return &gophercloud.ServiceClient{
 		ProviderClient: client,
 		Endpoint:       v2Endpoint,
-	}
+		//Endpoint: url,
+	}, nil
 }
 
 // NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
-func NewIdentityV3(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
+func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
 	v3Endpoint := client.IdentityBase + "v3/"
+	/*
+		eo.ApplyDefaults("identity")
+		url, err := client.EndpointLocator(eo)
+		if err != nil {
+			return nil, err
+		}
+	*/
 
 	return &gophercloud.ServiceClient{
 		ProviderClient: client,
 		Endpoint:       v3Endpoint,
-	}
-}
-
-func NewIdentityAdminV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("identity")
-	eo.Availability = gophercloud.AvailabilityAdmin
-
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-
-	// Force using v2 API
-	if strings.Contains(url, "/v3") {
-		url = strings.Replace(url, "/v3", "/v2.0", -1)
-	}
-
-	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
-}
-
-func NewIdentityAdminV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("identity")
-	eo.Availability = gophercloud.AvailabilityAdmin
-
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-
-	// Force using v3 API
-	if strings.Contains(url, "/v2.0") {
-		url = strings.Replace(url, "/v2.0", "/v3", -1)
-	}
-
-	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
+		//Endpoint: url,
+	}, nil
 }
 
 // NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
diff --git a/openstack/client_test.go b/openstack/client_test.go
deleted file mode 100644
index 257260c..0000000
--- a/openstack/client_test.go
+++ /dev/null
@@ -1,161 +0,0 @@
-package openstack
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestAuthenticatedClientV3(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	const ID = "0123456789"
-
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		fmt.Fprintf(w, `
-			{
-				"versions": {
-					"values": [
-						{
-							"status": "stable",
-							"id": "v3.0",
-							"links": [
-								{ "href": "%s", "rel": "self" }
-							]
-						},
-						{
-							"status": "stable",
-							"id": "v2.0",
-							"links": [
-								{ "href": "%s", "rel": "self" }
-							]
-						}
-					]
-				}
-			}
-		`, th.Endpoint()+"v3/", th.Endpoint()+"v2.0/")
-	})
-
-	th.Mux.HandleFunc("/v3/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Add("X-Subject-Token", ID)
-
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, `{ "token": { "expires_at": "2013-02-02T18:30:59.000000Z" } }`)
-	})
-
-	options := gophercloud.AuthOptions{
-		UserID:           "me",
-		Password:         "secret",
-		IdentityEndpoint: th.Endpoint(),
-	}
-	client, err := AuthenticatedClient(options)
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, ID, client.TokenID)
-}
-
-func TestAuthenticatedClientV2(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		fmt.Fprintf(w, `
-			{
-				"versions": {
-					"values": [
-						{
-							"status": "experimental",
-							"id": "v3.0",
-							"links": [
-								{ "href": "%s", "rel": "self" }
-							]
-						},
-						{
-							"status": "stable",
-							"id": "v2.0",
-							"links": [
-								{ "href": "%s", "rel": "self" }
-							]
-						}
-					]
-				}
-			}
-		`, th.Endpoint()+"v3/", th.Endpoint()+"v2.0/")
-	})
-
-	th.Mux.HandleFunc("/v2.0/tokens", func(w http.ResponseWriter, r *http.Request) {
-		fmt.Fprintf(w, `
-			{
-				"access": {
-					"token": {
-						"id": "01234567890",
-						"expires": "2014-10-01T10:00:00.000000Z"
-					},
-					"serviceCatalog": [
-						{
-							"name": "Cloud Servers",
-							"type": "compute",
-							"endpoints": [
-								{
-									"tenantId": "t1000",
-									"publicURL": "https://compute.north.host.com/v1/t1000",
-									"internalURL": "https://compute.north.internal/v1/t1000",
-									"region": "North",
-									"versionId": "1",
-									"versionInfo": "https://compute.north.host.com/v1/",
-									"versionList": "https://compute.north.host.com/"
-								},
-								{
-									"tenantId": "t1000",
-									"publicURL": "https://compute.north.host.com/v1.1/t1000",
-									"internalURL": "https://compute.north.internal/v1.1/t1000",
-									"region": "North",
-									"versionId": "1.1",
-									"versionInfo": "https://compute.north.host.com/v1.1/",
-									"versionList": "https://compute.north.host.com/"
-								}
-							],
-							"endpoints_links": []
-						},
-						{
-							"name": "Cloud Files",
-							"type": "object-store",
-							"endpoints": [
-								{
-									"tenantId": "t1000",
-									"publicURL": "https://storage.north.host.com/v1/t1000",
-									"internalURL": "https://storage.north.internal/v1/t1000",
-									"region": "North",
-									"versionId": "1",
-									"versionInfo": "https://storage.north.host.com/v1/",
-									"versionList": "https://storage.north.host.com/"
-								},
-								{
-									"tenantId": "t1000",
-									"publicURL": "https://storage.south.host.com/v1/t1000",
-									"internalURL": "https://storage.south.internal/v1/t1000",
-									"region": "South",
-									"versionId": "1",
-									"versionInfo": "https://storage.south.host.com/v1/",
-									"versionList": "https://storage.south.host.com/"
-								}
-							]
-						}
-					]
-				}
-			}
-		`)
-	})
-
-	options := gophercloud.AuthOptions{
-		Username:         "me",
-		Password:         "secret",
-		IdentityEndpoint: th.Endpoint(),
-	}
-	client, err := AuthenticatedClient(options)
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, "01234567890", client.TokenID)
-}
diff --git a/openstack/common/extensions/fixtures.go b/openstack/common/extensions/fixtures.go
deleted file mode 100644
index 0ed7de9..0000000
--- a/openstack/common/extensions/fixtures.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// +build fixtures
-
-package extensions
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ListOutput provides a single page of Extension results.
-const ListOutput = `
-{
-	"extensions": [
-		{
-			"updated": "2013-01-20T00:00:00-00:00",
-			"name": "Neutron Service Type Management",
-			"links": [],
-			"namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
-			"alias": "service-type",
-			"description": "API for retrieving service providers for Neutron advanced services"
-		}
-	]
-}`
-
-// GetOutput provides a single Extension result.
-const GetOutput = `
-{
-	"extension": {
-		"updated": "2013-02-03T10:00:00-00:00",
-		"name": "agent",
-		"links": [],
-		"namespace": "http://docs.openstack.org/ext/agent/api/v2.0",
-		"alias": "agent",
-		"description": "The agent management extension."
-	}
-}
-`
-
-// ListedExtension is the Extension that should be parsed from ListOutput.
-var ListedExtension = Extension{
-	Updated:     "2013-01-20T00:00:00-00:00",
-	Name:        "Neutron Service Type Management",
-	Links:       []interface{}{},
-	Namespace:   "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
-	Alias:       "service-type",
-	Description: "API for retrieving service providers for Neutron advanced services",
-}
-
-// ExpectedExtensions is a slice containing the Extension that should be parsed from ListOutput.
-var ExpectedExtensions = []Extension{ListedExtension}
-
-// SingleExtension is the Extension that should be parsed from GetOutput.
-var SingleExtension = &Extension{
-	Updated:     "2013-02-03T10:00:00-00:00",
-	Name:        "agent",
-	Links:       []interface{}{},
-	Namespace:   "http://docs.openstack.org/ext/agent/api/v2.0",
-	Alias:       "agent",
-	Description: "The agent management extension.",
-}
-
-// HandleListExtensionsSuccessfully creates an HTTP handler at `/extensions` on the test handler
-// mux that response with a list containing a single tenant.
-func HandleListExtensionsSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/extensions", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-
-		fmt.Fprintf(w, ListOutput)
-	})
-}
-
-// HandleGetExtensionSuccessfully creates an HTTP handler at `/extensions/agent` that responds with
-// a JSON payload corresponding to SingleExtension.
-func HandleGetExtensionSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/extensions/agent", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, GetOutput)
-	})
-}
diff --git a/openstack/common/extensions/requests.go b/openstack/common/extensions/requests.go
index 0b71085..46b7d60 100755
--- a/openstack/common/extensions/requests.go
+++ b/openstack/common/extensions/requests.go
@@ -1,15 +1,14 @@
 package extensions
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Get retrieves information for a specific extension using its alias.
-func Get(c *gophercloud.ServiceClient, alias string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(ExtensionURL(c, alias), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, alias string) (r GetResult) {
+	_, r.Err = c.Get(ExtensionURL(c, alias), &r.Body, nil)
+	return
 }
 
 // List returns a Pager which allows you to iterate over the full collection of extensions.
diff --git a/openstack/common/extensions/requests_test.go b/openstack/common/extensions/requests_test.go
deleted file mode 100644
index 6550283..0000000
--- a/openstack/common/extensions/requests_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package extensions
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListExtensionsSuccessfully(t)
-
-	count := 0
-
-	List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractExtensions(page)
-		th.AssertNoErr(t, err)
-		th.AssertDeepEquals(t, ExpectedExtensions, actual)
-
-		return true, nil
-	})
-
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetExtensionSuccessfully(t)
-
-	actual, err := Get(client.ServiceClient(), "agent").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, SingleExtension, actual)
-}
diff --git a/openstack/common/extensions/results.go b/openstack/common/extensions/results.go
index 777d083..d5f8650 100755
--- a/openstack/common/extensions/results.go
+++ b/openstack/common/extensions/results.go
@@ -1,9 +1,8 @@
 package extensions
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // GetResult temporarily stores the result of a Get call.
@@ -14,27 +13,21 @@
 
 // Extract interprets a GetResult as an Extension.
 func (r GetResult) Extract() (*Extension, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Extension *Extension `json:"extension"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Extension, err
+	err := r.ExtractInto(&s)
+	return s.Extension, err
 }
 
 // Extension is a struct that represents an OpenStack extension.
 type Extension struct {
-	Updated     string        `json:"updated" mapstructure:"updated"`
-	Name        string        `json:"name" mapstructure:"name"`
-	Links       []interface{} `json:"links" mapstructure:"links"`
-	Namespace   string        `json:"namespace" mapstructure:"namespace"`
-	Alias       string        `json:"alias" mapstructure:"alias"`
-	Description string        `json:"description" mapstructure:"description"`
+	Updated     string        `json:"updated"`
+	Name        string        `json:"name"`
+	Links       []interface{} `json:"links"`
+	Namespace   string        `json:"namespace"`
+	Alias       string        `json:"alias"`
+	Description string        `json:"description"`
 }
 
 // ExtensionPage is the page returned by a pager when traversing over a collection of extensions.
@@ -45,21 +38,16 @@
 // IsEmpty checks whether an ExtensionPage struct is empty.
 func (r ExtensionPage) IsEmpty() (bool, error) {
 	is, err := ExtractExtensions(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractExtensions accepts a Page struct, specifically an ExtensionPage struct, and extracts the
 // elements into a slice of Extension structs.
 // In other words, a generic collection is mapped into a relevant slice.
-func ExtractExtensions(page pagination.Page) ([]Extension, error) {
-	var resp struct {
-		Extensions []Extension `mapstructure:"extensions"`
+func ExtractExtensions(r pagination.Page) ([]Extension, error) {
+	var s struct {
+		Extensions []Extension `json:"extensions"`
 	}
-
-	err := mapstructure.Decode(page.(ExtensionPage).Body, &resp)
-
-	return resp.Extensions, err
+	err := (r.(ExtensionPage)).ExtractInto(&s)
+	return s.Extensions, err
 }
diff --git a/openstack/common/extensions/testing/doc.go b/openstack/common/extensions/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/common/extensions/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/common/extensions/testing/fixtures.go b/openstack/common/extensions/testing/fixtures.go
new file mode 100644
index 0000000..a986c95
--- /dev/null
+++ b/openstack/common/extensions/testing/fixtures.go
@@ -0,0 +1,90 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/common/extensions"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ListOutput provides a single page of Extension results.
+const ListOutput = `
+{
+	"extensions": [
+		{
+			"updated": "2013-01-20T00:00:00-00:00",
+			"name": "Neutron Service Type Management",
+			"links": [],
+			"namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
+			"alias": "service-type",
+			"description": "API for retrieving service providers for Neutron advanced services"
+		}
+	]
+}`
+
+// GetOutput provides a single Extension result.
+const GetOutput = `
+{
+	"extension": {
+		"updated": "2013-02-03T10:00:00-00:00",
+		"name": "agent",
+		"links": [],
+		"namespace": "http://docs.openstack.org/ext/agent/api/v2.0",
+		"alias": "agent",
+		"description": "The agent management extension."
+	}
+}
+`
+
+// ListedExtension is the Extension that should be parsed from ListOutput.
+var ListedExtension = extensions.Extension{
+	Updated:     "2013-01-20T00:00:00-00:00",
+	Name:        "Neutron Service Type Management",
+	Links:       []interface{}{},
+	Namespace:   "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
+	Alias:       "service-type",
+	Description: "API for retrieving service providers for Neutron advanced services",
+}
+
+// ExpectedExtensions is a slice containing the Extension that should be parsed from ListOutput.
+var ExpectedExtensions = []extensions.Extension{ListedExtension}
+
+// SingleExtension is the Extension that should be parsed from GetOutput.
+var SingleExtension = &extensions.Extension{
+	Updated:     "2013-02-03T10:00:00-00:00",
+	Name:        "agent",
+	Links:       []interface{}{},
+	Namespace:   "http://docs.openstack.org/ext/agent/api/v2.0",
+	Alias:       "agent",
+	Description: "The agent management extension.",
+}
+
+// HandleListExtensionsSuccessfully creates an HTTP handler at `/extensions` on the test handler
+// mux that response with a list containing a single tenant.
+func HandleListExtensionsSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/extensions", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+
+		fmt.Fprintf(w, ListOutput)
+	})
+}
+
+// HandleGetExtensionSuccessfully creates an HTTP handler at `/extensions/agent` that responds with
+// a JSON payload corresponding to SingleExtension.
+func HandleGetExtensionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/extensions/agent", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, GetOutput)
+	})
+}
diff --git a/openstack/common/extensions/testing/requests_test.go b/openstack/common/extensions/testing/requests_test.go
new file mode 100644
index 0000000..fbaedfa
--- /dev/null
+++ b/openstack/common/extensions/testing/requests_test.go
@@ -0,0 +1,39 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/common/extensions"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListExtensionsSuccessfully(t)
+
+	count := 0
+
+	extensions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := extensions.ExtractExtensions(page)
+		th.AssertNoErr(t, err)
+		th.AssertDeepEquals(t, ExpectedExtensions, actual)
+
+		return true, nil
+	})
+
+	th.CheckEquals(t, 1, count)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetExtensionSuccessfully(t)
+
+	actual, err := extensions.Get(client.ServiceClient(), "agent").Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, SingleExtension, actual)
+}
diff --git a/openstack/common/extensions/urls.go b/openstack/common/extensions/urls.go
index 6460c66..eaf38b2 100644
--- a/openstack/common/extensions/urls.go
+++ b/openstack/common/extensions/urls.go
@@ -1,6 +1,6 @@
 package extensions
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 // ExtensionURL generates the URL for an extension resource by name.
 func ExtensionURL(c *gophercloud.ServiceClient, name string) string {
diff --git a/openstack/common/extensions/urls_test.go b/openstack/common/extensions/urls_test.go
deleted file mode 100755
index 3223b1c..0000000
--- a/openstack/common/extensions/urls_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package extensions
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestExtensionURL(t *testing.T) {
-	actual := ExtensionURL(endpointClient(), "agent")
-	expected := endpoint + "extensions/agent"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestListExtensionURL(t *testing.T) {
-	actual := ListExtensionURL(endpointClient())
-	expected := endpoint + "extensions"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go
index dceff3d..28fef94 100644
--- a/openstack/compute/v2/extensions/bootfromvolume/requests.go
+++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go
@@ -1,48 +1,43 @@
 package bootfromvolume
 
 import (
-	"errors"
-	"strconv"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 )
 
 // SourceType represents the type of medium being used to create the volume.
 type SourceType string
 
 const (
-	Volume   SourceType = "volume"
+	// Volume SourceType
+	Volume SourceType = "volume"
+	// Snapshot SourceType
 	Snapshot SourceType = "snapshot"
-	Image    SourceType = "image"
-	Blank    SourceType = "blank"
+	// Image SourceType
+	Image SourceType = "image"
+	// Blank SourceType
+	Blank SourceType = "blank"
 )
 
 // BlockDevice is a structure with options for booting a server instance
 // from a volume. The volume may be created from an image, snapshot, or another
 // volume.
 type BlockDevice struct {
-	// BootIndex [optional] is the boot index. It defaults to 0.
+	// SourceType must be one of: "volume", "snapshot", "image".
+	SourceType SourceType `json:"source_type" required:"true"`
+	// UUID is the unique identifier for the volume, snapshot, or image (see above)
+	UUID string `json:"uuid,omitempty"`
+	// BootIndex is the boot index. It defaults to 0.
 	BootIndex int `json:"boot_index"`
-
-	// DeleteOnTermination [optional] specifies whether or not to delete the attached volume
+	// DeleteOnTermination specifies whether or not to delete the attached volume
 	// when the server is deleted. Defaults to `false`.
 	DeleteOnTermination bool `json:"delete_on_termination"`
-
-	// DestinationType [optional] is the type that gets created. Possible values are "volume"
+	// DestinationType is the type that gets created. Possible values are "volume"
 	// and "local".
-	DestinationType string `json:"destination_type"`
-
-	// GuestFormat [optional] specifies the format of the block device.
-	GuestFormat string `json:"guest_format"`
-
-	// SourceType [required] must be one of: "volume", "snapshot", "image".
-	SourceType SourceType `json:"source_type"`
-
-	// UUID [required] is the unique identifier for the volume, snapshot, or image (see above)
-	UUID string `json:"uuid"`
-
-	// VolumeSize [optional] is the size of the volume to create (in gigabytes).
+	DestinationType string `json:"destination_type,omitempty"`
+	// GuestFormat specifies the format of the block device.
+	GuestFormat string `json:"guest_format,omitempty"`
+	// VolumeSize is the size of the volume to create (in gigabytes).
 	VolumeSize int `json:"volume_size"`
 }
 
@@ -62,7 +57,9 @@
 	}
 
 	if len(opts.BlockDevice) == 0 {
-		return nil, errors.New("Required fields UUID and SourceType not set.")
+		err := gophercloud.ErrMissingInput{}
+		err.Argument = "bootfromvolume.CreateOptsExt.BlockDevice"
+		return nil, err
 	}
 
 	serverMap := base["server"].(map[string]interface{})
@@ -70,26 +67,11 @@
 	blockDevice := make([]map[string]interface{}, len(opts.BlockDevice))
 
 	for i, bd := range opts.BlockDevice {
-		if string(bd.SourceType) == "" {
-			return nil, errors.New("SourceType must be one of: volume, image, snapshot.")
+		b, err := gophercloud.BuildRequestBody(bd, "")
+		if err != nil {
+			return nil, err
 		}
-
-		blockDevice[i] = make(map[string]interface{})
-
-		blockDevice[i]["source_type"] = bd.SourceType
-		blockDevice[i]["boot_index"] = strconv.Itoa(bd.BootIndex)
-		blockDevice[i]["delete_on_termination"] = strconv.FormatBool(bd.DeleteOnTermination)
-		blockDevice[i]["volume_size"] = strconv.Itoa(bd.VolumeSize)
-		if bd.UUID != "" {
-			blockDevice[i]["uuid"] = bd.UUID
-		}
-		if bd.DestinationType != "" {
-			blockDevice[i]["destination_type"] = bd.DestinationType
-		}
-		if bd.GuestFormat != "" {
-			blockDevice[i]["guest_format"] = bd.GuestFormat
-		}
-
+		blockDevice[i] = b
 	}
 	serverMap["block_device_mapping_v2"] = blockDevice
 
@@ -97,22 +79,14 @@
 }
 
 // Create requests the creation of a server from the given block device mapping.
-func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) servers.CreateResult {
-	var res servers.CreateResult
-
-	reqBody, err := opts.ToServerCreateMap()
+func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) (r servers.CreateResult) {
+	b, err := opts.ToServerCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Delete imageName and flavorName that come from ToServerCreateMap().
-	// As of Liberty, Boot From Volume is failing if they are passed.
-	delete(reqBody["server"].(map[string]interface{}), "imageName")
-	delete(reqBody["server"].(map[string]interface{}), "flavorName")
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
-	return res
+	return
 }
diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests_test.go b/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
deleted file mode 100644
index 2c8bf49..0000000
--- a/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
+++ /dev/null
@@ -1,131 +0,0 @@
-package bootfromvolume
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestCreateOpts(t *testing.T) {
-	base := servers.CreateOpts{
-		Name:      "createdserver",
-		ImageRef:  "asdfasdfasdf",
-		FlavorRef: "performance1-1",
-	}
-
-	ext := CreateOptsExt{
-		CreateOptsBuilder: base,
-		BlockDevice: []BlockDevice{
-			BlockDevice{
-				UUID:            "123456",
-				SourceType:      Image,
-				DestinationType: "volume",
-				VolumeSize:      10,
-			},
-		},
-	}
-
-	expected := `
-    {
-      "server": {
-        "name": "createdserver",
-        "imageRef": "asdfasdfasdf",
-        "flavorRef": "performance1-1",
-        "flavorName": "",
-        "imageName": "",
-        "block_device_mapping_v2":[
-          {
-            "uuid":"123456",
-            "source_type":"image",
-            "destination_type":"volume",
-            "boot_index": "0",
-            "delete_on_termination": "false",
-            "volume_size": "10"
-          }
-        ]
-      }
-    }
-  `
-	actual, err := ext.ToServerCreateMap()
-	th.AssertNoErr(t, err)
-	th.CheckJSONEquals(t, expected, actual)
-}
-
-func TestCreateMultiEphemeralOpts(t *testing.T) {
-	base := servers.CreateOpts{
-		Name:      "createdserver",
-		ImageRef:  "asdfasdfasdf",
-		FlavorRef: "performance1-1",
-	}
-
-	ext := CreateOptsExt{
-		CreateOptsBuilder: base,
-		BlockDevice: []BlockDevice{
-			BlockDevice{
-				BootIndex:           0,
-				DeleteOnTermination: true,
-				DestinationType:     "local",
-				SourceType:          Image,
-				UUID:                "123456",
-			},
-			BlockDevice{
-				BootIndex:           -1,
-				DeleteOnTermination: true,
-				DestinationType:     "local",
-				GuestFormat:         "ext4",
-				SourceType:          Blank,
-				VolumeSize:          1,
-			},
-			BlockDevice{
-				BootIndex:           -1,
-				DeleteOnTermination: true,
-				DestinationType:     "local",
-				GuestFormat:         "ext4",
-				SourceType:          Blank,
-				VolumeSize:          1,
-			},
-		},
-	}
-
-	expected := `
-    {
-      "server": {
-        "name": "createdserver",
-        "imageRef": "asdfasdfasdf",
-        "flavorRef": "performance1-1",
-        "flavorName": "",
-        "imageName": "",
-        "block_device_mapping_v2":[
-          {
-            "boot_index": "0",
-            "delete_on_termination": "true",
-            "destination_type":"local",
-            "source_type":"image",
-            "uuid":"123456",
-            "volume_size": "0"
-          },
-          {
-            "boot_index": "-1",
-            "delete_on_termination": "true",
-            "destination_type":"local",
-            "guest_format":"ext4",
-            "source_type":"blank",
-            "volume_size": "1"
-          },
-          {
-            "boot_index": "-1",
-            "delete_on_termination": "true",
-            "destination_type":"local",
-            "guest_format":"ext4",
-            "source_type":"blank",
-            "volume_size": "1"
-          }
-        ]
-      }
-    }
-  `
-	actual, err := ext.ToServerCreateMap()
-	th.AssertNoErr(t, err)
-	th.CheckJSONEquals(t, expected, actual)
-}
diff --git a/openstack/compute/v2/extensions/bootfromvolume/results.go b/openstack/compute/v2/extensions/bootfromvolume/results.go
index f60329f..3211fb1 100644
--- a/openstack/compute/v2/extensions/bootfromvolume/results.go
+++ b/openstack/compute/v2/extensions/bootfromvolume/results.go
@@ -1,7 +1,7 @@
 package bootfromvolume
 
 import (
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+	os "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 )
 
 // CreateResult temporarily contains the response from a Create call.
diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/doc.go b/openstack/compute/v2/extensions/bootfromvolume/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/bootfromvolume/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go b/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go
new file mode 100644
index 0000000..dca1105
--- /dev/null
+++ b/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go
@@ -0,0 +1,129 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestCreateOpts(t *testing.T) {
+	base := servers.CreateOpts{
+		Name:      "createdserver",
+		ImageRef:  "asdfasdfasdf",
+		FlavorRef: "performance1-1",
+	}
+
+	ext := bootfromvolume.CreateOptsExt{
+		CreateOptsBuilder: base,
+		BlockDevice: []bootfromvolume.BlockDevice{
+			{
+				UUID:                "123456",
+				SourceType:          bootfromvolume.Image,
+				DestinationType:     "volume",
+				VolumeSize:          10,
+				DeleteOnTermination: false,
+			},
+		},
+	}
+
+	expected := `
+    {
+      "server": {
+        "name": "createdserver",
+        "imageRef": "asdfasdfasdf",
+        "flavorRef": "performance1-1",
+        "block_device_mapping_v2":[
+          {
+            "uuid":"123456",
+            "source_type":"image",
+            "destination_type":"volume",
+            "boot_index": 0,
+            "delete_on_termination": false,
+            "volume_size": 10
+          }
+        ]
+      }
+    }
+  `
+	actual, err := ext.ToServerCreateMap()
+	th.AssertNoErr(t, err)
+	th.CheckJSONEquals(t, expected, actual)
+}
+
+func TestCreateMultiEphemeralOpts(t *testing.T) {
+	base := servers.CreateOpts{
+		Name:      "createdserver",
+		ImageRef:  "asdfasdfasdf",
+		FlavorRef: "performance1-1",
+	}
+
+	ext := bootfromvolume.CreateOptsExt{
+		CreateOptsBuilder: base,
+		BlockDevice: []bootfromvolume.BlockDevice{
+			{
+				BootIndex:           0,
+				DeleteOnTermination: true,
+				DestinationType:     "local",
+				SourceType:          bootfromvolume.Image,
+				UUID:                "123456",
+			},
+			{
+				BootIndex:           -1,
+				DeleteOnTermination: true,
+				DestinationType:     "local",
+				GuestFormat:         "ext4",
+				SourceType:          bootfromvolume.Blank,
+				VolumeSize:          1,
+			},
+			{
+				BootIndex:           -1,
+				DeleteOnTermination: true,
+				DestinationType:     "local",
+				GuestFormat:         "ext4",
+				SourceType:          bootfromvolume.Blank,
+				VolumeSize:          1,
+			},
+		},
+	}
+
+	expected := `
+    {
+      "server": {
+        "name": "createdserver",
+        "imageRef": "asdfasdfasdf",
+        "flavorRef": "performance1-1",
+        "block_device_mapping_v2":[
+          {
+            "boot_index": 0,
+            "delete_on_termination": true,
+            "destination_type":"local",
+            "source_type":"image",
+            "uuid":"123456",
+            "volume_size": 0
+          },
+          {
+            "boot_index": -1,
+            "delete_on_termination": true,
+            "destination_type":"local",
+            "guest_format":"ext4",
+            "source_type":"blank",
+            "volume_size": 1
+          },
+          {
+            "boot_index": -1,
+            "delete_on_termination": true,
+            "destination_type":"local",
+            "guest_format":"ext4",
+            "source_type":"blank",
+            "volume_size": 1
+          }
+        ]
+      }
+    }
+  `
+	actual, err := ext.ToServerCreateMap()
+	th.AssertNoErr(t, err)
+	th.CheckJSONEquals(t, expected, actual)
+}
diff --git a/openstack/compute/v2/extensions/bootfromvolume/urls.go b/openstack/compute/v2/extensions/bootfromvolume/urls.go
index 0cffe25..dc007ea 100644
--- a/openstack/compute/v2/extensions/bootfromvolume/urls.go
+++ b/openstack/compute/v2/extensions/bootfromvolume/urls.go
@@ -1,6 +1,6 @@
 package bootfromvolume
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func createURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("os-volumes_boot")
diff --git a/openstack/compute/v2/extensions/bootfromvolume/urls_test.go b/openstack/compute/v2/extensions/bootfromvolume/urls_test.go
deleted file mode 100644
index 6ee6477..0000000
--- a/openstack/compute/v2/extensions/bootfromvolume/urls_test.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package bootfromvolume
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestCreateURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-volumes_boot", createURL(c))
-}
diff --git a/openstack/compute/v2/extensions/defsecrules/fixtures.go b/openstack/compute/v2/extensions/defsecrules/fixtures.go
deleted file mode 100644
index 9b3c2b6..0000000
--- a/openstack/compute/v2/extensions/defsecrules/fixtures.go
+++ /dev/null
@@ -1,145 +0,0 @@
-// +build fixtures
-
-package defsecrules
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const rootPath = "/os-security-group-default-rules"
-
-func mockListRulesResponse(t *testing.T) {
-	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "security_group_default_rules": [
-    {
-      "from_port": 80,
-      "id": "{ruleID}",
-      "ip_protocol": "TCP",
-      "ip_range": {
-        "cidr": "10.10.10.0/24"
-      },
-      "to_port": 80
-    }
-  ]
-}
-      `)
-	})
-}
-
-func mockCreateRuleResponse(t *testing.T) {
-	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "security_group_default_rule": {
-    "ip_protocol": "TCP",
-    "from_port": 80,
-    "to_port": 80,
-    "cidr": "10.10.12.0/24"
-  }
-}
-	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "security_group_default_rule": {
-    "from_port": 80,
-    "id": "{ruleID}",
-    "ip_protocol": "TCP",
-    "ip_range": {
-      "cidr": "10.10.12.0/24"
-    },
-    "to_port": 80
-  }
-}
-`)
-	})
-}
-
-func mockCreateRuleResponseICMPZero(t *testing.T) {
-	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "security_group_default_rule": {
-    "ip_protocol": "ICMP",
-    "from_port": 0,
-    "to_port": 0,
-    "cidr": "10.10.12.0/24"
-  }
-}
-	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "security_group_default_rule": {
-    "from_port": 0,
-    "id": "{ruleID}",
-    "ip_protocol": "ICMP",
-    "ip_range": {
-      "cidr": "10.10.12.0/24"
-    },
-    "to_port": 0
-  }
-}
-`)
-	})
-}
-
-func mockGetRuleResponse(t *testing.T, ruleID string) {
-	url := rootPath + "/" + ruleID
-	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "security_group_default_rule": {
-    "id": "{ruleID}",
-    "from_port": 80,
-    "to_port": 80,
-    "ip_protocol": "TCP",
-    "ip_range": {
-      "cidr": "10.10.12.0/24"
-    }
-  }
-}
-			`)
-	})
-}
-
-func mockDeleteRuleResponse(t *testing.T, ruleID string) {
-	url := rootPath + "/" + ruleID
-	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
diff --git a/openstack/compute/v2/extensions/defsecrules/requests.go b/openstack/compute/v2/extensions/defsecrules/requests.go
index d0098e6..184fdc9 100644
--- a/openstack/compute/v2/extensions/defsecrules/requests.go
+++ b/openstack/compute/v2/extensions/defsecrules/requests.go
@@ -1,33 +1,27 @@
 package defsecrules
 
 import (
-	"errors"
 	"strings"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // List will return a collection of default rules.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, rootURL(client), func(r pagination.PageResult) pagination.Page {
 		return DefaultRulePage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, rootURL(client), createPage)
+	})
 }
 
 // CreateOpts represents the configuration for adding a new default rule.
 type CreateOpts struct {
-	// Required - the lower bound of the port range that will be opened.
+	// The lower bound of the port range that will be opened.s
 	FromPort int `json:"from_port"`
-
-	// Required - the upper bound of the port range that will be opened.
+	// The upper bound of the port range that will be opened.
 	ToPort int `json:"to_port"`
-
-	// Required - the protocol type that will be allowed, e.g. TCP.
-	IPProtocol string `json:"ip_protocol"`
-
+	// The protocol type that will be allowed, e.g. TCP.
+	IPProtocol string `json:"ip_protocol" required:"true"`
 	// ONLY required if FromGroupID is blank. This represents the IP range that
 	// will be the source of network traffic to your security group. Use
 	// 0.0.0.0/0 to allow all IP addresses.
@@ -41,56 +35,36 @@
 
 // ToRuleCreateMap builds the create rule options into a serializable format.
 func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
-	rule := make(map[string]interface{})
-
 	if opts.FromPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
-		return rule, errors.New("A FromPort must be set")
+		return nil, gophercloud.ErrMissingInput{Argument: "FromPort"}
 	}
 	if opts.ToPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
-		return rule, errors.New("A ToPort must be set")
+		return nil, gophercloud.ErrMissingInput{Argument: "ToPort"}
 	}
-	if opts.IPProtocol == "" {
-		return rule, errors.New("A IPProtocol must be set")
-	}
-	if opts.CIDR == "" {
-		return rule, errors.New("A CIDR must be set")
-	}
-
-	rule["from_port"] = opts.FromPort
-	rule["to_port"] = opts.ToPort
-	rule["ip_protocol"] = opts.IPProtocol
-	rule["cidr"] = opts.CIDR
-
-	return map[string]interface{}{"security_group_default_rule": rule}, nil
+	return gophercloud.BuildRequestBody(opts, "security_group_default_rule")
 }
 
 // Create is the operation responsible for creating a new default rule.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var result CreateResult
-
-	reqBody, err := opts.ToRuleCreateMap()
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToRuleCreateMap()
 	if err != nil {
-		result.Err = err
-		return result
+		r.Err = err
+		return
 	}
-
-	_, result.Err = client.Post(rootURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return result
+	return
 }
 
 // Get will return details for a particular default rule.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(resourceURL(client, id), &result.Body, nil)
-	return result
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(resourceURL(client, id), &r.Body, nil)
+	return
 }
 
 // Delete will permanently delete a default rule from the project.
-func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
-	var result gophercloud.ErrResult
-	_, result.Err = client.Delete(resourceURL(client, id), nil)
-	return result
+func Delete(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) {
+	_, r.Err = client.Delete(resourceURL(client, id), nil)
+	return
 }
diff --git a/openstack/compute/v2/extensions/defsecrules/requests_test.go b/openstack/compute/v2/extensions/defsecrules/requests_test.go
deleted file mode 100644
index 1ab6637..0000000
--- a/openstack/compute/v2/extensions/defsecrules/requests_test.go
+++ /dev/null
@@ -1,126 +0,0 @@
-package defsecrules
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const ruleID = "{ruleID}"
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListRulesResponse(t)
-
-	count := 0
-
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractDefaultRules(page)
-		th.AssertNoErr(t, err)
-
-		expected := []DefaultRule{
-			DefaultRule{
-				FromPort:   80,
-				ID:         ruleID,
-				IPProtocol: "TCP",
-				IPRange:    secgroups.IPRange{CIDR: "10.10.10.0/24"},
-				ToPort:     80,
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockCreateRuleResponse(t)
-
-	opts := CreateOpts{
-		IPProtocol: "TCP",
-		FromPort:   80,
-		ToPort:     80,
-		CIDR:       "10.10.12.0/24",
-	}
-
-	group, err := Create(client.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &DefaultRule{
-		ID:         ruleID,
-		FromPort:   80,
-		ToPort:     80,
-		IPProtocol: "TCP",
-		IPRange:    secgroups.IPRange{CIDR: "10.10.12.0/24"},
-	}
-	th.AssertDeepEquals(t, expected, group)
-}
-
-func TestCreateICMPZero(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockCreateRuleResponseICMPZero(t)
-
-	opts := CreateOpts{
-		IPProtocol: "ICMP",
-		FromPort:   0,
-		ToPort:     0,
-		CIDR:       "10.10.12.0/24",
-	}
-
-	group, err := Create(client.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &DefaultRule{
-		ID:         ruleID,
-		FromPort:   0,
-		ToPort:     0,
-		IPProtocol: "ICMP",
-		IPRange:    secgroups.IPRange{CIDR: "10.10.12.0/24"},
-	}
-	th.AssertDeepEquals(t, expected, group)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetRuleResponse(t, ruleID)
-
-	group, err := Get(client.ServiceClient(), ruleID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &DefaultRule{
-		ID:         ruleID,
-		FromPort:   80,
-		ToPort:     80,
-		IPProtocol: "TCP",
-		IPRange:    secgroups.IPRange{CIDR: "10.10.12.0/24"},
-	}
-
-	th.AssertDeepEquals(t, expected, group)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteRuleResponse(t, ruleID)
-
-	err := Delete(client.ServiceClient(), ruleID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/compute/v2/extensions/defsecrules/results.go b/openstack/compute/v2/extensions/defsecrules/results.go
index e588d3e..61b918d 100644
--- a/openstack/compute/v2/extensions/defsecrules/results.go
+++ b/openstack/compute/v2/extensions/defsecrules/results.go
@@ -1,11 +1,9 @@
 package defsecrules
 
 import (
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // DefaultRule represents a default rule - which is identical to a
@@ -20,23 +18,17 @@
 // IsEmpty determines whether or not a page of default rules contains any results.
 func (page DefaultRulePage) IsEmpty() (bool, error) {
 	users, err := ExtractDefaultRules(page)
-	if err != nil {
-		return false, err
-	}
-	return len(users) == 0, nil
+	return len(users) == 0, err
 }
 
 // ExtractDefaultRules returns a slice of DefaultRules contained in a single
 // page of results.
-func ExtractDefaultRules(page pagination.Page) ([]DefaultRule, error) {
-	casted := page.(DefaultRulePage).Body
-	var response struct {
-		Rules []DefaultRule `mapstructure:"security_group_default_rules"`
+func ExtractDefaultRules(r pagination.Page) ([]DefaultRule, error) {
+	var s struct {
+		DefaultRules []DefaultRule `json:"security_group_default_rules"`
 	}
-
-	err := mapstructure.WeakDecode(casted, &response)
-
-	return response.Rules, err
+	err := (r.(DefaultRulePage)).ExtractInto(&s)
+	return s.DefaultRules, err
 }
 
 type commonResult struct {
@@ -55,15 +47,9 @@
 
 // Extract will extract a DefaultRule struct from most responses.
 func (r commonResult) Extract() (*DefaultRule, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		DefaultRule DefaultRule `json:"security_group_default_rule"`
 	}
-
-	var response struct {
-		Rule DefaultRule `mapstructure:"security_group_default_rule"`
-	}
-
-	err := mapstructure.WeakDecode(r.Body, &response)
-
-	return &response.Rule, err
+	err := r.ExtractInto(&s)
+	return &s.DefaultRule, err
 }
diff --git a/openstack/compute/v2/extensions/defsecrules/testing/doc.go b/openstack/compute/v2/extensions/defsecrules/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/defsecrules/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/defsecrules/testing/fixtures.go b/openstack/compute/v2/extensions/defsecrules/testing/fixtures.go
new file mode 100644
index 0000000..e4a62d4
--- /dev/null
+++ b/openstack/compute/v2/extensions/defsecrules/testing/fixtures.go
@@ -0,0 +1,143 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+const rootPath = "/os-security-group-default-rules"
+
+func mockListRulesResponse(t *testing.T) {
+	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+  "security_group_default_rules": [
+    {
+      "from_port": 80,
+      "id": "{ruleID}",
+      "ip_protocol": "TCP",
+      "ip_range": {
+        "cidr": "10.10.10.0/24"
+      },
+      "to_port": 80
+    }
+  ]
+}
+      `)
+	})
+}
+
+func mockCreateRuleResponse(t *testing.T) {
+	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		th.TestJSONRequest(t, r, `
+{
+  "security_group_default_rule": {
+    "ip_protocol": "TCP",
+    "from_port": 80,
+    "to_port": 80,
+    "cidr": "10.10.12.0/24"
+  }
+}
+	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+  "security_group_default_rule": {
+    "from_port": 80,
+    "id": "{ruleID}",
+    "ip_protocol": "TCP",
+    "ip_range": {
+      "cidr": "10.10.12.0/24"
+    },
+    "to_port": 80
+  }
+}
+`)
+	})
+}
+
+func mockCreateRuleResponseICMPZero(t *testing.T) {
+	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		th.TestJSONRequest(t, r, `
+{
+  "security_group_default_rule": {
+    "ip_protocol": "ICMP",
+    "from_port": 0,
+    "to_port": 0,
+    "cidr": "10.10.12.0/24"
+  }
+}
+	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+  "security_group_default_rule": {
+    "from_port": 0,
+    "id": "{ruleID}",
+    "ip_protocol": "ICMP",
+    "ip_range": {
+      "cidr": "10.10.12.0/24"
+    },
+    "to_port": 0
+  }
+}
+`)
+	})
+}
+
+func mockGetRuleResponse(t *testing.T, ruleID string) {
+	url := rootPath + "/" + ruleID
+	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+  "security_group_default_rule": {
+    "id": "{ruleID}",
+    "from_port": 80,
+    "to_port": 80,
+    "ip_protocol": "TCP",
+    "ip_range": {
+      "cidr": "10.10.12.0/24"
+    }
+  }
+}
+			`)
+	})
+}
+
+func mockDeleteRuleResponse(t *testing.T, ruleID string) {
+	url := rootPath + "/" + ruleID
+	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
diff --git a/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go b/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go
new file mode 100644
index 0000000..1f2fb86
--- /dev/null
+++ b/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go
@@ -0,0 +1,127 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+const ruleID = "{ruleID}"
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockListRulesResponse(t)
+
+	count := 0
+
+	err := defsecrules.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := defsecrules.ExtractDefaultRules(page)
+		th.AssertNoErr(t, err)
+
+		expected := []defsecrules.DefaultRule{
+			{
+				FromPort:   80,
+				ID:         ruleID,
+				IPProtocol: "TCP",
+				IPRange:    secgroups.IPRange{CIDR: "10.10.10.0/24"},
+				ToPort:     80,
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, count)
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockCreateRuleResponse(t)
+
+	opts := defsecrules.CreateOpts{
+		IPProtocol: "TCP",
+		FromPort:   80,
+		ToPort:     80,
+		CIDR:       "10.10.12.0/24",
+	}
+
+	group, err := defsecrules.Create(client.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &defsecrules.DefaultRule{
+		ID:         ruleID,
+		FromPort:   80,
+		ToPort:     80,
+		IPProtocol: "TCP",
+		IPRange:    secgroups.IPRange{CIDR: "10.10.12.0/24"},
+	}
+	th.AssertDeepEquals(t, expected, group)
+}
+
+func TestCreateICMPZero(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockCreateRuleResponseICMPZero(t)
+
+	opts := defsecrules.CreateOpts{
+		IPProtocol: "ICMP",
+		FromPort:   0,
+		ToPort:     0,
+		CIDR:       "10.10.12.0/24",
+	}
+
+	group, err := defsecrules.Create(client.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &defsecrules.DefaultRule{
+		ID:         ruleID,
+		FromPort:   0,
+		ToPort:     0,
+		IPProtocol: "ICMP",
+		IPRange:    secgroups.IPRange{CIDR: "10.10.12.0/24"},
+	}
+	th.AssertDeepEquals(t, expected, group)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockGetRuleResponse(t, ruleID)
+
+	group, err := defsecrules.Get(client.ServiceClient(), ruleID).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &defsecrules.DefaultRule{
+		ID:         ruleID,
+		FromPort:   80,
+		ToPort:     80,
+		IPProtocol: "TCP",
+		IPRange:    secgroups.IPRange{CIDR: "10.10.12.0/24"},
+	}
+
+	th.AssertDeepEquals(t, expected, group)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockDeleteRuleResponse(t, ruleID)
+
+	err := defsecrules.Delete(client.ServiceClient(), ruleID).ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/compute/v2/extensions/defsecrules/urls.go b/openstack/compute/v2/extensions/defsecrules/urls.go
index cc928ab..e5fbf82 100644
--- a/openstack/compute/v2/extensions/defsecrules/urls.go
+++ b/openstack/compute/v2/extensions/defsecrules/urls.go
@@ -1,6 +1,6 @@
 package defsecrules
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const rulepath = "os-security-group-default-rules"
 
diff --git a/openstack/compute/v2/extensions/delegate.go b/openstack/compute/v2/extensions/delegate.go
index 1007909..00e7c3b 100644
--- a/openstack/compute/v2/extensions/delegate.go
+++ b/openstack/compute/v2/extensions/delegate.go
@@ -1,9 +1,9 @@
 package extensions
 
 import (
-	"github.com/rackspace/gophercloud"
-	common "github.com/rackspace/gophercloud/openstack/common/extensions"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	common "github.com/gophercloud/gophercloud/openstack/common/extensions"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ExtractExtensions interprets a Page as a slice of Extensions.
diff --git a/openstack/compute/v2/extensions/delegate_test.go b/openstack/compute/v2/extensions/delegate_test.go
deleted file mode 100644
index c3c525f..0000000
--- a/openstack/compute/v2/extensions/delegate_test.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package extensions
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	common "github.com/rackspace/gophercloud/openstack/common/extensions"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/extensions", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-
-		fmt.Fprintf(w, `
-{
-		"extensions": [
-				{
-						"updated": "2013-01-20T00:00:00-00:00",
-						"name": "Neutron Service Type Management",
-						"links": [],
-						"namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
-						"alias": "service-type",
-						"description": "API for retrieving service providers for Neutron advanced services"
-				}
-		]
-}
-			`)
-	})
-
-	count := 0
-	List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractExtensions(page)
-		th.AssertNoErr(t, err)
-
-		expected := []common.Extension{
-			common.Extension{
-				Updated:     "2013-01-20T00:00:00-00:00",
-				Name:        "Neutron Service Type Management",
-				Links:       []interface{}{},
-				Namespace:   "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
-				Alias:       "service-type",
-				Description: "API for retrieving service providers for Neutron advanced services",
-			},
-		}
-		th.AssertDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/extensions/agent", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-		"extension": {
-				"updated": "2013-02-03T10:00:00-00:00",
-				"name": "agent",
-				"links": [],
-				"namespace": "http://docs.openstack.org/ext/agent/api/v2.0",
-				"alias": "agent",
-				"description": "The agent management extension."
-		}
-}
-		`)
-	})
-
-	ext, err := Get(client.ServiceClient(), "agent").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, ext.Updated, "2013-02-03T10:00:00-00:00")
-	th.AssertEquals(t, ext.Name, "agent")
-	th.AssertEquals(t, ext.Namespace, "http://docs.openstack.org/ext/agent/api/v2.0")
-	th.AssertEquals(t, ext.Alias, "agent")
-	th.AssertEquals(t, ext.Description, "The agent management extension.")
-}
diff --git a/openstack/compute/v2/extensions/diskconfig/requests.go b/openstack/compute/v2/extensions/diskconfig/requests.go
index 7407e0d..41d04b9 100644
--- a/openstack/compute/v2/extensions/diskconfig/requests.go
+++ b/openstack/compute/v2/extensions/diskconfig/requests.go
@@ -1,9 +1,8 @@
 package diskconfig
 
 import (
-	"errors"
-
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 )
 
 // DiskConfig represents one of the two possible settings for the DiskConfig option when creating,
@@ -23,19 +22,6 @@
 	Manual DiskConfig = "MANUAL"
 )
 
-// ErrInvalidDiskConfig is returned if an invalid string is specified for a DiskConfig option.
-var ErrInvalidDiskConfig = errors.New("DiskConfig must be either diskconfig.Auto or diskconfig.Manual.")
-
-// Validate ensures that a DiskConfig contains an appropriate value.
-func (config DiskConfig) validate() error {
-	switch config {
-	case Auto, Manual:
-		return nil
-	default:
-		return ErrInvalidDiskConfig
-	}
-}
-
 // CreateOptsExt adds a DiskConfig option to the base CreateOpts.
 type CreateOptsExt struct {
 	servers.CreateOptsBuilder
@@ -64,15 +50,16 @@
 // RebuildOptsExt adds a DiskConfig option to the base RebuildOpts.
 type RebuildOptsExt struct {
 	servers.RebuildOptsBuilder
-
-	// DiskConfig [optional] controls how the rebuilt server's disk is partitioned.
-	DiskConfig DiskConfig
+	// DiskConfig controls how the rebuilt server's disk is partitioned.
+	DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"`
 }
 
 // ToServerRebuildMap adds the diskconfig option to the base server rebuild options.
 func (opts RebuildOptsExt) ToServerRebuildMap() (map[string]interface{}, error) {
-	err := opts.DiskConfig.validate()
-	if err != nil {
+	if opts.DiskConfig != Auto && opts.DiskConfig != Manual {
+		err := gophercloud.ErrInvalidInput{}
+		err.Argument = "diskconfig.RebuildOptsExt.DiskConfig"
+		err.Info = "Must be either diskconfig.Auto or diskconfig.Manual"
 		return nil, err
 	}
 
@@ -97,8 +84,10 @@
 
 // ToServerResizeMap adds the diskconfig option to the base server creation options.
 func (opts ResizeOptsExt) ToServerResizeMap() (map[string]interface{}, error) {
-	err := opts.DiskConfig.validate()
-	if err != nil {
+	if opts.DiskConfig != Auto && opts.DiskConfig != Manual {
+		err := gophercloud.ErrInvalidInput{}
+		err.Argument = "diskconfig.ResizeOptsExt.DiskConfig"
+		err.Info = "Must be either diskconfig.Auto or diskconfig.Manual"
 		return nil, err
 	}
 
diff --git a/openstack/compute/v2/extensions/diskconfig/requests_test.go b/openstack/compute/v2/extensions/diskconfig/requests_test.go
deleted file mode 100644
index 17418a3..0000000
--- a/openstack/compute/v2/extensions/diskconfig/requests_test.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package diskconfig
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestCreateOpts(t *testing.T) {
-	base := servers.CreateOpts{
-		Name:      "createdserver",
-		ImageRef:  "asdfasdfasdf",
-		FlavorRef: "performance1-1",
-	}
-
-	ext := CreateOptsExt{
-		CreateOptsBuilder: base,
-		DiskConfig:        Manual,
-	}
-
-	expected := `
-		{
-			"server": {
-				"name": "createdserver",
-				"imageRef": "asdfasdfasdf",
-				"flavorRef": "performance1-1",
-				"flavorName": "",
-				"imageName": "",
-				"OS-DCF:diskConfig": "MANUAL"
-			}
-		}
-	`
-	actual, err := ext.ToServerCreateMap()
-	th.AssertNoErr(t, err)
-	th.CheckJSONEquals(t, expected, actual)
-}
-
-func TestRebuildOpts(t *testing.T) {
-	base := servers.RebuildOpts{
-		Name:      "rebuiltserver",
-		AdminPass: "swordfish",
-		ImageID:   "asdfasdfasdf",
-	}
-
-	ext := RebuildOptsExt{
-		RebuildOptsBuilder: base,
-		DiskConfig:         Auto,
-	}
-
-	actual, err := ext.ToServerRebuildMap()
-	th.AssertNoErr(t, err)
-
-	expected := `
-		{
-			"rebuild": {
-				"name": "rebuiltserver",
-				"imageRef": "asdfasdfasdf",
-				"adminPass": "swordfish",
-				"OS-DCF:diskConfig": "AUTO"
-			}
-		}
-	`
-	th.CheckJSONEquals(t, expected, actual)
-}
-
-func TestResizeOpts(t *testing.T) {
-	base := servers.ResizeOpts{
-		FlavorRef: "performance1-8",
-	}
-
-	ext := ResizeOptsExt{
-		ResizeOptsBuilder: base,
-		DiskConfig:        Auto,
-	}
-
-	actual, err := ext.ToServerResizeMap()
-	th.AssertNoErr(t, err)
-
-	expected := `
-		{
-			"resize": {
-				"flavorRef": "performance1-8",
-				"OS-DCF:diskConfig": "AUTO"
-			}
-		}
-	`
-	th.CheckJSONEquals(t, expected, actual)
-}
diff --git a/openstack/compute/v2/extensions/diskconfig/results.go b/openstack/compute/v2/extensions/diskconfig/results.go
index 10ec2da..3ba66f5 100644
--- a/openstack/compute/v2/extensions/diskconfig/results.go
+++ b/openstack/compute/v2/extensions/diskconfig/results.go
@@ -1,60 +1,17 @@
 package diskconfig
 
-import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	"github.com/rackspace/gophercloud/pagination"
-)
+import "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 
-func commonExtract(result gophercloud.Result) (*DiskConfig, error) {
-	var resp struct {
-		Server struct {
-			DiskConfig string `mapstructure:"OS-DCF:diskConfig"`
-		} `mapstructure:"server"`
-	}
-
-	err := mapstructure.Decode(result.Body, &resp)
-	if err != nil {
-		return nil, err
-	}
-
-	config := DiskConfig(resp.Server.DiskConfig)
-	return &config, nil
+type ServerWithDiskConfig struct {
+	servers.Server
+	DiskConfig DiskConfig `json:"OS-DCF:diskConfig"`
 }
 
-// ExtractGet returns the disk configuration from a servers.Get call.
-func ExtractGet(result servers.GetResult) (*DiskConfig, error) {
-	return commonExtract(result.Result)
+func (s ServerWithDiskConfig) ToServerCreateResult() (m map[string]interface{}) {
+	m["OS-DCF:diskConfig"] = s.DiskConfig
+	return
 }
 
-// ExtractUpdate returns the disk configuration from a servers.Update call.
-func ExtractUpdate(result servers.UpdateResult) (*DiskConfig, error) {
-	return commonExtract(result.Result)
-}
-
-// ExtractRebuild returns the disk configuration from a servers.Rebuild call.
-func ExtractRebuild(result servers.RebuildResult) (*DiskConfig, error) {
-	return commonExtract(result.Result)
-}
-
-// ExtractDiskConfig returns the DiskConfig setting for a specific server acquired from an
-// servers.ExtractServers call, while iterating through a Pager.
-func ExtractDiskConfig(page pagination.Page, index int) (*DiskConfig, error) {
-	casted := page.(servers.ServerPage).Body
-
-	type server struct {
-		DiskConfig string `mapstructure:"OS-DCF:diskConfig"`
-	}
-	var response struct {
-		Servers []server `mapstructure:"servers"`
-	}
-
-	err := mapstructure.Decode(casted, &response)
-	if err != nil {
-		return nil, err
-	}
-
-	config := DiskConfig(response.Servers[index].DiskConfig)
-	return &config, nil
+type CreateServerResultBuilder interface {
+	ToServerCreateResult() map[string]interface{}
 }
diff --git a/openstack/compute/v2/extensions/diskconfig/results_test.go b/openstack/compute/v2/extensions/diskconfig/results_test.go
deleted file mode 100644
index dd8d2b7..0000000
--- a/openstack/compute/v2/extensions/diskconfig/results_test.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package diskconfig
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestExtractGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	servers.HandleServerGetSuccessfully(t)
-
-	config, err := ExtractGet(servers.Get(client.ServiceClient(), "1234asdf"))
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, Manual, *config)
-}
-
-func TestExtractUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	servers.HandleServerUpdateSuccessfully(t)
-
-	r := servers.Update(client.ServiceClient(), "1234asdf", servers.UpdateOpts{
-		Name: "new-name",
-	})
-	config, err := ExtractUpdate(r)
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, Manual, *config)
-}
-
-func TestExtractRebuild(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	servers.HandleRebuildSuccessfully(t, servers.SingleServerBody)
-
-	r := servers.Rebuild(client.ServiceClient(), "1234asdf", servers.RebuildOpts{
-		Name:       "new-name",
-		AdminPass:  "swordfish",
-		ImageID:    "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
-		AccessIPv4: "1.2.3.4",
-	})
-	config, err := ExtractRebuild(r)
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, Manual, *config)
-}
-
-func TestExtractList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	servers.HandleServerListSuccessfully(t)
-
-	pages := 0
-	err := servers.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		config, err := ExtractDiskConfig(page, 0)
-		th.AssertNoErr(t, err)
-		th.CheckEquals(t, Manual, *config)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, pages, 1)
-}
diff --git a/openstack/compute/v2/extensions/diskconfig/testing/doc.go b/openstack/compute/v2/extensions/diskconfig/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/diskconfig/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go b/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go
new file mode 100644
index 0000000..6ce560a
--- /dev/null
+++ b/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go
@@ -0,0 +1,88 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diskconfig"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestCreateOpts(t *testing.T) {
+	base := servers.CreateOpts{
+		Name:      "createdserver",
+		ImageRef:  "asdfasdfasdf",
+		FlavorRef: "performance1-1",
+	}
+
+	ext := diskconfig.CreateOptsExt{
+		CreateOptsBuilder: base,
+		DiskConfig:        diskconfig.Manual,
+	}
+
+	expected := `
+		{
+			"server": {
+				"name": "createdserver",
+				"imageRef": "asdfasdfasdf",
+				"flavorRef": "performance1-1",
+				"OS-DCF:diskConfig": "MANUAL"
+			}
+		}
+	`
+	actual, err := ext.ToServerCreateMap()
+	th.AssertNoErr(t, err)
+	th.CheckJSONEquals(t, expected, actual)
+}
+
+func TestRebuildOpts(t *testing.T) {
+	base := servers.RebuildOpts{
+		Name:      "rebuiltserver",
+		AdminPass: "swordfish",
+		ImageID:   "asdfasdfasdf",
+	}
+
+	ext := diskconfig.RebuildOptsExt{
+		RebuildOptsBuilder: base,
+		DiskConfig:         diskconfig.Auto,
+	}
+
+	actual, err := ext.ToServerRebuildMap()
+	th.AssertNoErr(t, err)
+
+	expected := `
+		{
+			"rebuild": {
+				"name": "rebuiltserver",
+				"imageRef": "asdfasdfasdf",
+				"adminPass": "swordfish",
+				"OS-DCF:diskConfig": "AUTO"
+			}
+		}
+	`
+	th.CheckJSONEquals(t, expected, actual)
+}
+
+func TestResizeOpts(t *testing.T) {
+	base := servers.ResizeOpts{
+		FlavorRef: "performance1-8",
+	}
+
+	ext := diskconfig.ResizeOptsExt{
+		ResizeOptsBuilder: base,
+		DiskConfig:        diskconfig.Auto,
+	}
+
+	actual, err := ext.ToServerResizeMap()
+	th.AssertNoErr(t, err)
+
+	expected := `
+		{
+			"resize": {
+				"flavorRef": "performance1-8",
+				"OS-DCF:diskConfig": "AUTO"
+			}
+		}
+	`
+	th.CheckJSONEquals(t, expected, actual)
+}
diff --git a/openstack/compute/v2/extensions/floatingip/doc.go b/openstack/compute/v2/extensions/floatingip/doc.go
deleted file mode 100644
index f74f58c..0000000
--- a/openstack/compute/v2/extensions/floatingip/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package floatingip provides the ability to manage floating ips through
-// nova-network
-package floatingip
diff --git a/openstack/compute/v2/extensions/floatingip/fixtures.go b/openstack/compute/v2/extensions/floatingip/fixtures.go
deleted file mode 100644
index e47fa4c..0000000
--- a/openstack/compute/v2/extensions/floatingip/fixtures.go
+++ /dev/null
@@ -1,193 +0,0 @@
-// +build fixtures
-
-package floatingip
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ListOutput is a sample response to a List call.
-const ListOutput = `
-{
-    "floating_ips": [
-        {
-            "fixed_ip": null,
-            "id": 1,
-            "instance_id": null,
-            "ip": "10.10.10.1",
-            "pool": "nova"
-        },
-        {
-            "fixed_ip": "166.78.185.201",
-            "id": 2,
-            "instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-            "ip": "10.10.10.2",
-            "pool": "nova"
-        }
-    ]
-}
-`
-
-// GetOutput is a sample response to a Get call.
-const GetOutput = `
-{
-    "floating_ip": {
-        "fixed_ip": "166.78.185.201",
-        "id": 2,
-        "instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-        "ip": "10.10.10.2",
-        "pool": "nova"
-    }
-}
-`
-
-// CreateOutput is a sample response to a Post call
-const CreateOutput = `
-{
-    "floating_ip": {
-        "fixed_ip": null,
-        "id": 1,
-        "instance_id": null,
-        "ip": "10.10.10.1",
-        "pool": "nova"
-    }
-}
-`
-
-// FirstFloatingIP is the first result in ListOutput.
-var FirstFloatingIP = FloatingIP{
-	ID:   "1",
-	IP:   "10.10.10.1",
-	Pool: "nova",
-}
-
-// SecondFloatingIP is the first result in ListOutput.
-var SecondFloatingIP = FloatingIP{
-	FixedIP:    "166.78.185.201",
-	ID:         "2",
-	InstanceID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-	IP:         "10.10.10.2",
-	Pool:       "nova",
-}
-
-// ExpectedFloatingIPsSlice is the slice of results that should be parsed
-// from ListOutput, in the expected order.
-var ExpectedFloatingIPsSlice = []FloatingIP{FirstFloatingIP, SecondFloatingIP}
-
-// CreatedFloatingIP is the parsed result from CreateOutput.
-var CreatedFloatingIP = FloatingIP{
-	ID:   "1",
-	IP:   "10.10.10.1",
-	Pool: "nova",
-}
-
-// HandleListSuccessfully configures the test server to respond to a List request.
-func HandleListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-floating-ips", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, ListOutput)
-	})
-}
-
-// HandleGetSuccessfully configures the test server to respond to a Get request
-// for an existing floating ip
-func HandleGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-floating-ips/2", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, GetOutput)
-	})
-}
-
-// HandleCreateSuccessfully configures the test server to respond to a Create request
-// for a new floating ip
-func HandleCreateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-floating-ips", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `
-{
-	"pool": "nova"
-}
-`)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, CreateOutput)
-	})
-}
-
-// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
-// an existing floating ip
-func HandleDeleteSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-floating-ips/1", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-// HandleAssociateSuccessfully configures the test server to respond to a Post request
-// to associate an allocated floating IP
-func HandleAssociateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `
-{
-	"addFloatingIp": {
-		"address": "10.10.10.2"
-	}
-}
-`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-// HandleFixedAssociateSucessfully configures the test server to respond to a Post request
-// to associate an allocated floating IP with a specific fixed IP address
-func HandleAssociateFixedSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `
-{
-	"addFloatingIp": {
-		"address": "10.10.10.2",
-		"fixed_address": "166.78.185.201"
-	}
-}
-`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-// HandleDisassociateSuccessfully configures the test server to respond to a Post request
-// to disassociate an allocated floating IP
-func HandleDisassociateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `
-{
-	"removeFloatingIp": {
-		"address": "10.10.10.2"
-	}
-}
-`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/openstack/compute/v2/extensions/floatingip/requests.go b/openstack/compute/v2/extensions/floatingip/requests.go
deleted file mode 100644
index 8206462..0000000
--- a/openstack/compute/v2/extensions/floatingip/requests.go
+++ /dev/null
@@ -1,171 +0,0 @@
-package floatingip
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a Pager that allows you to iterate over a collection of FloatingIPs.
-func List(client *gophercloud.ServiceClient) pagination.Pager {
-	return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
-		return FloatingIPsPage{pagination.SinglePageBase(r)}
-	})
-}
-
-// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the
-// CreateOpts struct in this package does.
-type CreateOptsBuilder interface {
-	ToFloatingIPCreateMap() (map[string]interface{}, error)
-}
-
-// CreateOpts specifies a Floating IP allocation request
-type CreateOpts struct {
-	// Pool is the pool of floating IPs to allocate one from
-	Pool string
-}
-
-// AssociateOpts specifies the required information to associate or disassociate a floating IP to an instance
-type AssociateOpts struct {
-	// ServerID is the UUID of the server
-	ServerID string
-
-	// FixedIP is an optional fixed IP address of the server
-	FixedIP string
-
-	// FloatingIP is the floating IP to associate with an instance
-	FloatingIP string
-}
-
-// ToFloatingIPCreateMap constructs a request body from CreateOpts.
-func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
-	if opts.Pool == "" {
-		return nil, errors.New("Missing field required for floating IP creation: Pool")
-	}
-
-	return map[string]interface{}{"pool": opts.Pool}, nil
-}
-
-// ToAssociateMap constructs a request body from AssociateOpts.
-func (opts AssociateOpts) ToAssociateMap() (map[string]interface{}, error) {
-	if opts.ServerID == "" {
-		return nil, errors.New("Required field missing for floating IP association: ServerID")
-	}
-
-	if opts.FloatingIP == "" {
-		return nil, errors.New("Required field missing for floating IP association: FloatingIP")
-	}
-
-	associateInfo := map[string]interface{}{
-		"serverId":   opts.ServerID,
-		"floatingIp": opts.FloatingIP,
-		"fixedIp":    opts.FixedIP,
-	}
-
-	return associateInfo, nil
-
-}
-
-// Create requests the creation of a new floating IP
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToFloatingIPCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-	return res
-}
-
-// Get returns data about a previously created FloatingIP.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
-}
-
-// Delete requests the deletion of a previous allocated FloatingIP.
-func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, id), nil)
-	return res
-}
-
-// association / disassociation
-
-// Associate pairs an allocated floating IP with an instance
-// Deprecated. Use AssociateInstance.
-func Associate(client *gophercloud.ServiceClient, serverId, fip string) AssociateResult {
-	var res AssociateResult
-
-	addFloatingIp := make(map[string]interface{})
-	addFloatingIp["address"] = fip
-	reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp}
-
-	_, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil)
-	return res
-}
-
-// AssociateInstance pairs an allocated floating IP with an instance.
-func AssociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) AssociateResult {
-	var res AssociateResult
-
-	associateInfo, err := opts.ToAssociateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	addFloatingIp := make(map[string]interface{})
-	addFloatingIp["address"] = associateInfo["floatingIp"].(string)
-
-	// fixedIp is not required
-	if associateInfo["fixedIp"] != "" {
-		addFloatingIp["fixed_address"] = associateInfo["fixedIp"].(string)
-	}
-
-	serverId := associateInfo["serverId"].(string)
-
-	reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp}
-	_, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil)
-	return res
-}
-
-// Disassociate decouples an allocated floating IP from an instance
-// Deprecated. Use DisassociateInstance.
-func Disassociate(client *gophercloud.ServiceClient, serverId, fip string) DisassociateResult {
-	var res DisassociateResult
-
-	removeFloatingIp := make(map[string]interface{})
-	removeFloatingIp["address"] = fip
-	reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp}
-
-	_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
-	return res
-}
-
-// DisassociateInstance decouples an allocated floating IP from an instance
-func DisassociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) DisassociateResult {
-	var res DisassociateResult
-
-	associateInfo, err := opts.ToAssociateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	removeFloatingIp := make(map[string]interface{})
-	removeFloatingIp["address"] = associateInfo["floatingIp"].(string)
-	reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp}
-
-	serverId := associateInfo["serverId"].(string)
-
-	_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
-	return res
-}
diff --git a/openstack/compute/v2/extensions/floatingip/requests_test.go b/openstack/compute/v2/extensions/floatingip/requests_test.go
deleted file mode 100644
index 4d86fe2..0000000
--- a/openstack/compute/v2/extensions/floatingip/requests_test.go
+++ /dev/null
@@ -1,123 +0,0 @@
-package floatingip
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListSuccessfully(t)
-
-	count := 0
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractFloatingIPs(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, ExpectedFloatingIPsSlice, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreateSuccessfully(t)
-
-	actual, err := Create(client.ServiceClient(), CreateOpts{
-		Pool: "nova",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &CreatedFloatingIP, actual)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t)
-
-	actual, err := Get(client.ServiceClient(), "2").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &SecondFloatingIP, actual)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDeleteSuccessfully(t)
-
-	err := Delete(client.ServiceClient(), "1").ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestAssociateDeprecated(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleAssociateSuccessfully(t)
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-	fip := "10.10.10.2"
-
-	err := Associate(client.ServiceClient(), serverId, fip).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestAssociate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleAssociateSuccessfully(t)
-
-	associateOpts := AssociateOpts{
-		ServerID:   "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-		FloatingIP: "10.10.10.2",
-	}
-
-	err := AssociateInstance(client.ServiceClient(), associateOpts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestAssociateFixed(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleAssociateFixedSuccessfully(t)
-
-	associateOpts := AssociateOpts{
-		ServerID:   "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-		FloatingIP: "10.10.10.2",
-		FixedIP:    "166.78.185.201",
-	}
-
-	err := AssociateInstance(client.ServiceClient(), associateOpts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDisassociateDeprecated(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDisassociateSuccessfully(t)
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-	fip := "10.10.10.2"
-
-	err := Disassociate(client.ServiceClient(), serverId, fip).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDisassociateInstance(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDisassociateSuccessfully(t)
-
-	associateOpts := AssociateOpts{
-		ServerID:   "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-		FloatingIP: "10.10.10.2",
-	}
-
-	err := DisassociateInstance(client.ServiceClient(), associateOpts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/compute/v2/extensions/floatingip/results.go b/openstack/compute/v2/extensions/floatingip/results.go
deleted file mode 100644
index be77fa1..0000000
--- a/openstack/compute/v2/extensions/floatingip/results.go
+++ /dev/null
@@ -1,99 +0,0 @@
-package floatingip
-
-import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// A FloatingIP is an IP that can be associated with an instance
-type FloatingIP struct {
-	// ID is a unique ID of the Floating IP
-	ID string `mapstructure:"id"`
-
-	// FixedIP is the IP of the instance related to the Floating IP
-	FixedIP string `mapstructure:"fixed_ip,omitempty"`
-
-	// InstanceID is the ID of the instance that is using the Floating IP
-	InstanceID string `mapstructure:"instance_id"`
-
-	// IP is the actual Floating IP
-	IP string `mapstructure:"ip"`
-
-	// Pool is the pool of floating IPs that this floating IP belongs to
-	Pool string `mapstructure:"pool"`
-}
-
-// FloatingIPsPage stores a single, only page of FloatingIPs
-// results from a List call.
-type FloatingIPsPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty determines whether or not a FloatingIPsPage is empty.
-func (page FloatingIPsPage) IsEmpty() (bool, error) {
-	va, err := ExtractFloatingIPs(page)
-	return len(va) == 0, err
-}
-
-// ExtractFloatingIPs interprets a page of results as a slice of
-// FloatingIPs.
-func ExtractFloatingIPs(page pagination.Page) ([]FloatingIP, error) {
-	casted := page.(FloatingIPsPage).Body
-	var response struct {
-		FloatingIPs []FloatingIP `mapstructure:"floating_ips"`
-	}
-
-	err := mapstructure.WeakDecode(casted, &response)
-
-	return response.FloatingIPs, err
-}
-
-type FloatingIPResult struct {
-	gophercloud.Result
-}
-
-// Extract is a method that attempts to interpret any FloatingIP resource
-// response as a FloatingIP struct.
-func (r FloatingIPResult) Extract() (*FloatingIP, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
-		FloatingIP *FloatingIP `json:"floating_ip" mapstructure:"floating_ip"`
-	}
-
-	err := mapstructure.WeakDecode(r.Body, &res)
-	return res.FloatingIP, err
-}
-
-// CreateResult is the response from a Create operation. Call its Extract method to interpret it
-// as a FloatingIP.
-type CreateResult struct {
-	FloatingIPResult
-}
-
-// GetResult is the response from a Get operation. Call its Extract method to interpret it
-// as a FloatingIP.
-type GetResult struct {
-	FloatingIPResult
-}
-
-// DeleteResult is the response from a Delete operation. Call its Extract method to determine if
-// the call succeeded or failed.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-// AssociateResult is the response from a Delete operation. Call its Extract method to determine if
-// the call succeeded or failed.
-type AssociateResult struct {
-	gophercloud.ErrResult
-}
-
-// DisassociateResult is the response from a Delete operation. Call its Extract method to determine if
-// the call succeeded or failed.
-type DisassociateResult struct {
-	gophercloud.ErrResult
-}
diff --git a/openstack/compute/v2/extensions/floatingip/urls.go b/openstack/compute/v2/extensions/floatingip/urls.go
deleted file mode 100644
index 54198f8..0000000
--- a/openstack/compute/v2/extensions/floatingip/urls.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package floatingip
-
-import "github.com/rackspace/gophercloud"
-
-const resourcePath = "os-floating-ips"
-
-func resourceURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL(resourcePath)
-}
-
-func listURL(c *gophercloud.ServiceClient) string {
-	return resourceURL(c)
-}
-
-func createURL(c *gophercloud.ServiceClient) string {
-	return resourceURL(c)
-}
-
-func getURL(c *gophercloud.ServiceClient, id string) string {
-	return c.ServiceURL(resourcePath, id)
-}
-
-func deleteURL(c *gophercloud.ServiceClient, id string) string {
-	return getURL(c, id)
-}
-
-func serverURL(c *gophercloud.ServiceClient, serverId string) string {
-	return c.ServiceURL("servers/" + serverId + "/action")
-}
-
-func associateURL(c *gophercloud.ServiceClient, serverId string) string {
-	return serverURL(c, serverId)
-}
-
-func disassociateURL(c *gophercloud.ServiceClient, serverId string) string {
-	return serverURL(c, serverId)
-}
diff --git a/openstack/compute/v2/extensions/floatingip/urls_test.go b/openstack/compute/v2/extensions/floatingip/urls_test.go
deleted file mode 100644
index f73d6fb..0000000
--- a/openstack/compute/v2/extensions/floatingip/urls_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package floatingip
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-floating-ips", listURL(c))
-}
-
-func TestCreateURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-floating-ips", createURL(c))
-}
-
-func TestGetURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	id := "1"
-
-	th.CheckEquals(t, c.Endpoint+"os-floating-ips/"+id, getURL(c, id))
-}
-
-func TestDeleteURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	id := "1"
-
-	th.CheckEquals(t, c.Endpoint+"os-floating-ips/"+id, deleteURL(c, id))
-}
-
-func TestAssociateURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/action", associateURL(c, serverId))
-}
-
-func TestDisassociateURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/action", disassociateURL(c, serverId))
-}
diff --git a/openstack/compute/v2/extensions/floatingips/doc.go b/openstack/compute/v2/extensions/floatingips/doc.go
new file mode 100644
index 0000000..6682fa6
--- /dev/null
+++ b/openstack/compute/v2/extensions/floatingips/doc.go
@@ -0,0 +1,3 @@
+// Package floatingips provides the ability to manage floating ips through
+// nova-network
+package floatingips
diff --git a/openstack/compute/v2/extensions/floatingips/requests.go b/openstack/compute/v2/extensions/floatingips/requests.go
new file mode 100644
index 0000000..b36aeba
--- /dev/null
+++ b/openstack/compute/v2/extensions/floatingips/requests.go
@@ -0,0 +1,112 @@
+package floatingips
+
+import (
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
+)
+
+// List returns a Pager that allows you to iterate over a collection of FloatingIPs.
+func List(client *gophercloud.ServiceClient) pagination.Pager {
+	return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
+		return FloatingIPPage{pagination.SinglePageBase(r)}
+	})
+}
+
+// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the
+// CreateOpts struct in this package does.
+type CreateOptsBuilder interface {
+	ToFloatingIPCreateMap() (map[string]interface{}, error)
+}
+
+// CreateOpts specifies a Floating IP allocation request
+type CreateOpts struct {
+	// Pool is the pool of floating IPs to allocate one from
+	Pool string `json:"pool" required:"true"`
+}
+
+// ToFloatingIPCreateMap constructs a request body from CreateOpts.
+func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "")
+}
+
+// Create requests the creation of a new floating IP
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToFloatingIPCreateMap()
+	if err != nil {
+		r.Err = err
+		return
+	}
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
+		OkCodes: []int{200},
+	})
+	return
+}
+
+// Get returns data about a previously created FloatingIP.
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return
+}
+
+// Delete requests the deletion of a previous allocated FloatingIP.
+func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = client.Delete(deleteURL(client, id), nil)
+	return
+}
+
+// AssociateOptsBuilder is the interface types must satfisfy to be used as
+// Associate options
+type AssociateOptsBuilder interface {
+	ToFloatingIPAssociateMap() (map[string]interface{}, error)
+}
+
+// AssociateOpts specifies the required information to associate a floating IP with an instance
+type AssociateOpts struct {
+	// FloatingIP is the floating IP to associate with an instance
+	FloatingIP string `json:"address" required:"true"`
+	// FixedIP is an optional fixed IP address of the server
+	FixedIP string `json:"fixed_address,omitempty"`
+}
+
+// ToFloatingIPAssociateMap constructs a request body from AssociateOpts.
+func (opts AssociateOpts) ToFloatingIPAssociateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "addFloatingIp")
+}
+
+// AssociateInstance pairs an allocated floating IP with an instance.
+func AssociateInstance(client *gophercloud.ServiceClient, serverID string, opts AssociateOptsBuilder) (r AssociateResult) {
+	b, err := opts.ToFloatingIPAssociateMap()
+	if err != nil {
+		r.Err = err
+		return
+	}
+	_, r.Err = client.Post(associateURL(client, serverID), b, nil, nil)
+	return
+}
+
+// DisassociateOptsBuilder is the interface types must satfisfy to be used as
+// Disassociate options
+type DisassociateOptsBuilder interface {
+	ToFloatingIPDisassociateMap() (map[string]interface{}, error)
+}
+
+// DisassociateOpts specifies the required information to disassociate a floating IP with an instance
+type DisassociateOpts struct {
+	FloatingIP string `json:"address" required:"true"`
+}
+
+// ToFloatingIPDisassociateMap constructs a request body from AssociateOpts.
+func (opts DisassociateOpts) ToFloatingIPDisassociateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "removeFloatingIp")
+}
+
+// DisassociateInstance decouples an allocated floating IP from an instance
+func DisassociateInstance(client *gophercloud.ServiceClient, serverID string, opts DisassociateOptsBuilder) (r DisassociateResult) {
+	b, err := opts.ToFloatingIPDisassociateMap()
+	if err != nil {
+		r.Err = err
+		return
+	}
+	_, r.Err = client.Post(disassociateURL(client, serverID), b, nil, nil)
+	return
+}
diff --git a/openstack/compute/v2/extensions/floatingips/results.go b/openstack/compute/v2/extensions/floatingips/results.go
new file mode 100644
index 0000000..753f3af
--- /dev/null
+++ b/openstack/compute/v2/extensions/floatingips/results.go
@@ -0,0 +1,91 @@
+package floatingips
+
+import (
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
+)
+
+// A FloatingIP is an IP that can be associated with an instance
+type FloatingIP struct {
+	// ID is a unique ID of the Floating IP
+	ID string `json:"id"`
+
+	// FixedIP is the IP of the instance related to the Floating IP
+	FixedIP string `json:"fixed_ip,omitempty"`
+
+	// InstanceID is the ID of the instance that is using the Floating IP
+	InstanceID string `json:"instance_id"`
+
+	// IP is the actual Floating IP
+	IP string `json:"ip"`
+
+	// Pool is the pool of floating IPs that this floating IP belongs to
+	Pool string `json:"pool"`
+}
+
+// FloatingIPPage stores a single, only page of FloatingIPs
+// results from a List call.
+type FloatingIPPage struct {
+	pagination.SinglePageBase
+}
+
+// IsEmpty determines whether or not a FloatingIPsPage is empty.
+func (page FloatingIPPage) IsEmpty() (bool, error) {
+	va, err := ExtractFloatingIPs(page)
+	return len(va) == 0, err
+}
+
+// ExtractFloatingIPs interprets a page of results as a slice of
+// FloatingIPs.
+func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) {
+	var s struct {
+		FloatingIPs []FloatingIP `json:"floating_ips"`
+	}
+	err := (r.(FloatingIPPage)).ExtractInto(&s)
+	return s.FloatingIPs, err
+}
+
+// FloatingIPResult is the raw result from a FloatingIP request.
+type FloatingIPResult struct {
+	gophercloud.Result
+}
+
+// Extract is a method that attempts to interpret any FloatingIP resource
+// response as a FloatingIP struct.
+func (r FloatingIPResult) Extract() (*FloatingIP, error) {
+	var s struct {
+		FloatingIP *FloatingIP `json:"floating_ip"`
+	}
+	err := r.ExtractInto(&s)
+	return s.FloatingIP, err
+}
+
+// CreateResult is the response from a Create operation. Call its Extract method to interpret it
+// as a FloatingIP.
+type CreateResult struct {
+	FloatingIPResult
+}
+
+// GetResult is the response from a Get operation. Call its Extract method to interpret it
+// as a FloatingIP.
+type GetResult struct {
+	FloatingIPResult
+}
+
+// DeleteResult is the response from a Delete operation. Call its Extract method to determine if
+// the call succeeded or failed.
+type DeleteResult struct {
+	gophercloud.ErrResult
+}
+
+// AssociateResult is the response from a Delete operation. Call its Extract method to determine if
+// the call succeeded or failed.
+type AssociateResult struct {
+	gophercloud.ErrResult
+}
+
+// DisassociateResult is the response from a Delete operation. Call its Extract method to determine if
+// the call succeeded or failed.
+type DisassociateResult struct {
+	gophercloud.ErrResult
+}
diff --git a/openstack/compute/v2/extensions/floatingips/testing/doc.go b/openstack/compute/v2/extensions/floatingips/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/floatingips/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/floatingips/testing/fixtures.go b/openstack/compute/v2/extensions/floatingips/testing/fixtures.go
new file mode 100644
index 0000000..e372606
--- /dev/null
+++ b/openstack/compute/v2/extensions/floatingips/testing/fixtures.go
@@ -0,0 +1,192 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ListOutput is a sample response to a List call.
+const ListOutput = `
+{
+    "floating_ips": [
+        {
+            "fixed_ip": null,
+            "id": "1",
+            "instance_id": null,
+            "ip": "10.10.10.1",
+            "pool": "nova"
+        },
+        {
+            "fixed_ip": "166.78.185.201",
+            "id": "2",
+            "instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
+            "ip": "10.10.10.2",
+            "pool": "nova"
+        }
+    ]
+}
+`
+
+// GetOutput is a sample response to a Get call.
+const GetOutput = `
+{
+    "floating_ip": {
+        "fixed_ip": "166.78.185.201",
+        "id": "2",
+        "instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
+        "ip": "10.10.10.2",
+        "pool": "nova"
+    }
+}
+`
+
+// CreateOutput is a sample response to a Post call
+const CreateOutput = `
+{
+    "floating_ip": {
+        "fixed_ip": null,
+        "id": "1",
+        "instance_id": null,
+        "ip": "10.10.10.1",
+        "pool": "nova"
+    }
+}
+`
+
+// FirstFloatingIP is the first result in ListOutput.
+var FirstFloatingIP = floatingips.FloatingIP{
+	ID:   "1",
+	IP:   "10.10.10.1",
+	Pool: "nova",
+}
+
+// SecondFloatingIP is the first result in ListOutput.
+var SecondFloatingIP = floatingips.FloatingIP{
+	FixedIP:    "166.78.185.201",
+	ID:         "2",
+	InstanceID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
+	IP:         "10.10.10.2",
+	Pool:       "nova",
+}
+
+// ExpectedFloatingIPsSlice is the slice of results that should be parsed
+// from ListOutput, in the expected order.
+var ExpectedFloatingIPsSlice = []floatingips.FloatingIP{FirstFloatingIP, SecondFloatingIP}
+
+// CreatedFloatingIP is the parsed result from CreateOutput.
+var CreatedFloatingIP = floatingips.FloatingIP{
+	ID:   "1",
+	IP:   "10.10.10.1",
+	Pool: "nova",
+}
+
+// HandleListSuccessfully configures the test server to respond to a List request.
+func HandleListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-floating-ips", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, ListOutput)
+	})
+}
+
+// HandleGetSuccessfully configures the test server to respond to a Get request
+// for an existing floating ip
+func HandleGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-floating-ips/2", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, GetOutput)
+	})
+}
+
+// HandleCreateSuccessfully configures the test server to respond to a Create request
+// for a new floating ip
+func HandleCreateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-floating-ips", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `
+{
+	"pool": "nova"
+}
+`)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, CreateOutput)
+	})
+}
+
+// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
+// an existing floating ip
+func HandleDeleteSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-floating-ips/1", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+// HandleAssociateSuccessfully configures the test server to respond to a Post request
+// to associate an allocated floating IP
+func HandleAssociateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `
+{
+	"addFloatingIp": {
+		"address": "10.10.10.2"
+	}
+}
+`)
+
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+// HandleFixedAssociateSucessfully configures the test server to respond to a Post request
+// to associate an allocated floating IP with a specific fixed IP address
+func HandleAssociateFixedSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `
+{
+	"addFloatingIp": {
+		"address": "10.10.10.2",
+		"fixed_address": "166.78.185.201"
+	}
+}
+`)
+
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+// HandleDisassociateSuccessfully configures the test server to respond to a Post request
+// to disassociate an allocated floating IP
+func HandleDisassociateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `
+{
+	"removeFloatingIp": {
+		"address": "10.10.10.2"
+	}
+}
+`)
+
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
diff --git a/openstack/compute/v2/extensions/floatingips/testing/requests_test.go b/openstack/compute/v2/extensions/floatingips/testing/requests_test.go
new file mode 100644
index 0000000..e53da91
--- /dev/null
+++ b/openstack/compute/v2/extensions/floatingips/testing/requests_test.go
@@ -0,0 +1,99 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListSuccessfully(t)
+
+	count := 0
+	err := floatingips.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := floatingips.ExtractFloatingIPs(page)
+		th.AssertNoErr(t, err)
+		th.CheckDeepEquals(t, ExpectedFloatingIPsSlice, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, 1, count)
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleCreateSuccessfully(t)
+
+	actual, err := floatingips.Create(client.ServiceClient(), floatingips.CreateOpts{
+		Pool: "nova",
+	}).Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &CreatedFloatingIP, actual)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t)
+
+	actual, err := floatingips.Get(client.ServiceClient(), "2").Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &SecondFloatingIP, actual)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDeleteSuccessfully(t)
+
+	err := floatingips.Delete(client.ServiceClient(), "1").ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestAssociate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleAssociateSuccessfully(t)
+
+	associateOpts := floatingips.AssociateOpts{
+		FloatingIP: "10.10.10.2",
+	}
+
+	err := floatingips.AssociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestAssociateFixed(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleAssociateFixedSuccessfully(t)
+
+	associateOpts := floatingips.AssociateOpts{
+		FloatingIP: "10.10.10.2",
+		FixedIP:    "166.78.185.201",
+	}
+
+	err := floatingips.AssociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestDisassociateInstance(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDisassociateSuccessfully(t)
+
+	disassociateOpts := floatingips.DisassociateOpts{
+		FloatingIP: "10.10.10.2",
+	}
+
+	err := floatingips.DisassociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", disassociateOpts).ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/compute/v2/extensions/floatingips/urls.go b/openstack/compute/v2/extensions/floatingips/urls.go
new file mode 100644
index 0000000..4768e5a
--- /dev/null
+++ b/openstack/compute/v2/extensions/floatingips/urls.go
@@ -0,0 +1,37 @@
+package floatingips
+
+import "github.com/gophercloud/gophercloud"
+
+const resourcePath = "os-floating-ips"
+
+func resourceURL(c *gophercloud.ServiceClient) string {
+	return c.ServiceURL(resourcePath)
+}
+
+func listURL(c *gophercloud.ServiceClient) string {
+	return resourceURL(c)
+}
+
+func createURL(c *gophercloud.ServiceClient) string {
+	return resourceURL(c)
+}
+
+func getURL(c *gophercloud.ServiceClient, id string) string {
+	return c.ServiceURL(resourcePath, id)
+}
+
+func deleteURL(c *gophercloud.ServiceClient, id string) string {
+	return getURL(c, id)
+}
+
+func serverURL(c *gophercloud.ServiceClient, serverID string) string {
+	return c.ServiceURL("servers/" + serverID + "/action")
+}
+
+func associateURL(c *gophercloud.ServiceClient, serverID string) string {
+	return serverURL(c, serverID)
+}
+
+func disassociateURL(c *gophercloud.ServiceClient, serverID string) string {
+	return serverURL(c, serverID)
+}
diff --git a/openstack/compute/v2/extensions/keypairs/fixtures.go b/openstack/compute/v2/extensions/keypairs/fixtures.go
deleted file mode 100644
index d10af99..0000000
--- a/openstack/compute/v2/extensions/keypairs/fixtures.go
+++ /dev/null
@@ -1,171 +0,0 @@
-// +build fixtures
-
-package keypairs
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ListOutput is a sample response to a List call.
-const ListOutput = `
-{
-	"keypairs": [
-		{
-			"keypair": {
-				"fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a",
-				"name": "firstkey",
-				"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n"
-			}
-		},
-		{
-			"keypair": {
-				"fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
-				"name": "secondkey",
-				"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n"
-			}
-		}
-	]
-}
-`
-
-// GetOutput is a sample response to a Get call.
-const GetOutput = `
-{
-	"keypair": {
-		"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n",
-		"name": "firstkey",
-		"fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a"
-	}
-}
-`
-
-// CreateOutput is a sample response to a Create call.
-const CreateOutput = `
-{
-	"keypair": {
-		"fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
-		"name": "createdkey",
-		"private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n",
-		"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
-		"user_id": "fake"
-	}
-}
-`
-
-// ImportOutput is a sample response to a Create call that provides its own public key.
-const ImportOutput = `
-{
-	"keypair": {
-		"fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c",
-		"name": "importedkey",
-		"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova",
-		"user_id": "fake"
-	}
-}
-`
-
-// FirstKeyPair is the first result in ListOutput.
-var FirstKeyPair = KeyPair{
-	Name:        "firstkey",
-	Fingerprint: "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a",
-	PublicKey:   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n",
-}
-
-// SecondKeyPair is the second result in ListOutput.
-var SecondKeyPair = KeyPair{
-	Name:        "secondkey",
-	Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
-	PublicKey:   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
-}
-
-// ExpectedKeyPairSlice is the slice of results that should be parsed from ListOutput, in the expected
-// order.
-var ExpectedKeyPairSlice = []KeyPair{FirstKeyPair, SecondKeyPair}
-
-// CreatedKeyPair is the parsed result from CreatedOutput.
-var CreatedKeyPair = KeyPair{
-	Name:        "createdkey",
-	Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
-	PublicKey:   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
-	PrivateKey:  "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n",
-	UserID:      "fake",
-}
-
-// ImportedKeyPair is the parsed result from ImportOutput.
-var ImportedKeyPair = KeyPair{
-	Name:        "importedkey",
-	Fingerprint: "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c",
-	PublicKey:   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova",
-	UserID:      "fake",
-}
-
-// HandleListSuccessfully configures the test server to respond to a List request.
-func HandleListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, ListOutput)
-	})
-}
-
-// HandleGetSuccessfully configures the test server to respond to a Get request for "firstkey".
-func HandleGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-keypairs/firstkey", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, GetOutput)
-	})
-}
-
-// HandleCreateSuccessfully configures the test server to respond to a Create request for a new
-// keypair called "createdkey".
-func HandleCreateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{ "keypair": { "name": "createdkey" } }`)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, CreateOutput)
-	})
-}
-
-// HandleImportSuccessfully configures the test server to respond to an Import request for an
-// existing keypair called "importedkey".
-func HandleImportSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `
-			{
-				"keypair": {
-					"name": "importedkey",
-					"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova"
-				}
-			}
-		`)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, ImportOutput)
-	})
-}
-
-// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
-// keypair called "deletedkey".
-func HandleDeleteSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-keypairs/deletedkey", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/extensions/keypairs/requests.go
index c56ee67..adf1e55 100644
--- a/openstack/compute/v2/extensions/keypairs/requests.go
+++ b/openstack/compute/v2/extensions/keypairs/requests.go
@@ -1,11 +1,9 @@
 package keypairs
 
 import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // CreateOptsExt adds a KeyPair option to the base CreateOpts.
@@ -47,56 +45,40 @@
 
 // CreateOpts specifies keypair creation or import parameters.
 type CreateOpts struct {
-	// Name [required] is a friendly name to refer to this KeyPair in other services.
-	Name string
-
+	// Name is a friendly name to refer to this KeyPair in other services.
+	Name string `json:"name" required:"true"`
 	// PublicKey [optional] is a pregenerated OpenSSH-formatted public key. If provided, this key
 	// will be imported and no new key will be created.
-	PublicKey string
+	PublicKey string `json:"public_key,omitempty"`
 }
 
 // ToKeyPairCreateMap constructs a request body from CreateOpts.
 func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
-	if opts.Name == "" {
-		return nil, errors.New("Missing field required for keypair creation: Name")
-	}
-
-	keypair := make(map[string]interface{})
-	keypair["name"] = opts.Name
-	if opts.PublicKey != "" {
-		keypair["public_key"] = opts.PublicKey
-	}
-
-	return map[string]interface{}{"keypair": keypair}, nil
+	return gophercloud.BuildRequestBody(opts, "keypair")
 }
 
 // Create requests the creation of a new keypair on the server, or to import a pre-existing
 // keypair.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToKeyPairCreateMap()
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToKeyPairCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // Get returns public data about a previously uploaded KeyPair.
-func Get(client *gophercloud.ServiceClient, name string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, name), &res.Body, nil)
-	return res
+func Get(client *gophercloud.ServiceClient, name string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, name), &r.Body, nil)
+	return
 }
 
 // Delete requests the deletion of a previous stored KeyPair from the server.
-func Delete(client *gophercloud.ServiceClient, name string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, name), nil)
-	return res
+func Delete(client *gophercloud.ServiceClient, name string) (r DeleteResult) {
+	_, r.Err = client.Delete(deleteURL(client, name), nil)
+	return
 }
diff --git a/openstack/compute/v2/extensions/keypairs/requests_test.go b/openstack/compute/v2/extensions/keypairs/requests_test.go
deleted file mode 100644
index 67d1833..0000000
--- a/openstack/compute/v2/extensions/keypairs/requests_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package keypairs
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListSuccessfully(t)
-
-	count := 0
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractKeyPairs(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, ExpectedKeyPairSlice, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreateSuccessfully(t)
-
-	actual, err := Create(client.ServiceClient(), CreateOpts{
-		Name: "createdkey",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &CreatedKeyPair, actual)
-}
-
-func TestImport(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleImportSuccessfully(t)
-
-	actual, err := Create(client.ServiceClient(), CreateOpts{
-		Name:      "importedkey",
-		PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &ImportedKeyPair, actual)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t)
-
-	actual, err := Get(client.ServiceClient(), "firstkey").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &FirstKeyPair, actual)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDeleteSuccessfully(t)
-
-	err := Delete(client.ServiceClient(), "deletedkey").ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/compute/v2/extensions/keypairs/results.go b/openstack/compute/v2/extensions/keypairs/results.go
index f1a0d8e..f4d8d35 100644
--- a/openstack/compute/v2/extensions/keypairs/results.go
+++ b/openstack/compute/v2/extensions/keypairs/results.go
@@ -1,31 +1,30 @@
 package keypairs
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // KeyPair is an SSH key known to the OpenStack cluster that is available to be injected into
 // servers.
 type KeyPair struct {
 	// Name is used to refer to this keypair from other services within this region.
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 
 	// Fingerprint is a short sequence of bytes that can be used to authenticate or validate a longer
 	// public key.
-	Fingerprint string `mapstructure:"fingerprint"`
+	Fingerprint string `json:"fingerprint"`
 
 	// PublicKey is the public key from this pair, in OpenSSH format. "ssh-rsa AAAAB3Nz..."
-	PublicKey string `mapstructure:"public_key"`
+	PublicKey string `json:"public_key"`
 
 	// PrivateKey is the private key from this pair, in PEM format.
 	// "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..." It is only present if this keypair was just
 	// returned from a Create call
-	PrivateKey string `mapstructure:"private_key"`
+	PrivateKey string `json:"private_key"`
 
 	// UserID is the user who owns this keypair.
-	UserID string `mapstructure:"user_id"`
+	UserID string `json:"user_id"`
 }
 
 // KeyPairPage stores a single, only page of KeyPair results from a List call.
@@ -40,18 +39,16 @@
 }
 
 // ExtractKeyPairs interprets a page of results as a slice of KeyPairs.
-func ExtractKeyPairs(page pagination.Page) ([]KeyPair, error) {
+func ExtractKeyPairs(r pagination.Page) ([]KeyPair, error) {
 	type pair struct {
-		KeyPair KeyPair `mapstructure:"keypair"`
+		KeyPair KeyPair `json:"keypair"`
 	}
-
-	var resp struct {
-		KeyPairs []pair `mapstructure:"keypairs"`
+	var s struct {
+		KeyPairs []pair `json:"keypairs"`
 	}
-
-	err := mapstructure.Decode(page.(KeyPairPage).Body, &resp)
-	results := make([]KeyPair, len(resp.KeyPairs))
-	for i, pair := range resp.KeyPairs {
+	err := (r.(KeyPairPage)).ExtractInto(&s)
+	results := make([]KeyPair, len(s.KeyPairs))
+	for i, pair := range s.KeyPairs {
 		results[i] = pair.KeyPair
 	}
 	return results, err
@@ -63,16 +60,11 @@
 
 // Extract is a method that attempts to interpret any KeyPair resource response as a KeyPair struct.
 func (r keyPairResult) Extract() (*KeyPair, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		KeyPair *KeyPair `json:"keypair"`
 	}
-
-	var res struct {
-		KeyPair *KeyPair `json:"keypair" mapstructure:"keypair"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-	return res.KeyPair, err
+	err := r.ExtractInto(&s)
+	return s.KeyPair, err
 }
 
 // CreateResult is the response from a Create operation. Call its Extract method to interpret it
diff --git a/openstack/compute/v2/extensions/keypairs/testing/doc.go b/openstack/compute/v2/extensions/keypairs/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/keypairs/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/keypairs/testing/fixtures.go b/openstack/compute/v2/extensions/keypairs/testing/fixtures.go
new file mode 100644
index 0000000..dc716d8
--- /dev/null
+++ b/openstack/compute/v2/extensions/keypairs/testing/fixtures.go
@@ -0,0 +1,170 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ListOutput is a sample response to a List call.
+const ListOutput = `
+{
+	"keypairs": [
+		{
+			"keypair": {
+				"fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a",
+				"name": "firstkey",
+				"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n"
+			}
+		},
+		{
+			"keypair": {
+				"fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
+				"name": "secondkey",
+				"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n"
+			}
+		}
+	]
+}
+`
+
+// GetOutput is a sample response to a Get call.
+const GetOutput = `
+{
+	"keypair": {
+		"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n",
+		"name": "firstkey",
+		"fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a"
+	}
+}
+`
+
+// CreateOutput is a sample response to a Create call.
+const CreateOutput = `
+{
+	"keypair": {
+		"fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
+		"name": "createdkey",
+		"private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n",
+		"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
+		"user_id": "fake"
+	}
+}
+`
+
+// ImportOutput is a sample response to a Create call that provides its own public key.
+const ImportOutput = `
+{
+	"keypair": {
+		"fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c",
+		"name": "importedkey",
+		"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova",
+		"user_id": "fake"
+	}
+}
+`
+
+// FirstKeyPair is the first result in ListOutput.
+var FirstKeyPair = keypairs.KeyPair{
+	Name:        "firstkey",
+	Fingerprint: "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a",
+	PublicKey:   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n",
+}
+
+// SecondKeyPair is the second result in ListOutput.
+var SecondKeyPair = keypairs.KeyPair{
+	Name:        "secondkey",
+	Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
+	PublicKey:   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
+}
+
+// ExpectedKeyPairSlice is the slice of results that should be parsed from ListOutput, in the expected
+// order.
+var ExpectedKeyPairSlice = []keypairs.KeyPair{FirstKeyPair, SecondKeyPair}
+
+// CreatedKeyPair is the parsed result from CreatedOutput.
+var CreatedKeyPair = keypairs.KeyPair{
+	Name:        "createdkey",
+	Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
+	PublicKey:   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
+	PrivateKey:  "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n",
+	UserID:      "fake",
+}
+
+// ImportedKeyPair is the parsed result from ImportOutput.
+var ImportedKeyPair = keypairs.KeyPair{
+	Name:        "importedkey",
+	Fingerprint: "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c",
+	PublicKey:   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova",
+	UserID:      "fake",
+}
+
+// HandleListSuccessfully configures the test server to respond to a List request.
+func HandleListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, ListOutput)
+	})
+}
+
+// HandleGetSuccessfully configures the test server to respond to a Get request for "firstkey".
+func HandleGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-keypairs/firstkey", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, GetOutput)
+	})
+}
+
+// HandleCreateSuccessfully configures the test server to respond to a Create request for a new
+// keypair called "createdkey".
+func HandleCreateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "keypair": { "name": "createdkey" } }`)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, CreateOutput)
+	})
+}
+
+// HandleImportSuccessfully configures the test server to respond to an Import request for an
+// existing keypair called "importedkey".
+func HandleImportSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `
+			{
+				"keypair": {
+					"name": "importedkey",
+					"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova"
+				}
+			}
+		`)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, ImportOutput)
+	})
+}
+
+// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
+// keypair called "deletedkey".
+func HandleDeleteSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-keypairs/deletedkey", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
diff --git a/openstack/compute/v2/extensions/keypairs/testing/requests_test.go b/openstack/compute/v2/extensions/keypairs/testing/requests_test.go
new file mode 100644
index 0000000..1e05e66
--- /dev/null
+++ b/openstack/compute/v2/extensions/keypairs/testing/requests_test.go
@@ -0,0 +1,72 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListSuccessfully(t)
+
+	count := 0
+	err := keypairs.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := keypairs.ExtractKeyPairs(page)
+		th.AssertNoErr(t, err)
+		th.CheckDeepEquals(t, ExpectedKeyPairSlice, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, 1, count)
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleCreateSuccessfully(t)
+
+	actual, err := keypairs.Create(client.ServiceClient(), keypairs.CreateOpts{
+		Name: "createdkey",
+	}).Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &CreatedKeyPair, actual)
+}
+
+func TestImport(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleImportSuccessfully(t)
+
+	actual, err := keypairs.Create(client.ServiceClient(), keypairs.CreateOpts{
+		Name:      "importedkey",
+		PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova",
+	}).Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &ImportedKeyPair, actual)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t)
+
+	actual, err := keypairs.Get(client.ServiceClient(), "firstkey").Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &FirstKeyPair, actual)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDeleteSuccessfully(t)
+
+	err := keypairs.Delete(client.ServiceClient(), "deletedkey").ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/compute/v2/extensions/keypairs/urls.go b/openstack/compute/v2/extensions/keypairs/urls.go
index 702f532..fec38f3 100644
--- a/openstack/compute/v2/extensions/keypairs/urls.go
+++ b/openstack/compute/v2/extensions/keypairs/urls.go
@@ -1,6 +1,6 @@
 package keypairs
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const resourcePath = "os-keypairs"
 
diff --git a/openstack/compute/v2/extensions/keypairs/urls_test.go b/openstack/compute/v2/extensions/keypairs/urls_test.go
deleted file mode 100644
index 60efd2a..0000000
--- a/openstack/compute/v2/extensions/keypairs/urls_test.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package keypairs
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-keypairs", listURL(c))
-}
-
-func TestCreateURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-keypairs", createURL(c))
-}
-
-func TestGetURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-keypairs/wat", getURL(c, "wat"))
-}
-
-func TestDeleteURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-keypairs/wat", deleteURL(c, "wat"))
-}
diff --git a/openstack/compute/v2/extensions/networks/fixtures.go b/openstack/compute/v2/extensions/networks/fixtures.go
deleted file mode 100644
index 12b9485..0000000
--- a/openstack/compute/v2/extensions/networks/fixtures.go
+++ /dev/null
@@ -1,209 +0,0 @@
-// +build fixtures
-
-package networks
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ListOutput is a sample response to a List call.
-const ListOutput = `
-{
-    "networks": [
-        {
-            "bridge": "br100",
-            "bridge_interface": "eth0",
-            "broadcast": "10.0.0.7",
-            "cidr": "10.0.0.0/29",
-            "cidr_v6": null,
-            "created_at": "2011-08-15 06:19:19.387525",
-            "deleted": false,
-            "deleted_at": null,
-            "dhcp_start": "10.0.0.3",
-            "dns1": null,
-            "dns2": null,
-            "gateway": "10.0.0.1",
-            "gateway_v6": null,
-            "host": "nsokolov-desktop",
-            "id": "20c8acc0-f747-4d71-a389-46d078ebf047",
-            "injected": false,
-            "label": "mynet_0",
-            "multi_host": false,
-            "netmask": "255.255.255.248",
-            "netmask_v6": null,
-            "priority": null,
-            "project_id": "1234",
-            "rxtx_base": null,
-            "updated_at": "2011-08-16 09:26:13.048257",
-            "vlan": 100,
-            "vpn_private_address": "10.0.0.2",
-            "vpn_public_address": "127.0.0.1",
-            "vpn_public_port": 1000
-        },
-        {
-            "bridge": "br101",
-            "bridge_interface": "eth0",
-            "broadcast": "10.0.0.15",
-            "cidr": "10.0.0.10/29",
-            "cidr_v6": null,
-            "created_at": "2011-08-15 06:19:19.885495",
-            "deleted": false,
-            "deleted_at": null,
-            "dhcp_start": "10.0.0.11",
-            "dns1": null,
-            "dns2": null,
-            "gateway": "10.0.0.9",
-            "gateway_v6": null,
-            "host": null,
-            "id": "20c8acc0-f747-4d71-a389-46d078ebf000",
-            "injected": false,
-            "label": "mynet_1",
-            "multi_host": false,
-            "netmask": "255.255.255.248",
-            "netmask_v6": null,
-            "priority": null,
-            "project_id": null,
-            "rxtx_base": null,
-            "updated_at": null,
-            "vlan": 101,
-            "vpn_private_address": "10.0.0.10",
-            "vpn_public_address": null,
-            "vpn_public_port": 1001
-        }
-    ]
-}
-`
-
-// GetOutput is a sample response to a Get call.
-const GetOutput = `
-{
-    "network": {
-			"bridge": "br101",
-			"bridge_interface": "eth0",
-			"broadcast": "10.0.0.15",
-			"cidr": "10.0.0.10/29",
-			"cidr_v6": null,
-			"created_at": "2011-08-15 06:19:19.885495",
-			"deleted": false,
-			"deleted_at": null,
-			"dhcp_start": "10.0.0.11",
-			"dns1": null,
-			"dns2": null,
-			"gateway": "10.0.0.9",
-			"gateway_v6": null,
-			"host": null,
-			"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
-			"injected": false,
-			"label": "mynet_1",
-			"multi_host": false,
-			"netmask": "255.255.255.248",
-			"netmask_v6": null,
-			"priority": null,
-			"project_id": null,
-			"rxtx_base": null,
-			"updated_at": null,
-			"vlan": 101,
-			"vpn_private_address": "10.0.0.10",
-			"vpn_public_address": null,
-			"vpn_public_port": 1001
-		}
-}
-`
-
-// FirstNetwork is the first result in ListOutput.
-var nilTime time.Time
-var FirstNetwork = Network{
-	Bridge:            "br100",
-	BridgeInterface:   "eth0",
-	Broadcast:         "10.0.0.7",
-	CIDR:              "10.0.0.0/29",
-	CIDRv6:            "",
-	CreatedAt:         time.Date(2011, 8, 15, 6, 19, 19, 387525000, time.UTC),
-	Deleted:           false,
-	DeletedAt:         nilTime,
-	DHCPStart:         "10.0.0.3",
-	DNS1:              "",
-	DNS2:              "",
-	Gateway:           "10.0.0.1",
-	Gatewayv6:         "",
-	Host:              "nsokolov-desktop",
-	ID:                "20c8acc0-f747-4d71-a389-46d078ebf047",
-	Injected:          false,
-	Label:             "mynet_0",
-	MultiHost:         false,
-	Netmask:           "255.255.255.248",
-	Netmaskv6:         "",
-	Priority:          0,
-	ProjectID:         "1234",
-	RXTXBase:          0,
-	UpdatedAt:         time.Date(2011, 8, 16, 9, 26, 13, 48257000, time.UTC),
-	VLAN:              100,
-	VPNPrivateAddress: "10.0.0.2",
-	VPNPublicAddress:  "127.0.0.1",
-	VPNPublicPort:     1000,
-}
-
-// SecondNetwork is the second result in ListOutput.
-var SecondNetwork = Network{
-	Bridge:            "br101",
-	BridgeInterface:   "eth0",
-	Broadcast:         "10.0.0.15",
-	CIDR:              "10.0.0.10/29",
-	CIDRv6:            "",
-	CreatedAt:         time.Date(2011, 8, 15, 6, 19, 19, 885495000, time.UTC),
-	Deleted:           false,
-	DeletedAt:         nilTime,
-	DHCPStart:         "10.0.0.11",
-	DNS1:              "",
-	DNS2:              "",
-	Gateway:           "10.0.0.9",
-	Gatewayv6:         "",
-	Host:              "",
-	ID:                "20c8acc0-f747-4d71-a389-46d078ebf000",
-	Injected:          false,
-	Label:             "mynet_1",
-	MultiHost:         false,
-	Netmask:           "255.255.255.248",
-	Netmaskv6:         "",
-	Priority:          0,
-	ProjectID:         "",
-	RXTXBase:          0,
-	UpdatedAt:         nilTime,
-	VLAN:              101,
-	VPNPrivateAddress: "10.0.0.10",
-	VPNPublicAddress:  "",
-	VPNPublicPort:     1001,
-}
-
-// ExpectedNetworkSlice is the slice of results that should be parsed
-// from ListOutput, in the expected order.
-var ExpectedNetworkSlice = []Network{FirstNetwork, SecondNetwork}
-
-// HandleListSuccessfully configures the test server to respond to a List request.
-func HandleListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, ListOutput)
-	})
-}
-
-// HandleGetSuccessfully configures the test server to respond to a Get request
-// for an existing network.
-func HandleGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-networks/20c8acc0-f747-4d71-a389-46d078ebf000", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, GetOutput)
-	})
-}
diff --git a/openstack/compute/v2/extensions/networks/requests.go b/openstack/compute/v2/extensions/networks/requests.go
index eb20387..5432a10 100644
--- a/openstack/compute/v2/extensions/networks/requests.go
+++ b/openstack/compute/v2/extensions/networks/requests.go
@@ -1,22 +1,19 @@
 package networks
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // List returns a Pager that allows you to iterate over a collection of Network.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	url := listURL(client)
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
 		return NetworkPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, url, createPage)
+	})
 }
 
 // Get returns data about a previously created Network.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return
 }
diff --git a/openstack/compute/v2/extensions/networks/requests_test.go b/openstack/compute/v2/extensions/networks/requests_test.go
deleted file mode 100644
index 722b3f0..0000000
--- a/openstack/compute/v2/extensions/networks/requests_test.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package networks
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListSuccessfully(t)
-
-	count := 0
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNetworks(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, ExpectedNetworkSlice, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t)
-
-	actual, err := Get(client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &SecondNetwork, actual)
-}
diff --git a/openstack/compute/v2/extensions/networks/results.go b/openstack/compute/v2/extensions/networks/results.go
index 55b361d..cbcce31 100644
--- a/openstack/compute/v2/extensions/networks/results.go
+++ b/openstack/compute/v2/extensions/networks/results.go
@@ -1,99 +1,95 @@
 package networks
 
 import (
-	"fmt"
-	"time"
-
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // A Network represents a nova-network that an instance communicates on
 type Network struct {
 	// The Bridge that VIFs on this network are connected to
-	Bridge string `mapstructure:"bridge"`
+	Bridge string `json:"bridge"`
 
 	// BridgeInterface is what interface is connected to the Bridge
-	BridgeInterface string `mapstructure:"bridge_interface"`
+	BridgeInterface string `json:"bridge_interface"`
 
 	// The Broadcast address of the network.
-	Broadcast string `mapstructure:"broadcast"`
+	Broadcast string `json:"broadcast"`
 
 	// CIDR is the IPv4 subnet.
-	CIDR string `mapstructure:"cidr"`
+	CIDR string `json:"cidr"`
 
 	// CIDRv6 is the IPv6 subnet.
-	CIDRv6 string `mapstructure:"cidr_v6"`
+	CIDRv6 string `json:"cidr_v6"`
 
 	// CreatedAt is when the network was created..
-	CreatedAt time.Time `mapstructure:"-"`
+	CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at,omitempty"`
 
 	// Deleted shows if the network has been deleted.
-	Deleted bool `mapstructure:"deleted"`
+	Deleted bool `json:"deleted"`
 
 	// DeletedAt is the time when the network was deleted.
-	DeletedAt time.Time `mapstructure:"-"`
+	DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at,omitempty"`
 
 	// DHCPStart is the start of the DHCP address range.
-	DHCPStart string `mapstructure:"dhcp_start"`
+	DHCPStart string `json:"dhcp_start"`
 
 	// DNS1 is the first DNS server to use through DHCP.
-	DNS1 string `mapstructure:"dns_1"`
+	DNS1 string `json:"dns_1"`
 
 	// DNS2 is the first DNS server to use through DHCP.
-	DNS2 string `mapstructure:"dns_2"`
+	DNS2 string `json:"dns_2"`
 
 	// Gateway is the network gateway.
-	Gateway string `mapstructure:"gateway"`
+	Gateway string `json:"gateway"`
 
 	// Gatewayv6 is the IPv6 network gateway.
-	Gatewayv6 string `mapstructure:"gateway_v6"`
+	Gatewayv6 string `json:"gateway_v6"`
 
 	// Host is the host that the network service is running on.
-	Host string `mapstructure:"host"`
+	Host string `json:"host"`
 
 	// ID is the UUID of the network.
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Injected determines if network information is injected into the host.
-	Injected bool `mapstructure:"injected"`
+	Injected bool `json:"injected"`
 
 	// Label is the common name that the network has..
-	Label string `mapstructure:"label"`
+	Label string `json:"label"`
 
 	// MultiHost is if multi-host networking is enablec..
-	MultiHost bool `mapstructure:"multi_host"`
+	MultiHost bool `json:"multi_host"`
 
 	// Netmask is the network netmask.
-	Netmask string `mapstructure:"netmask"`
+	Netmask string `json:"netmask"`
 
 	// Netmaskv6 is the IPv6 netmask.
-	Netmaskv6 string `mapstructure:"netmask_v6"`
+	Netmaskv6 string `json:"netmask_v6"`
 
 	// Priority is the network interface priority.
-	Priority int `mapstructure:"priority"`
+	Priority int `json:"priority"`
 
 	// ProjectID is the project associated with this network.
-	ProjectID string `mapstructure:"project_id"`
+	ProjectID string `json:"project_id"`
 
 	// RXTXBase configures bandwidth entitlement.
-	RXTXBase int `mapstructure:"rxtx_base"`
+	RXTXBase int `json:"rxtx_base"`
 
 	// UpdatedAt is the time when the network was last updated.
-	UpdatedAt time.Time `mapstructure:"-"`
+	UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at,omitempty"`
 
 	// VLAN is the vlan this network runs on.
-	VLAN int `mapstructure:"vlan"`
+	VLAN int `json:"vlan"`
 
 	// VPNPrivateAddress is the private address of the CloudPipe VPN.
-	VPNPrivateAddress string `mapstructure:"vpn_private_address"`
+	VPNPrivateAddress string `json:"vpn_private_address"`
 
 	// VPNPublicAddress is the public address of the CloudPipe VPN.
-	VPNPublicAddress string `mapstructure:"vpn_public_address"`
+	VPNPublicAddress string `json:"vpn_public_address"`
 
 	// VPNPublicPort is the port of the CloudPipe VPN.
-	VPNPublicPort int `mapstructure:"vpn_public_port"`
+	VPNPublicPort int `json:"vpn_public_port"`
 }
 
 // NetworkPage stores a single, only page of Networks
@@ -109,52 +105,12 @@
 }
 
 // ExtractNetworks interprets a page of results as a slice of Networks
-func ExtractNetworks(page pagination.Page) ([]Network, error) {
-	var res struct {
-		Networks []Network `mapstructure:"networks"`
+func ExtractNetworks(r pagination.Page) ([]Network, error) {
+	var s struct {
+		Networks []Network `json:"networks"`
 	}
-
-	err := mapstructure.Decode(page.(NetworkPage).Body, &res)
-
-	var rawNetworks []interface{}
-	body := page.(NetworkPage).Body
-	switch body.(type) {
-	case map[string]interface{}:
-		rawNetworks = body.(map[string]interface{})["networks"].([]interface{})
-	case map[string][]interface{}:
-		rawNetworks = body.(map[string][]interface{})["networks"]
-	default:
-		return res.Networks, fmt.Errorf("Unknown type")
-	}
-
-	for i := range rawNetworks {
-		thisNetwork := rawNetworks[i].(map[string]interface{})
-		if t, ok := thisNetwork["created_at"].(string); ok && t != "" {
-			createdAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
-			if err != nil {
-				return res.Networks, err
-			}
-			res.Networks[i].CreatedAt = createdAt
-		}
-
-		if t, ok := thisNetwork["updated_at"].(string); ok && t != "" {
-			updatedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
-			if err != nil {
-				return res.Networks, err
-			}
-			res.Networks[i].UpdatedAt = updatedAt
-		}
-
-		if t, ok := thisNetwork["deleted_at"].(string); ok && t != "" {
-			deletedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
-			if err != nil {
-				return res.Networks, err
-			}
-			res.Networks[i].DeletedAt = deletedAt
-		}
-	}
-
-	return res.Networks, err
+	err := (r.(NetworkPage)).ExtractInto(&s)
+	return s.Networks, err
 }
 
 type NetworkResult struct {
@@ -164,55 +120,11 @@
 // Extract is a method that attempts to interpret any Network resource
 // response as a Network struct.
 func (r NetworkResult) Extract() (*Network, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Network *Network `json:"network"`
 	}
-
-	var res struct {
-		Network *Network `json:"network" mapstructure:"network"`
-	}
-
-	config := &mapstructure.DecoderConfig{
-		Result:           &res,
-		WeaklyTypedInput: true,
-	}
-	decoder, err := mapstructure.NewDecoder(config)
-	if err != nil {
-		return nil, err
-	}
-
-	if err := decoder.Decode(r.Body); err != nil {
-		return nil, err
-	}
-
-	b := r.Body.(map[string]interface{})["network"].(map[string]interface{})
-
-	if t, ok := b["created_at"].(string); ok && t != "" {
-		createdAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
-		if err != nil {
-			return res.Network, err
-		}
-		res.Network.CreatedAt = createdAt
-	}
-
-	if t, ok := b["updated_at"].(string); ok && t != "" {
-		updatedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
-		if err != nil {
-			return res.Network, err
-		}
-		res.Network.UpdatedAt = updatedAt
-	}
-
-	if t, ok := b["deleted_at"].(string); ok && t != "" {
-		deletedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
-		if err != nil {
-			return res.Network, err
-		}
-		res.Network.DeletedAt = deletedAt
-	}
-
-	return res.Network, err
-
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // GetResult is the response from a Get operation. Call its Extract method to interpret it
diff --git a/openstack/compute/v2/extensions/networks/testing/doc.go b/openstack/compute/v2/extensions/networks/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/networks/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/networks/testing/fixtures.go b/openstack/compute/v2/extensions/networks/testing/fixtures.go
new file mode 100644
index 0000000..e2fa49b
--- /dev/null
+++ b/openstack/compute/v2/extensions/networks/testing/fixtures.go
@@ -0,0 +1,204 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ListOutput is a sample response to a List call.
+const ListOutput = `
+{
+    "networks": [
+        {
+            "bridge": "br100",
+            "bridge_interface": "eth0",
+            "broadcast": "10.0.0.7",
+            "cidr": "10.0.0.0/29",
+            "cidr_v6": null,
+            "created_at": "2011-08-15T06:19:19.387525",
+            "deleted": false,
+            "dhcp_start": "10.0.0.3",
+            "dns1": null,
+            "dns2": null,
+            "gateway": "10.0.0.1",
+            "gateway_v6": null,
+            "host": "nsokolov-desktop",
+            "id": "20c8acc0-f747-4d71-a389-46d078ebf047",
+            "injected": false,
+            "label": "mynet_0",
+            "multi_host": false,
+            "netmask": "255.255.255.248",
+            "netmask_v6": null,
+            "priority": null,
+            "project_id": "1234",
+            "rxtx_base": null,
+            "updated_at": "2011-08-16T09:26:13.048257",
+            "vlan": 100,
+            "vpn_private_address": "10.0.0.2",
+            "vpn_public_address": "127.0.0.1",
+            "vpn_public_port": 1000
+        },
+        {
+            "bridge": "br101",
+            "bridge_interface": "eth0",
+            "broadcast": "10.0.0.15",
+            "cidr": "10.0.0.10/29",
+            "cidr_v6": null,
+            "created_at": "2011-08-15T06:19:19.387525",
+            "deleted": false,
+            "dhcp_start": "10.0.0.11",
+            "dns1": null,
+            "dns2": null,
+            "gateway": "10.0.0.9",
+            "gateway_v6": null,
+            "host": null,
+            "id": "20c8acc0-f747-4d71-a389-46d078ebf000",
+            "injected": false,
+            "label": "mynet_1",
+            "multi_host": false,
+            "netmask": "255.255.255.248",
+            "netmask_v6": null,
+            "priority": null,
+            "project_id": null,
+            "rxtx_base": null,
+            "vlan": 101,
+            "vpn_private_address": "10.0.0.10",
+            "vpn_public_address": null,
+            "vpn_public_port": 1001
+        }
+    ]
+}
+`
+
+// GetOutput is a sample response to a Get call.
+const GetOutput = `
+{
+    "network": {
+			"bridge": "br101",
+			"bridge_interface": "eth0",
+			"broadcast": "10.0.0.15",
+			"cidr": "10.0.0.10/29",
+			"cidr_v6": null,
+			"created_at": "2011-08-15T06:19:19.387525",
+			"deleted": false,
+			"dhcp_start": "10.0.0.11",
+			"dns1": null,
+			"dns2": null,
+			"gateway": "10.0.0.9",
+			"gateway_v6": null,
+			"host": null,
+			"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
+			"injected": false,
+			"label": "mynet_1",
+			"multi_host": false,
+			"netmask": "255.255.255.248",
+			"netmask_v6": null,
+			"priority": null,
+			"project_id": null,
+			"rxtx_base": null,
+			"vlan": 101,
+			"vpn_private_address": "10.0.0.10",
+			"vpn_public_address": null,
+			"vpn_public_port": 1001
+		}
+}
+`
+
+// FirstNetwork is the first result in ListOutput.
+var nilTime time.Time
+var FirstNetwork = networks.Network{
+	Bridge:            "br100",
+	BridgeInterface:   "eth0",
+	Broadcast:         "10.0.0.7",
+	CIDR:              "10.0.0.0/29",
+	CIDRv6:            "",
+	CreatedAt:         gophercloud.JSONRFC3339MilliNoZ(time.Date(2011, 8, 15, 6, 19, 19, 387525000, time.UTC)),
+	Deleted:           false,
+	DeletedAt:         gophercloud.JSONRFC3339MilliNoZ(nilTime),
+	DHCPStart:         "10.0.0.3",
+	DNS1:              "",
+	DNS2:              "",
+	Gateway:           "10.0.0.1",
+	Gatewayv6:         "",
+	Host:              "nsokolov-desktop",
+	ID:                "20c8acc0-f747-4d71-a389-46d078ebf047",
+	Injected:          false,
+	Label:             "mynet_0",
+	MultiHost:         false,
+	Netmask:           "255.255.255.248",
+	Netmaskv6:         "",
+	Priority:          0,
+	ProjectID:         "1234",
+	RXTXBase:          0,
+	UpdatedAt:         gophercloud.JSONRFC3339MilliNoZ(time.Date(2011, 8, 16, 9, 26, 13, 48257000, time.UTC)),
+	VLAN:              100,
+	VPNPrivateAddress: "10.0.0.2",
+	VPNPublicAddress:  "127.0.0.1",
+	VPNPublicPort:     1000,
+}
+
+// SecondNetwork is the second result in ListOutput.
+var SecondNetwork = networks.Network{
+	Bridge:            "br101",
+	BridgeInterface:   "eth0",
+	Broadcast:         "10.0.0.15",
+	CIDR:              "10.0.0.10/29",
+	CIDRv6:            "",
+	CreatedAt:         gophercloud.JSONRFC3339MilliNoZ(time.Date(2011, 8, 15, 6, 19, 19, 387525000, time.UTC)),
+	Deleted:           false,
+	DeletedAt:         gophercloud.JSONRFC3339MilliNoZ(nilTime),
+	DHCPStart:         "10.0.0.11",
+	DNS1:              "",
+	DNS2:              "",
+	Gateway:           "10.0.0.9",
+	Gatewayv6:         "",
+	Host:              "",
+	ID:                "20c8acc0-f747-4d71-a389-46d078ebf000",
+	Injected:          false,
+	Label:             "mynet_1",
+	MultiHost:         false,
+	Netmask:           "255.255.255.248",
+	Netmaskv6:         "",
+	Priority:          0,
+	ProjectID:         "",
+	RXTXBase:          0,
+	UpdatedAt:         gophercloud.JSONRFC3339MilliNoZ(nilTime),
+	VLAN:              101,
+	VPNPrivateAddress: "10.0.0.10",
+	VPNPublicAddress:  "",
+	VPNPublicPort:     1001,
+}
+
+// ExpectedNetworkSlice is the slice of results that should be parsed
+// from ListOutput, in the expected order.
+var ExpectedNetworkSlice = []networks.Network{FirstNetwork, SecondNetwork}
+
+// HandleListSuccessfully configures the test server to respond to a List request.
+func HandleListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-networks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, ListOutput)
+	})
+}
+
+// HandleGetSuccessfully configures the test server to respond to a Get request
+// for an existing network.
+func HandleGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-networks/20c8acc0-f747-4d71-a389-46d078ebf000", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, GetOutput)
+	})
+}
diff --git a/openstack/compute/v2/extensions/networks/testing/requests_test.go b/openstack/compute/v2/extensions/networks/testing/requests_test.go
new file mode 100644
index 0000000..36b5463
--- /dev/null
+++ b/openstack/compute/v2/extensions/networks/testing/requests_test.go
@@ -0,0 +1,38 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListSuccessfully(t)
+
+	count := 0
+	err := networks.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := networks.ExtractNetworks(page)
+		th.AssertNoErr(t, err)
+		th.CheckDeepEquals(t, ExpectedNetworkSlice, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, 1, count)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t)
+
+	actual, err := networks.Get(client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &SecondNetwork, actual)
+}
diff --git a/openstack/compute/v2/extensions/networks/urls.go b/openstack/compute/v2/extensions/networks/urls.go
index 6966462..491bde6 100644
--- a/openstack/compute/v2/extensions/networks/urls.go
+++ b/openstack/compute/v2/extensions/networks/urls.go
@@ -1,6 +1,6 @@
 package networks
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const resourcePath = "os-networks"
 
diff --git a/openstack/compute/v2/extensions/networks/urls_test.go b/openstack/compute/v2/extensions/networks/urls_test.go
deleted file mode 100644
index be54c90..0000000
--- a/openstack/compute/v2/extensions/networks/urls_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package networks
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-networks", listURL(c))
-}
-
-func TestGetURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	id := "1"
-
-	th.CheckEquals(t, c.Endpoint+"os-networks/"+id, getURL(c, id))
-}
diff --git a/openstack/compute/v2/extensions/quotasets/fixtures.go b/openstack/compute/v2/extensions/quotasets/fixtures.go
deleted file mode 100644
index c1bb4ea..0000000
--- a/openstack/compute/v2/extensions/quotasets/fixtures.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// +build fixtures
-
-package quotasets
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// GetOutput is a sample response to a Get call.
-const GetOutput = `
-{
-   "quota_set" : {
-      "instances" : 25,
-      "security_groups" : 10,
-      "security_group_rules" : 20,
-      "cores" : 200,
-      "injected_file_content_bytes" : 10240,
-      "injected_files" : 5,
-      "metadata_items" : 128,
-      "ram" : 200000,
-      "keypairs" : 10,
-      "injected_file_path_bytes" : 255
-   }
-}
-`
-
-const FirstTenantID = "555544443333222211110000ffffeeee"
-
-// FirstQuotaSet is the first result in ListOutput.
-var FirstQuotaSet = QuotaSet{
-	FixedIps:                 0,
-	FloatingIps:              0,
-	InjectedFileContentBytes: 10240,
-	InjectedFilePathBytes:    255,
-	InjectedFiles:            5,
-	KeyPairs:                 10,
-	MetadataItems:            128,
-	Ram:                      200000,
-	SecurityGroupRules:       20,
-	SecurityGroups:           10,
-	Cores:                    200,
-	Instances:                25,
-}
-
-// HandleGetSuccessfully configures the test server to respond to a Get request for sample tenant
-func HandleGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, GetOutput)
-	})
-}
diff --git a/openstack/compute/v2/extensions/quotasets/requests.go b/openstack/compute/v2/extensions/quotasets/requests.go
index 52f0839..76beb17 100644
--- a/openstack/compute/v2/extensions/quotasets/requests.go
+++ b/openstack/compute/v2/extensions/quotasets/requests.go
@@ -1,7 +1,7 @@
 package quotasets
 
 import (
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 // Get returns public data about a previously created QuotaSet.
diff --git a/openstack/compute/v2/extensions/quotasets/requests_test.go b/openstack/compute/v2/extensions/quotasets/requests_test.go
deleted file mode 100644
index 5d766fa..0000000
--- a/openstack/compute/v2/extensions/quotasets/requests_test.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package quotasets
-
-import (
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-	"testing"
-)
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t)
-	actual, err := Get(client.ServiceClient(), FirstTenantID).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &FirstQuotaSet, actual)
-}
diff --git a/openstack/compute/v2/extensions/quotasets/results.go b/openstack/compute/v2/extensions/quotasets/results.go
index cbf4d6b..f6c4e5a 100644
--- a/openstack/compute/v2/extensions/quotasets/results.go
+++ b/openstack/compute/v2/extensions/quotasets/results.go
@@ -1,39 +1,38 @@
 package quotasets
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // QuotaSet is a set of operational limits that allow for control of compute usage.
 type QuotaSet struct {
 	//ID is tenant associated with this quota_set
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 	//FixedIps is number of fixed ips alloted this quota_set
-	FixedIps int `mapstructure:"fixed_ips"`
+	FixedIps int `json:"fixed_ips"`
 	// FloatingIps is number of floating ips alloted this quota_set
-	FloatingIps int `mapstructure:"floating_ips"`
+	FloatingIps int `json:"floating_ips"`
 	// InjectedFileContentBytes is content bytes allowed for each injected file
-	InjectedFileContentBytes int `mapstructure:"injected_file_content_bytes"`
+	InjectedFileContentBytes int `json:"injected_file_content_bytes"`
 	// InjectedFilePathBytes is allowed bytes for each injected file path
-	InjectedFilePathBytes int `mapstructure:"injected_file_path_bytes"`
+	InjectedFilePathBytes int `json:"injected_file_path_bytes"`
 	// InjectedFiles is injected files allowed for each project
-	InjectedFiles int `mapstructure:"injected_files"`
+	InjectedFiles int `json:"injected_files"`
 	// KeyPairs is number of ssh keypairs
-	KeyPairs int `mapstructure:"keypairs"`
+	KeyPairs int `json:"keypairs"`
 	// MetadataItems is number of metadata items allowed for each instance
-	MetadataItems int `mapstructure:"metadata_items"`
+	MetadataItems int `json:"metadata_items"`
 	// Ram is megabytes allowed for each instance
-	Ram int `mapstructure:"ram"`
+	Ram int `json:"ram"`
 	// SecurityGroupRules is rules allowed for each security group
-	SecurityGroupRules int `mapstructure:"security_group_rules"`
+	SecurityGroupRules int `json:"security_group_rules"`
 	// SecurityGroups security groups allowed for each project
-	SecurityGroups int `mapstructure:"security_groups"`
+	SecurityGroups int `json:"security_groups"`
 	// Cores is number of instance cores allowed for each project
-	Cores int `mapstructure:"cores"`
+	Cores int `json:"cores"`
 	// Instances is number of instances allowed for each project
-	Instances int `mapstructure:"instances"`
+	Instances int `json:"instances"`
 }
 
 // QuotaSetPage stores a single, only page of QuotaSet results from a List call.
@@ -48,17 +47,12 @@
 }
 
 // ExtractQuotaSets interprets a page of results as a slice of QuotaSets.
-func ExtractQuotaSets(page pagination.Page) ([]QuotaSet, error) {
-	var resp struct {
-		QuotaSets []QuotaSet `mapstructure:"quotas"`
+func ExtractQuotaSets(r pagination.Page) ([]QuotaSet, error) {
+	var s struct {
+		QuotaSets []QuotaSet `json:"quotas"`
 	}
-
-	err := mapstructure.Decode(page.(QuotaSetPage).Body, &resp)
-	results := make([]QuotaSet, len(resp.QuotaSets))
-	for i, q := range resp.QuotaSets {
-		results[i] = q
-	}
-	return results, err
+	err := (r.(QuotaSetPage)).ExtractInto(&s)
+	return s.QuotaSets, err
 }
 
 type quotaResult struct {
@@ -67,16 +61,11 @@
 
 // Extract is a method that attempts to interpret any QuotaSet resource response as a QuotaSet struct.
 func (r quotaResult) Extract() (*QuotaSet, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		QuotaSet *QuotaSet `json:"quota_set"`
 	}
-
-	var res struct {
-		QuotaSet *QuotaSet `json:"quota_set" mapstructure:"quota_set"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-	return res.QuotaSet, err
+	err := r.ExtractInto(&s)
+	return s.QuotaSet, err
 }
 
 // GetResult is the response from a Get operation. Call its Extract method to interpret it
diff --git a/openstack/compute/v2/extensions/quotasets/testing/doc.go b/openstack/compute/v2/extensions/quotasets/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/quotasets/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go
new file mode 100644
index 0000000..3fef872
--- /dev/null
+++ b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go
@@ -0,0 +1,58 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// GetOutput is a sample response to a Get call.
+const GetOutput = `
+{
+   "quota_set" : {
+      "instances" : 25,
+      "security_groups" : 10,
+      "security_group_rules" : 20,
+      "cores" : 200,
+      "injected_file_content_bytes" : 10240,
+      "injected_files" : 5,
+      "metadata_items" : 128,
+      "ram" : 200000,
+      "keypairs" : 10,
+      "injected_file_path_bytes" : 255
+   }
+}
+`
+
+const FirstTenantID = "555544443333222211110000ffffeeee"
+
+// FirstQuotaSet is the first result in ListOutput.
+var FirstQuotaSet = quotasets.QuotaSet{
+	FixedIps:                 0,
+	FloatingIps:              0,
+	InjectedFileContentBytes: 10240,
+	InjectedFilePathBytes:    255,
+	InjectedFiles:            5,
+	KeyPairs:                 10,
+	MetadataItems:            128,
+	Ram:                      200000,
+	SecurityGroupRules:       20,
+	SecurityGroups:           10,
+	Cores:                    200,
+	Instances:                25,
+}
+
+// HandleGetSuccessfully configures the test server to respond to a Get request for sample tenant
+func HandleGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, GetOutput)
+	})
+}
diff --git a/openstack/compute/v2/extensions/quotasets/testing/requests_test.go b/openstack/compute/v2/extensions/quotasets/testing/requests_test.go
new file mode 100644
index 0000000..8fc1fd4
--- /dev/null
+++ b/openstack/compute/v2/extensions/quotasets/testing/requests_test.go
@@ -0,0 +1,18 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t)
+	actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &FirstQuotaSet, actual)
+}
diff --git a/openstack/compute/v2/extensions/quotasets/urls.go b/openstack/compute/v2/extensions/quotasets/urls.go
index c04d941..e910376 100644
--- a/openstack/compute/v2/extensions/quotasets/urls.go
+++ b/openstack/compute/v2/extensions/quotasets/urls.go
@@ -1,6 +1,6 @@
 package quotasets
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const resourcePath = "os-quota-sets"
 
diff --git a/openstack/compute/v2/extensions/quotasets/urls_test.go b/openstack/compute/v2/extensions/quotasets/urls_test.go
deleted file mode 100644
index f19a6ad..0000000
--- a/openstack/compute/v2/extensions/quotasets/urls_test.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package quotasets
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestGetURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-quota-sets/wat", getURL(c, "wat"))
-}
diff --git a/openstack/compute/v2/extensions/schedulerhints/requests.go b/openstack/compute/v2/extensions/schedulerhints/requests.go
index 567eef4..1e06b46 100644
--- a/openstack/compute/v2/extensions/schedulerhints/requests.go
+++ b/openstack/compute/v2/extensions/schedulerhints/requests.go
@@ -1,12 +1,12 @@
 package schedulerhints
 
 import (
-	"fmt"
 	"net"
 	"regexp"
 	"strings"
 
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 )
 
 // SchedulerHints represents a set of scheduling hints that are passed to the
@@ -14,40 +14,39 @@
 type SchedulerHints struct {
 	// Group specifies a Server Group to place the instance in.
 	Group string
-
 	// DifferentHost will place the instance on a compute node that does not
 	// host the given instances.
 	DifferentHost []string
-
 	// SameHost will place the instance on a compute node that hosts the given
 	// instances.
 	SameHost []string
-
 	// Query is a conditional statement that results in compute nodes able to
 	// host the instance.
 	Query []interface{}
-
 	// TargetCell specifies a cell name where the instance will be placed.
-	TargetCell string
-
+	TargetCell string `json:"target_cell,omitempty"`
 	// BuildNearHostIP specifies a subnet of compute nodes to host the instance.
 	BuildNearHostIP string
 }
 
-// SchedulerHintsBuilder builds the scheduler hints into a serializable format.
-type SchedulerHintsBuilder interface {
-	ToServerSchedulerHintsMap() (map[string]interface{}, error)
+// CreateOptsBuilder builds the scheduler hints into a serializable format.
+type CreateOptsBuilder interface {
+	ToServerSchedulerHintsCreateMap() (map[string]interface{}, error)
 }
 
 // ToServerSchedulerHintsMap builds the scheduler hints into a serializable format.
-func (opts SchedulerHints) ToServerSchedulerHintsMap() (map[string]interface{}, error) {
+func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) {
 	sh := make(map[string]interface{})
 
 	uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
 
 	if opts.Group != "" {
 		if !uuidRegex.MatchString(opts.Group) {
-			return nil, fmt.Errorf("Group must be a UUID")
+			err := gophercloud.ErrInvalidInput{}
+			err.Argument = "schedulerhints.SchedulerHints.Group"
+			err.Value = opts.Group
+			err.Info = "Group must be a UUID"
+			return nil, err
 		}
 		sh["group"] = opts.Group
 	}
@@ -55,7 +54,11 @@
 	if len(opts.DifferentHost) > 0 {
 		for _, diffHost := range opts.DifferentHost {
 			if !uuidRegex.MatchString(diffHost) {
-				return nil, fmt.Errorf("The hosts in DifferentHost must be in UUID format.")
+				err := gophercloud.ErrInvalidInput{}
+				err.Argument = "schedulerhints.SchedulerHints.DifferentHost"
+				err.Value = opts.DifferentHost
+				err.Info = "The hosts must be in UUID format."
+				return nil, err
 			}
 		}
 		sh["different_host"] = opts.DifferentHost
@@ -64,7 +67,11 @@
 	if len(opts.SameHost) > 0 {
 		for _, sameHost := range opts.SameHost {
 			if !uuidRegex.MatchString(sameHost) {
-				return nil, fmt.Errorf("The hosts in SameHost must be in UUID format.")
+				err := gophercloud.ErrInvalidInput{}
+				err.Argument = "schedulerhints.SchedulerHints.SameHost"
+				err.Value = opts.SameHost
+				err.Info = "The hosts must be in UUID format."
+				return nil, err
 			}
 		}
 		sh["same_host"] = opts.SameHost
@@ -83,7 +90,11 @@
 	*/
 	if len(opts.Query) > 0 {
 		if len(opts.Query) < 3 {
-			return nil, fmt.Errorf("Query must be a conditional statement in the format of [op,variable,value]")
+			err := gophercloud.ErrInvalidInput{}
+			err.Argument = "schedulerhints.SchedulerHints.Query"
+			err.Value = opts.Query
+			err.Info = "Must be a conditional statement in the format of [op,variable,value]"
+			return nil, err
 		}
 		sh["query"] = opts.Query
 	}
@@ -94,7 +105,11 @@
 
 	if opts.BuildNearHostIP != "" {
 		if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil {
-			return nil, fmt.Errorf("BuildNearHostIP must be a valid subnet in the form 192.168.1.1/24")
+			err := gophercloud.ErrInvalidInput{}
+			err.Argument = "schedulerhints.SchedulerHints.BuildNearHostIP"
+			err.Value = opts.BuildNearHostIP
+			err.Info = "Must be a valid subnet in the form 192.168.1.1/24"
+			return nil, err
 		}
 		ipParts := strings.Split(opts.BuildNearHostIP, "/")
 		sh["build_near_host_ip"] = ipParts[0]
@@ -107,9 +122,8 @@
 // CreateOptsExt adds a SchedulerHints option to the base CreateOpts.
 type CreateOptsExt struct {
 	servers.CreateOptsBuilder
-
 	// SchedulerHints provides a set of hints to the scheduler.
-	SchedulerHints SchedulerHintsBuilder
+	SchedulerHints CreateOptsBuilder
 }
 
 // ToServerCreateMap adds the SchedulerHints option to the base server creation options.
@@ -119,7 +133,7 @@
 		return nil, err
 	}
 
-	schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsMap()
+	schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsCreateMap()
 	if err != nil {
 		return nil, err
 	}
diff --git a/openstack/compute/v2/extensions/schedulerhints/requests_test.go b/openstack/compute/v2/extensions/schedulerhints/requests_test.go
deleted file mode 100644
index 9b38b35..0000000
--- a/openstack/compute/v2/extensions/schedulerhints/requests_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package schedulerhints
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestCreateOpts(t *testing.T) {
-	base := servers.CreateOpts{
-		Name:      "createdserver",
-		ImageRef:  "asdfasdfasdf",
-		FlavorRef: "performance1-1",
-	}
-
-	schedulerHints := SchedulerHints{
-		Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
-		DifferentHost: []string{
-			"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
-			"8c19174f-4220-44f0-824a-cd1eeef10287",
-		},
-		SameHost: []string{
-			"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
-			"8c19174f-4220-44f0-824a-cd1eeef10287",
-		},
-		Query:           []interface{}{">=", "$free_ram_mb", "1024"},
-		TargetCell:      "foobar",
-		BuildNearHostIP: "192.168.1.1/24",
-	}
-
-	ext := CreateOptsExt{
-		CreateOptsBuilder: base,
-		SchedulerHints:    schedulerHints,
-	}
-
-	expected := `
-		{
-			"server": {
-				"name": "createdserver",
-				"imageRef": "asdfasdfasdf",
-				"flavorRef": "performance1-1",
-				"flavorName": "",
-				"imageName": ""
-			},
-			"os:scheduler_hints": {
-				"group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
-				"different_host": [
-					"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
-					"8c19174f-4220-44f0-824a-cd1eeef10287"
-				],
-				"same_host": [
-					"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
-					"8c19174f-4220-44f0-824a-cd1eeef10287"
-				],
-				"query": [
-					">=", "$free_ram_mb", "1024"
-				],
-				"target_cell": "foobar",
-				"build_near_host_ip": "192.168.1.1",
-				"cidr": "/24"
-			}
-		}
-	`
-	actual, err := ext.ToServerCreateMap()
-	th.AssertNoErr(t, err)
-	th.CheckJSONEquals(t, expected, actual)
-}
-
-func TestCreateOptsWithComplexQuery(t *testing.T) {
-	base := servers.CreateOpts{
-		Name:      "createdserver",
-		ImageRef:  "asdfasdfasdf",
-		FlavorRef: "performance1-1",
-	}
-
-	schedulerHints := SchedulerHints{
-		Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
-		DifferentHost: []string{
-			"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
-			"8c19174f-4220-44f0-824a-cd1eeef10287",
-		},
-		SameHost: []string{
-			"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
-			"8c19174f-4220-44f0-824a-cd1eeef10287",
-		},
-		Query:           []interface{}{"and", []string{">=", "$free_ram_mb", "1024"}, []string{">=", "$free_disk_mb", "204800"}},
-		TargetCell:      "foobar",
-		BuildNearHostIP: "192.168.1.1/24",
-	}
-
-	ext := CreateOptsExt{
-		CreateOptsBuilder: base,
-		SchedulerHints:    schedulerHints,
-	}
-
-	expected := `
-		{
-			"server": {
-				"name": "createdserver",
-				"imageRef": "asdfasdfasdf",
-				"flavorRef": "performance1-1",
-				"flavorName": "",
-				"imageName": ""
-			},
-			"os:scheduler_hints": {
-				"group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
-				"different_host": [
-					"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
-					"8c19174f-4220-44f0-824a-cd1eeef10287"
-				],
-				"same_host": [
-					"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
-					"8c19174f-4220-44f0-824a-cd1eeef10287"
-				],
-				"query": [
-					"and",
-					[">=", "$free_ram_mb", "1024"],
-					[">=", "$free_disk_mb", "204800"]
-				],
-				"target_cell": "foobar",
-				"build_near_host_ip": "192.168.1.1",
-				"cidr": "/24"
-			}
-		}
-	`
-	actual, err := ext.ToServerCreateMap()
-	th.AssertNoErr(t, err)
-	th.CheckJSONEquals(t, expected, actual)
-}
diff --git a/openstack/compute/v2/extensions/schedulerhints/testing/doc.go b/openstack/compute/v2/extensions/schedulerhints/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/schedulerhints/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go b/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go
new file mode 100644
index 0000000..cf21c4e
--- /dev/null
+++ b/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go
@@ -0,0 +1,127 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestCreateOpts(t *testing.T) {
+	base := servers.CreateOpts{
+		Name:      "createdserver",
+		ImageRef:  "asdfasdfasdf",
+		FlavorRef: "performance1-1",
+	}
+
+	schedulerHints := schedulerhints.SchedulerHints{
+		Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
+		DifferentHost: []string{
+			"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
+			"8c19174f-4220-44f0-824a-cd1eeef10287",
+		},
+		SameHost: []string{
+			"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
+			"8c19174f-4220-44f0-824a-cd1eeef10287",
+		},
+		Query:           []interface{}{">=", "$free_ram_mb", "1024"},
+		TargetCell:      "foobar",
+		BuildNearHostIP: "192.168.1.1/24",
+	}
+
+	ext := schedulerhints.CreateOptsExt{
+		CreateOptsBuilder: base,
+		SchedulerHints:    schedulerHints,
+	}
+
+	expected := `
+		{
+			"server": {
+				"name": "createdserver",
+				"imageRef": "asdfasdfasdf",
+				"flavorRef": "performance1-1"
+			},
+			"os:scheduler_hints": {
+				"group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
+				"different_host": [
+					"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
+					"8c19174f-4220-44f0-824a-cd1eeef10287"
+				],
+				"same_host": [
+					"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
+					"8c19174f-4220-44f0-824a-cd1eeef10287"
+				],
+				"query": [
+					">=", "$free_ram_mb", "1024"
+				],
+				"target_cell": "foobar",
+				"build_near_host_ip": "192.168.1.1",
+				"cidr": "/24"
+			}
+		}
+	`
+	actual, err := ext.ToServerCreateMap()
+	th.AssertNoErr(t, err)
+	th.CheckJSONEquals(t, expected, actual)
+}
+
+func TestCreateOptsWithComplexQuery(t *testing.T) {
+	base := servers.CreateOpts{
+		Name:      "createdserver",
+		ImageRef:  "asdfasdfasdf",
+		FlavorRef: "performance1-1",
+	}
+
+	schedulerHints := schedulerhints.SchedulerHints{
+		Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
+		DifferentHost: []string{
+			"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
+			"8c19174f-4220-44f0-824a-cd1eeef10287",
+		},
+		SameHost: []string{
+			"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
+			"8c19174f-4220-44f0-824a-cd1eeef10287",
+		},
+		Query:           []interface{}{"and", []string{">=", "$free_ram_mb", "1024"}, []string{">=", "$free_disk_mb", "204800"}},
+		TargetCell:      "foobar",
+		BuildNearHostIP: "192.168.1.1/24",
+	}
+
+	ext := schedulerhints.CreateOptsExt{
+		CreateOptsBuilder: base,
+		SchedulerHints:    schedulerHints,
+	}
+
+	expected := `
+		{
+			"server": {
+				"name": "createdserver",
+				"imageRef": "asdfasdfasdf",
+				"flavorRef": "performance1-1"
+			},
+			"os:scheduler_hints": {
+				"group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
+				"different_host": [
+					"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
+					"8c19174f-4220-44f0-824a-cd1eeef10287"
+				],
+				"same_host": [
+					"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
+					"8c19174f-4220-44f0-824a-cd1eeef10287"
+				],
+				"query": [
+					"and",
+					[">=", "$free_ram_mb", "1024"],
+					[">=", "$free_disk_mb", "204800"]
+				],
+				"target_cell": "foobar",
+				"build_near_host_ip": "192.168.1.1",
+				"cidr": "/24"
+			}
+		}
+	`
+	actual, err := ext.ToServerCreateMap()
+	th.AssertNoErr(t, err)
+	th.CheckJSONEquals(t, expected, actual)
+}
diff --git a/openstack/compute/v2/extensions/secgroups/fixtures.go b/openstack/compute/v2/extensions/secgroups/fixtures.go
deleted file mode 100644
index d58d908..0000000
--- a/openstack/compute/v2/extensions/secgroups/fixtures.go
+++ /dev/null
@@ -1,305 +0,0 @@
-// +build fixtures
-
-package secgroups
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const rootPath = "/os-security-groups"
-
-const listGroupsJSON = `
-{
-  "security_groups": [
-    {
-      "description": "default",
-      "id": "{groupID}",
-      "name": "default",
-      "rules": [],
-      "tenant_id": "openstack"
-    }
-  ]
-}
-`
-
-func mockListGroupsResponse(t *testing.T) {
-	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, listGroupsJSON)
-	})
-}
-
-func mockListGroupsByServerResponse(t *testing.T, serverID string) {
-	url := fmt.Sprintf("/servers/%s%s", serverID, rootPath)
-	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, listGroupsJSON)
-	})
-}
-
-func mockCreateGroupResponse(t *testing.T) {
-	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "security_group": {
-    "name": "test",
-    "description": "something"
-  }
-}
-	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "security_group": {
-    "description": "something",
-    "id": "{groupID}",
-    "name": "test",
-    "rules": [],
-    "tenant_id": "openstack"
-  }
-}
-`)
-	})
-}
-
-func mockUpdateGroupResponse(t *testing.T, groupID string) {
-	url := fmt.Sprintf("%s/%s", rootPath, groupID)
-	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "security_group": {
-    "name": "new_name",
-		"description": "new_desc"
-  }
-}
-	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "security_group": {
-    "description": "something",
-    "id": "{groupID}",
-    "name": "new_name",
-    "rules": [],
-    "tenant_id": "openstack"
-  }
-}
-`)
-	})
-}
-
-func mockGetGroupsResponse(t *testing.T, groupID string) {
-	url := fmt.Sprintf("%s/%s", rootPath, groupID)
-	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "security_group": {
-    "description": "default",
-    "id": "{groupID}",
-    "name": "default",
-    "rules": [
-      {
-        "from_port": 80,
-        "group": {
-          "tenant_id": "openstack",
-          "name": "default"
-        },
-        "ip_protocol": "TCP",
-        "to_port": 85,
-        "parent_group_id": "{groupID}",
-        "ip_range": {
-						"cidr": "0.0.0.0"
-				},
-        "id": "{ruleID}"
-      }
-    ],
-    "tenant_id": "openstack"
-  }
-}
-			`)
-	})
-}
-
-func mockGetNumericIDGroupResponse(t *testing.T, groupID int) {
-	url := fmt.Sprintf("%s/%d", rootPath, groupID)
-	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-	"security_group": {
-		"id": 12345
-	}
-}
-			`)
-	})
-}
-
-func mockDeleteGroupResponse(t *testing.T, groupID string) {
-	url := fmt.Sprintf("%s/%s", rootPath, groupID)
-	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockAddRuleResponse(t *testing.T) {
-	th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "security_group_rule": {
-    "from_port": 22,
-    "ip_protocol": "TCP",
-    "to_port": 22,
-    "parent_group_id": "{groupID}",
-    "cidr": "0.0.0.0/0"
-  }
-}	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "security_group_rule": {
-    "from_port": 22,
-    "group": {},
-    "ip_protocol": "TCP",
-    "to_port": 22,
-    "parent_group_id": "{groupID}",
-    "ip_range": {
-      "cidr": "0.0.0.0/0"
-    },
-    "id": "{ruleID}"
-  }
-}`)
-	})
-}
-
-func mockAddRuleResponseICMPZero(t *testing.T) {
-	th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "security_group_rule": {
-    "from_port": 0,
-    "ip_protocol": "ICMP",
-    "to_port": 0,
-    "parent_group_id": "{groupID}",
-    "cidr": "0.0.0.0/0"
-  }
-}	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "security_group_rule": {
-    "from_port": 0,
-    "group": {},
-    "ip_protocol": "ICMP",
-    "to_port": 0,
-    "parent_group_id": "{groupID}",
-    "ip_range": {
-      "cidr": "0.0.0.0/0"
-    },
-    "id": "{ruleID}"
-  }
-}`)
-	})
-}
-
-func mockDeleteRuleResponse(t *testing.T, ruleID string) {
-	url := fmt.Sprintf("/os-security-group-rules/%s", ruleID)
-	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockAddServerToGroupResponse(t *testing.T, serverID string) {
-	url := fmt.Sprintf("/servers/%s/action", serverID)
-	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "addSecurityGroup": {
-    "name": "test"
-  }
-}
-	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-		fmt.Fprintf(w, `{}`)
-	})
-}
-
-func mockRemoveServerFromGroupResponse(t *testing.T, serverID string) {
-	url := fmt.Sprintf("/servers/%s/action", serverID)
-	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "removeSecurityGroup": {
-    "name": "test"
-  }
-}
-	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-		fmt.Fprintf(w, `{}`)
-	})
-}
diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/extensions/secgroups/requests.go
index 120dcae..ec8019f 100644
--- a/openstack/compute/v2/extensions/secgroups/requests.go
+++ b/openstack/compute/v2/extensions/secgroups/requests.go
@@ -1,19 +1,14 @@
 package secgroups
 
 import (
-	"errors"
-	"strings"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 func commonList(client *gophercloud.ServiceClient, url string) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
 		return SecurityGroupPage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, url, createPage)
+	})
 }
 
 // List will return a collection of all the security groups for a particular
@@ -32,11 +27,10 @@
 // security groups. It therefore represents the mutable attributes of a
 // security group.
 type GroupOpts struct {
-	// Required - the name of your security group.
-	Name string `json:"name"`
-
-	// Required - the description of your security group.
-	Description string `json:"description"`
+	// the name of your security group.
+	Name string `json:"name" required:"true"`
+	// the description of your security group.
+	Description string `json:"description" required:"true"`
 }
 
 // CreateOpts is the struct responsible for creating a security group.
@@ -47,43 +41,22 @@
 	ToSecGroupCreateMap() (map[string]interface{}, error)
 }
 
-var (
-	errName = errors.New("Name is a required field")
-	errDesc = errors.New("Description is a required field")
-)
-
 // ToSecGroupCreateMap builds the create options into a serializable format.
 func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) {
-	sg := make(map[string]interface{})
-
-	if opts.Name == "" {
-		return sg, errName
-	}
-	if opts.Description == "" {
-		return sg, errDesc
-	}
-
-	sg["name"] = opts.Name
-	sg["description"] = opts.Description
-
-	return map[string]interface{}{"security_group": sg}, nil
+	return gophercloud.BuildRequestBody(opts, "security_group")
 }
 
 // Create will create a new security group.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var result CreateResult
-
-	reqBody, err := opts.ToSecGroupCreateMap()
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToSecGroupCreateMap()
 	if err != nil {
-		result.Err = err
-		return result
+		r.Err = err
+		return
 	}
-
-	_, result.Err = client.Post(rootURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return result
+	return
 }
 
 // UpdateOpts is the struct responsible for updating an existing security group.
@@ -96,78 +69,55 @@
 
 // ToSecGroupUpdateMap builds the update options into a serializable format.
 func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) {
-	sg := make(map[string]interface{})
-
-	if opts.Name == "" {
-		return sg, errName
-	}
-	if opts.Description == "" {
-		return sg, errDesc
-	}
-
-	sg["name"] = opts.Name
-	sg["description"] = opts.Description
-
-	return map[string]interface{}{"security_group": sg}, nil
+	return gophercloud.BuildRequestBody(opts, "security_group")
 }
 
 // Update will modify the mutable properties of a security group, notably its
 // name and description.
-func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var result UpdateResult
-
-	reqBody, err := opts.ToSecGroupUpdateMap()
+func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToSecGroupUpdateMap()
 	if err != nil {
-		result.Err = err
-		return result
+		r.Err = err
+		return
 	}
-
-	_, result.Err = client.Put(resourceURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Put(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return result
+	return
 }
 
 // Get will return details for a particular security group.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(resourceURL(client, id), &result.Body, nil)
-	return result
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(resourceURL(client, id), &r.Body, nil)
+	return
 }
 
 // Delete will permanently delete a security group from the project.
-func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
-	var result gophercloud.ErrResult
-	_, result.Err = client.Delete(resourceURL(client, id), nil)
-	return result
+func Delete(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) {
+	_, r.Err = client.Delete(resourceURL(client, id), nil)
+	return
 }
 
 // CreateRuleOpts represents the configuration for adding a new rule to an
 // existing security group.
 type CreateRuleOpts struct {
-	// Required - the ID of the group that this rule will be added to.
-	ParentGroupID string `json:"parent_group_id"`
-
-	// Required - the lower bound of the port range that will be opened.
+	// the ID of the group that this rule will be added to.
+	ParentGroupID string `json:"parent_group_id" required:"true"`
+	// the lower bound of the port range that will be opened.
 	FromPort int `json:"from_port"`
-
-	// Required - the upper bound of the port range that will be opened.
+	// the upper bound of the port range that will be opened.
 	ToPort int `json:"to_port"`
-
-	// Required - the protocol type that will be allowed, e.g. TCP.
-	IPProtocol string `json:"ip_protocol"`
-
+	// the protocol type that will be allowed, e.g. TCP.
+	IPProtocol string `json:"ip_protocol" required:"true"`
 	// ONLY required if FromGroupID is blank. This represents the IP range that
 	// will be the source of network traffic to your security group. Use
 	// 0.0.0.0/0 to allow all IP addresses.
-	CIDR string `json:"cidr,omitempty"`
-
+	CIDR string `json:"cidr,omitempty" or:"FromGroupID"`
 	// ONLY required if CIDR is blank. This value represents the ID of a group
 	// that forwards traffic to the parent group. So, instead of accepting
 	// network traffic from an entire IP range, you can instead refine the
 	// inbound source by an existing security group.
-	FromGroupID string `json:"group_id,omitempty"`
+	FromGroupID string `json:"group_id,omitempty" or:"CIDR"`
 }
 
 // CreateRuleOptsBuilder builds the create rule options into a serializable format.
@@ -177,63 +127,28 @@
 
 // ToRuleCreateMap builds the create rule options into a serializable format.
 func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
-	rule := make(map[string]interface{})
-
-	if opts.ParentGroupID == "" {
-		return rule, errors.New("A ParentGroupID must be set")
-	}
-	if opts.FromPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
-		return rule, errors.New("A FromPort must be set")
-	}
-	if opts.ToPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
-		return rule, errors.New("A ToPort must be set")
-	}
-	if opts.IPProtocol == "" {
-		return rule, errors.New("A IPProtocol must be set")
-	}
-	if opts.CIDR == "" && opts.FromGroupID == "" {
-		return rule, errors.New("A CIDR or FromGroupID must be set")
-	}
-
-	rule["parent_group_id"] = opts.ParentGroupID
-	rule["from_port"] = opts.FromPort
-	rule["to_port"] = opts.ToPort
-	rule["ip_protocol"] = opts.IPProtocol
-
-	if opts.CIDR != "" {
-		rule["cidr"] = opts.CIDR
-	}
-	if opts.FromGroupID != "" {
-		rule["group_id"] = opts.FromGroupID
-	}
-
-	return map[string]interface{}{"security_group_rule": rule}, nil
+	return gophercloud.BuildRequestBody(opts, "security_group_rule")
 }
 
 // CreateRule will add a new rule to an existing security group (whose ID is
 // specified in CreateRuleOpts). You have the option of controlling inbound
 // traffic from either an IP range (CIDR) or from another security group.
-func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) CreateRuleResult {
-	var result CreateRuleResult
-
-	reqBody, err := opts.ToRuleCreateMap()
+func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) (r CreateRuleResult) {
+	b, err := opts.ToRuleCreateMap()
 	if err != nil {
-		result.Err = err
-		return result
+		r.Err = err
+		return
 	}
-
-	_, result.Err = client.Post(rootRuleURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(rootRuleURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return result
+	return
 }
 
 // DeleteRule will permanently delete a rule from a security group.
-func DeleteRule(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
-	var result gophercloud.ErrResult
-	_, result.Err = client.Delete(resourceRuleURL(client, id), nil)
-	return result
+func DeleteRule(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) {
+	_, r.Err = client.Delete(resourceRuleURL(client, id), nil)
+	return
 }
 
 func actionMap(prefix, groupName string) map[string]map[string]string {
@@ -242,17 +157,15 @@
 	}
 }
 
-// AddServerToGroup will associate a server and a security group, enforcing the
+// AddServer will associate a server and a security group, enforcing the
 // rules of the group on the server.
-func AddServerToGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
-	var result gophercloud.ErrResult
-	_, result.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), &result.Body, nil)
-	return result
+func AddServer(client *gophercloud.ServiceClient, serverID, groupName string) (r gophercloud.ErrResult) {
+	_, r.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), &r.Body, nil)
+	return
 }
 
-// RemoveServerFromGroup will disassociate a server from a security group.
-func RemoveServerFromGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
-	var result gophercloud.ErrResult
-	_, result.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), &result.Body, nil)
-	return result
+// RemoveServer will disassociate a server from a security group.
+func RemoveServer(client *gophercloud.ServiceClient, serverID, groupName string) (r gophercloud.ErrResult) {
+	_, r.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), &r.Body, nil)
+	return
 }
diff --git a/openstack/compute/v2/extensions/secgroups/requests_test.go b/openstack/compute/v2/extensions/secgroups/requests_test.go
deleted file mode 100644
index c9b93a2..0000000
--- a/openstack/compute/v2/extensions/secgroups/requests_test.go
+++ /dev/null
@@ -1,278 +0,0 @@
-package secgroups
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const (
-	serverID = "{serverID}"
-	groupID  = "{groupID}"
-	ruleID   = "{ruleID}"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListGroupsResponse(t)
-
-	count := 0
-
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractSecurityGroups(page)
-		if err != nil {
-			t.Errorf("Failed to extract users: %v", err)
-			return false, err
-		}
-
-		expected := []SecurityGroup{
-			SecurityGroup{
-				ID:          groupID,
-				Description: "default",
-				Name:        "default",
-				Rules:       []Rule{},
-				TenantID:    "openstack",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestListByServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListGroupsByServerResponse(t, serverID)
-
-	count := 0
-
-	err := ListByServer(client.ServiceClient(), serverID).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractSecurityGroups(page)
-		if err != nil {
-			t.Errorf("Failed to extract users: %v", err)
-			return false, err
-		}
-
-		expected := []SecurityGroup{
-			SecurityGroup{
-				ID:          groupID,
-				Description: "default",
-				Name:        "default",
-				Rules:       []Rule{},
-				TenantID:    "openstack",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockCreateGroupResponse(t)
-
-	opts := CreateOpts{
-		Name:        "test",
-		Description: "something",
-	}
-
-	group, err := Create(client.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &SecurityGroup{
-		ID:          groupID,
-		Name:        "test",
-		Description: "something",
-		TenantID:    "openstack",
-		Rules:       []Rule{},
-	}
-	th.AssertDeepEquals(t, expected, group)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockUpdateGroupResponse(t, groupID)
-
-	opts := UpdateOpts{
-		Name:        "new_name",
-		Description: "new_desc",
-	}
-
-	group, err := Update(client.ServiceClient(), groupID, opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &SecurityGroup{
-		ID:          groupID,
-		Name:        "new_name",
-		Description: "something",
-		TenantID:    "openstack",
-		Rules:       []Rule{},
-	}
-	th.AssertDeepEquals(t, expected, group)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetGroupsResponse(t, groupID)
-
-	group, err := Get(client.ServiceClient(), groupID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &SecurityGroup{
-		ID:          groupID,
-		Description: "default",
-		Name:        "default",
-		TenantID:    "openstack",
-		Rules: []Rule{
-			Rule{
-				FromPort:      80,
-				ToPort:        85,
-				IPProtocol:    "TCP",
-				IPRange:       IPRange{CIDR: "0.0.0.0"},
-				Group:         Group{TenantID: "openstack", Name: "default"},
-				ParentGroupID: groupID,
-				ID:            ruleID,
-			},
-		},
-	}
-
-	th.AssertDeepEquals(t, expected, group)
-}
-
-func TestGetNumericID(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	numericGroupID := 12345
-
-	mockGetNumericIDGroupResponse(t, numericGroupID)
-
-	group, err := Get(client.ServiceClient(), "12345").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &SecurityGroup{ID: "12345"}
-	th.AssertDeepEquals(t, expected, group)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteGroupResponse(t, groupID)
-
-	err := Delete(client.ServiceClient(), groupID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestAddRule(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockAddRuleResponse(t)
-
-	opts := CreateRuleOpts{
-		ParentGroupID: groupID,
-		FromPort:      22,
-		ToPort:        22,
-		IPProtocol:    "TCP",
-		CIDR:          "0.0.0.0/0",
-	}
-
-	rule, err := CreateRule(client.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Rule{
-		FromPort:      22,
-		ToPort:        22,
-		Group:         Group{},
-		IPProtocol:    "TCP",
-		ParentGroupID: groupID,
-		IPRange:       IPRange{CIDR: "0.0.0.0/0"},
-		ID:            ruleID,
-	}
-
-	th.AssertDeepEquals(t, expected, rule)
-}
-
-func TestAddRuleICMPZero(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockAddRuleResponseICMPZero(t)
-
-	opts := CreateRuleOpts{
-		ParentGroupID: groupID,
-		FromPort:      0,
-		ToPort:        0,
-		IPProtocol:    "ICMP",
-		CIDR:          "0.0.0.0/0",
-	}
-
-	rule, err := CreateRule(client.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Rule{
-		FromPort:      0,
-		ToPort:        0,
-		Group:         Group{},
-		IPProtocol:    "ICMP",
-		ParentGroupID: groupID,
-		IPRange:       IPRange{CIDR: "0.0.0.0/0"},
-		ID:            ruleID,
-	}
-
-	th.AssertDeepEquals(t, expected, rule)
-}
-
-func TestDeleteRule(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteRuleResponse(t, ruleID)
-
-	err := DeleteRule(client.ServiceClient(), ruleID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestAddServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockAddServerToGroupResponse(t, serverID)
-
-	err := AddServerToGroup(client.ServiceClient(), serverID, "test").ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestRemoveServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockRemoveServerFromGroupResponse(t, serverID)
-
-	err := RemoveServerFromGroup(client.ServiceClient(), serverID, "test").ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/compute/v2/extensions/secgroups/results.go b/openstack/compute/v2/extensions/secgroups/results.go
index 478c5dc..764f580 100644
--- a/openstack/compute/v2/extensions/secgroups/results.go
+++ b/openstack/compute/v2/extensions/secgroups/results.go
@@ -1,10 +1,8 @@
 package secgroups
 
 import (
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // SecurityGroup represents a security group.
@@ -24,7 +22,7 @@
 	Rules []Rule
 
 	// The ID of the tenant to which this security group belongs.
-	TenantID string `mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 }
 
 // Rule represents a security group rule, a policy which determines how a
@@ -36,19 +34,19 @@
 	ID string
 
 	// The lower bound of the port range which this security group should open up
-	FromPort int `mapstructure:"from_port"`
+	FromPort int `json:"from_port"`
 
 	// The upper bound of the port range which this security group should open up
-	ToPort int `mapstructure:"to_port"`
+	ToPort int `json:"to_port"`
 
 	// The IP protocol (e.g. TCP) which the security group accepts
-	IPProtocol string `mapstructure:"ip_protocol"`
+	IPProtocol string `json:"ip_protocol"`
 
 	// The CIDR IP range whose traffic can be received
-	IPRange IPRange `mapstructure:"ip_range"`
+	IPRange IPRange `json:"ip_range"`
 
 	// The security group ID to which this rule belongs
-	ParentGroupID string `mapstructure:"parent_group_id"`
+	ParentGroupID string `json:"parent_group_id"`
 
 	// Not documented.
 	Group Group
@@ -62,7 +60,7 @@
 
 // Group represents a group.
 type Group struct {
-	TenantID string `mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 	Name     string
 }
 
@@ -74,22 +72,16 @@
 // IsEmpty determines whether or not a page of Security Groups contains any results.
 func (page SecurityGroupPage) IsEmpty() (bool, error) {
 	users, err := ExtractSecurityGroups(page)
-	if err != nil {
-		return false, err
-	}
-	return len(users) == 0, nil
+	return len(users) == 0, err
 }
 
 // ExtractSecurityGroups returns a slice of SecurityGroups contained in a single page of results.
-func ExtractSecurityGroups(page pagination.Page) ([]SecurityGroup, error) {
-	casted := page.(SecurityGroupPage).Body
-	var response struct {
-		SecurityGroups []SecurityGroup `mapstructure:"security_groups"`
+func ExtractSecurityGroups(r pagination.Page) ([]SecurityGroup, error) {
+	var s struct {
+		SecurityGroups []SecurityGroup `json:"security_groups"`
 	}
-
-	err := mapstructure.WeakDecode(casted, &response)
-
-	return response.SecurityGroups, err
+	err := (r.(SecurityGroupPage)).ExtractInto(&s)
+	return s.SecurityGroups, err
 }
 
 type commonResult struct {
@@ -113,17 +105,11 @@
 
 // Extract will extract a SecurityGroup struct from most responses.
 func (r commonResult) Extract() (*SecurityGroup, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		SecurityGroup *SecurityGroup `json:"security_group"`
 	}
-
-	var response struct {
-		SecurityGroup SecurityGroup `mapstructure:"security_group"`
-	}
-
-	err := mapstructure.WeakDecode(r.Body, &response)
-
-	return &response.SecurityGroup, err
+	err := r.ExtractInto(&s)
+	return s.SecurityGroup, err
 }
 
 // CreateRuleResult represents the result when adding rules to a security group.
@@ -133,15 +119,9 @@
 
 // Extract will extract a Rule struct from a CreateRuleResult.
 func (r CreateRuleResult) Extract() (*Rule, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Rule *Rule `json:"security_group_rule"`
 	}
-
-	var response struct {
-		Rule Rule `mapstructure:"security_group_rule"`
-	}
-
-	err := mapstructure.WeakDecode(r.Body, &response)
-
-	return &response.Rule, err
+	err := r.ExtractInto(&s)
+	return s.Rule, err
 }
diff --git a/openstack/compute/v2/extensions/secgroups/testing/doc.go b/openstack/compute/v2/extensions/secgroups/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/secgroups/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/secgroups/testing/fixtures.go b/openstack/compute/v2/extensions/secgroups/testing/fixtures.go
new file mode 100644
index 0000000..8a83ca8
--- /dev/null
+++ b/openstack/compute/v2/extensions/secgroups/testing/fixtures.go
@@ -0,0 +1,303 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+const rootPath = "/os-security-groups"
+
+const listGroupsJSON = `
+{
+  "security_groups": [
+    {
+      "description": "default",
+      "id": "{groupID}",
+      "name": "default",
+      "rules": [],
+      "tenant_id": "openstack"
+    }
+  ]
+}
+`
+
+func mockListGroupsResponse(t *testing.T) {
+	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, listGroupsJSON)
+	})
+}
+
+func mockListGroupsByServerResponse(t *testing.T, serverID string) {
+	url := fmt.Sprintf("/servers/%s%s", serverID, rootPath)
+	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, listGroupsJSON)
+	})
+}
+
+func mockCreateGroupResponse(t *testing.T) {
+	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		th.TestJSONRequest(t, r, `
+{
+  "security_group": {
+    "name": "test",
+    "description": "something"
+  }
+}
+	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+  "security_group": {
+    "description": "something",
+    "id": "{groupID}",
+    "name": "test",
+    "rules": [],
+    "tenant_id": "openstack"
+  }
+}
+`)
+	})
+}
+
+func mockUpdateGroupResponse(t *testing.T, groupID string) {
+	url := fmt.Sprintf("%s/%s", rootPath, groupID)
+	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		th.TestJSONRequest(t, r, `
+{
+  "security_group": {
+    "name": "new_name",
+		"description": "new_desc"
+  }
+}
+	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+  "security_group": {
+    "description": "something",
+    "id": "{groupID}",
+    "name": "new_name",
+    "rules": [],
+    "tenant_id": "openstack"
+  }
+}
+`)
+	})
+}
+
+func mockGetGroupsResponse(t *testing.T, groupID string) {
+	url := fmt.Sprintf("%s/%s", rootPath, groupID)
+	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+  "security_group": {
+    "description": "default",
+    "id": "{groupID}",
+    "name": "default",
+    "rules": [
+      {
+        "from_port": 80,
+        "group": {
+          "tenant_id": "openstack",
+          "name": "default"
+        },
+        "ip_protocol": "TCP",
+        "to_port": 85,
+        "parent_group_id": "{groupID}",
+        "ip_range": {
+						"cidr": "0.0.0.0"
+				},
+        "id": "{ruleID}"
+      }
+    ],
+    "tenant_id": "openstack"
+  }
+}
+			`)
+	})
+}
+
+func mockGetNumericIDGroupResponse(t *testing.T, groupID int) {
+	url := fmt.Sprintf("%s/%d", rootPath, groupID)
+	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+	"security_group": {
+		"id": "12345"
+	}
+}
+			`)
+	})
+}
+
+func mockDeleteGroupResponse(t *testing.T, groupID string) {
+	url := fmt.Sprintf("%s/%s", rootPath, groupID)
+	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+func mockAddRuleResponse(t *testing.T) {
+	th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		th.TestJSONRequest(t, r, `
+{
+  "security_group_rule": {
+    "from_port": 22,
+    "ip_protocol": "TCP",
+    "to_port": 22,
+    "parent_group_id": "{groupID}",
+    "cidr": "0.0.0.0/0"
+  }
+}	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+  "security_group_rule": {
+    "from_port": 22,
+    "group": {},
+    "ip_protocol": "TCP",
+    "to_port": 22,
+    "parent_group_id": "{groupID}",
+    "ip_range": {
+      "cidr": "0.0.0.0/0"
+    },
+    "id": "{ruleID}"
+  }
+}`)
+	})
+}
+
+func mockAddRuleResponseICMPZero(t *testing.T) {
+	th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		th.TestJSONRequest(t, r, `
+{
+  "security_group_rule": {
+    "from_port": 0,
+    "ip_protocol": "ICMP",
+    "to_port": 0,
+    "parent_group_id": "{groupID}",
+    "cidr": "0.0.0.0/0"
+  }
+}	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+  "security_group_rule": {
+    "from_port": 0,
+    "group": {},
+    "ip_protocol": "ICMP",
+    "to_port": 0,
+    "parent_group_id": "{groupID}",
+    "ip_range": {
+      "cidr": "0.0.0.0/0"
+    },
+    "id": "{ruleID}"
+  }
+}`)
+	})
+}
+
+func mockDeleteRuleResponse(t *testing.T, ruleID string) {
+	url := fmt.Sprintf("/os-security-group-rules/%s", ruleID)
+	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+func mockAddServerToGroupResponse(t *testing.T, serverID string) {
+	url := fmt.Sprintf("/servers/%s/action", serverID)
+	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		th.TestJSONRequest(t, r, `
+{
+  "addSecurityGroup": {
+    "name": "test"
+  }
+}
+	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusAccepted)
+		fmt.Fprintf(w, `{}`)
+	})
+}
+
+func mockRemoveServerFromGroupResponse(t *testing.T, serverID string) {
+	url := fmt.Sprintf("/servers/%s/action", serverID)
+	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		th.TestJSONRequest(t, r, `
+{
+  "removeSecurityGroup": {
+    "name": "test"
+  }
+}
+	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusAccepted)
+		fmt.Fprintf(w, `{}`)
+	})
+}
diff --git a/openstack/compute/v2/extensions/secgroups/testing/requests_test.go b/openstack/compute/v2/extensions/secgroups/testing/requests_test.go
new file mode 100644
index 0000000..b7ffa20
--- /dev/null
+++ b/openstack/compute/v2/extensions/secgroups/testing/requests_test.go
@@ -0,0 +1,279 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+const (
+	serverID = "{serverID}"
+	groupID  = "{groupID}"
+	ruleID   = "{ruleID}"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockListGroupsResponse(t)
+
+	count := 0
+
+	err := secgroups.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := secgroups.ExtractSecurityGroups(page)
+		if err != nil {
+			t.Errorf("Failed to extract users: %v", err)
+			return false, err
+		}
+
+		expected := []secgroups.SecurityGroup{
+			{
+				ID:          groupID,
+				Description: "default",
+				Name:        "default",
+				Rules:       []secgroups.Rule{},
+				TenantID:    "openstack",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, count)
+}
+
+func TestListByServer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockListGroupsByServerResponse(t, serverID)
+
+	count := 0
+
+	err := secgroups.ListByServer(client.ServiceClient(), serverID).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := secgroups.ExtractSecurityGroups(page)
+		if err != nil {
+			t.Errorf("Failed to extract users: %v", err)
+			return false, err
+		}
+
+		expected := []secgroups.SecurityGroup{
+			{
+				ID:          groupID,
+				Description: "default",
+				Name:        "default",
+				Rules:       []secgroups.Rule{},
+				TenantID:    "openstack",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, count)
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockCreateGroupResponse(t)
+
+	opts := secgroups.CreateOpts{
+		Name:        "test",
+		Description: "something",
+	}
+
+	group, err := secgroups.Create(client.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &secgroups.SecurityGroup{
+		ID:          groupID,
+		Name:        "test",
+		Description: "something",
+		TenantID:    "openstack",
+		Rules:       []secgroups.Rule{},
+	}
+	th.AssertDeepEquals(t, expected, group)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockUpdateGroupResponse(t, groupID)
+
+	opts := secgroups.UpdateOpts{
+		Name:        "new_name",
+		Description: "new_desc",
+	}
+
+	group, err := secgroups.Update(client.ServiceClient(), groupID, opts).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &secgroups.SecurityGroup{
+		ID:          groupID,
+		Name:        "new_name",
+		Description: "something",
+		TenantID:    "openstack",
+		Rules:       []secgroups.Rule{},
+	}
+	th.AssertDeepEquals(t, expected, group)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockGetGroupsResponse(t, groupID)
+
+	group, err := secgroups.Get(client.ServiceClient(), groupID).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &secgroups.SecurityGroup{
+		ID:          groupID,
+		Description: "default",
+		Name:        "default",
+		TenantID:    "openstack",
+		Rules: []secgroups.Rule{
+			{
+				FromPort:      80,
+				ToPort:        85,
+				IPProtocol:    "TCP",
+				IPRange:       secgroups.IPRange{CIDR: "0.0.0.0"},
+				Group:         secgroups.Group{TenantID: "openstack", Name: "default"},
+				ParentGroupID: groupID,
+				ID:            ruleID,
+			},
+		},
+	}
+
+	th.AssertDeepEquals(t, expected, group)
+}
+
+func TestGetNumericID(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	numericGroupID := 12345
+
+	mockGetNumericIDGroupResponse(t, numericGroupID)
+
+	group, err := secgroups.Get(client.ServiceClient(), "12345").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &secgroups.SecurityGroup{ID: "12345"}
+	th.AssertDeepEquals(t, expected, group)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockDeleteGroupResponse(t, groupID)
+
+	err := secgroups.Delete(client.ServiceClient(), groupID).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestAddRule(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockAddRuleResponse(t)
+
+	opts := secgroups.CreateRuleOpts{
+		ParentGroupID: groupID,
+		FromPort:      22,
+		ToPort:        22,
+		IPProtocol:    "TCP",
+		CIDR:          "0.0.0.0/0",
+	}
+
+	rule, err := secgroups.CreateRule(client.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &secgroups.Rule{
+		FromPort:      22,
+		ToPort:        22,
+		Group:         secgroups.Group{},
+		IPProtocol:    "TCP",
+		ParentGroupID: groupID,
+		IPRange:       secgroups.IPRange{CIDR: "0.0.0.0/0"},
+		ID:            ruleID,
+	}
+
+	th.AssertDeepEquals(t, expected, rule)
+}
+
+func TestAddRuleICMPZero(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockAddRuleResponseICMPZero(t)
+
+	opts := secgroups.CreateRuleOpts{
+		ParentGroupID: groupID,
+		FromPort:      0,
+		ToPort:        0,
+		IPProtocol:    "ICMP",
+		CIDR:          "0.0.0.0/0",
+	}
+
+	rule, err := secgroups.CreateRule(client.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &secgroups.Rule{
+		FromPort:      0,
+		ToPort:        0,
+		Group:         secgroups.Group{},
+		IPProtocol:    "ICMP",
+		ParentGroupID: groupID,
+		IPRange:       secgroups.IPRange{CIDR: "0.0.0.0/0"},
+		ID:            ruleID,
+	}
+
+	th.AssertDeepEquals(t, expected, rule)
+}
+
+func TestDeleteRule(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockDeleteRuleResponse(t, ruleID)
+
+	err := secgroups.DeleteRule(client.ServiceClient(), ruleID).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestAddServer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockAddServerToGroupResponse(t, serverID)
+
+	err := secgroups.AddServer(client.ServiceClient(), serverID, "test").ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestRemoveServer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockRemoveServerFromGroupResponse(t, serverID)
+
+	err := secgroups.RemoveServer(client.ServiceClient(), serverID, "test").ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/compute/v2/extensions/secgroups/urls.go b/openstack/compute/v2/extensions/secgroups/urls.go
index dc53fbf..d99746c 100644
--- a/openstack/compute/v2/extensions/secgroups/urls.go
+++ b/openstack/compute/v2/extensions/secgroups/urls.go
@@ -1,6 +1,6 @@
 package secgroups
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	secgrouppath = "os-security-groups"
diff --git a/openstack/compute/v2/extensions/servergroups/fixtures.go b/openstack/compute/v2/extensions/servergroups/fixtures.go
deleted file mode 100644
index 133fd85..0000000
--- a/openstack/compute/v2/extensions/servergroups/fixtures.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// +build fixtures
-
-package servergroups
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ListOutput is a sample response to a List call.
-const ListOutput = `
-{
-    "server_groups": [
-        {
-            "id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
-            "name": "test",
-            "policies": [
-                "anti-affinity"
-            ],
-            "members": [],
-            "metadata": {}
-        },
-        {
-            "id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-            "name": "test2",
-            "policies": [
-                "affinity"
-            ],
-            "members": [],
-            "metadata": {}
-        }
-    ]
-}
-`
-
-// GetOutput is a sample response to a Get call.
-const GetOutput = `
-{
-    "server_group": {
-        "id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
-        "name": "test",
-        "policies": [
-            "anti-affinity"
-        ],
-        "members": [],
-        "metadata": {}
-    }
-}
-`
-
-// CreateOutput is a sample response to a Post call
-const CreateOutput = `
-{
-    "server_group": {
-        "id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
-        "name": "test",
-        "policies": [
-            "anti-affinity"
-        ],
-        "members": [],
-        "metadata": {}
-    }
-}
-`
-
-// FirstServerGroup is the first result in ListOutput.
-var FirstServerGroup = ServerGroup{
-	ID:   "616fb98f-46ca-475e-917e-2563e5a8cd19",
-	Name: "test",
-	Policies: []string{
-		"anti-affinity",
-	},
-	Members:  []string{},
-	Metadata: map[string]interface{}{},
-}
-
-// SecondServerGroup is the second result in ListOutput.
-var SecondServerGroup = ServerGroup{
-	ID:   "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-	Name: "test2",
-	Policies: []string{
-		"affinity",
-	},
-	Members:  []string{},
-	Metadata: map[string]interface{}{},
-}
-
-// ExpectedServerGroupSlice is the slice of results that should be parsed
-// from ListOutput, in the expected order.
-var ExpectedServerGroupSlice = []ServerGroup{FirstServerGroup, SecondServerGroup}
-
-// CreatedServerGroup is the parsed result from CreateOutput.
-var CreatedServerGroup = ServerGroup{
-	ID:   "616fb98f-46ca-475e-917e-2563e5a8cd19",
-	Name: "test",
-	Policies: []string{
-		"anti-affinity",
-	},
-	Members:  []string{},
-	Metadata: map[string]interface{}{},
-}
-
-// HandleListSuccessfully configures the test server to respond to a List request.
-func HandleListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-server-groups", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, ListOutput)
-	})
-}
-
-// HandleGetSuccessfully configures the test server to respond to a Get request
-// for an existing server group
-func HandleGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-server-groups/4d8c3732-a248-40ed-bebc-539a6ffd25c0", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, GetOutput)
-	})
-}
-
-// HandleCreateSuccessfully configures the test server to respond to a Create request
-// for a new server group
-func HandleCreateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-server-groups", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `
-{
-    "server_group": {
-        "name": "test",
-        "policies": [
-            "anti-affinity"
-        ]
-    }
-}
-`)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, CreateOutput)
-	})
-}
-
-// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
-// an existing server group
-func HandleDeleteSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-server-groups/616fb98f-46ca-475e-917e-2563e5a8cd19", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/openstack/compute/v2/extensions/servergroups/requests.go b/openstack/compute/v2/extensions/servergroups/requests.go
index 1597b43..ee98837 100644
--- a/openstack/compute/v2/extensions/servergroups/requests.go
+++ b/openstack/compute/v2/extensions/servergroups/requests.go
@@ -1,16 +1,14 @@
 package servergroups
 
 import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // List returns a Pager that allows you to iterate over a collection of ServerGroups.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
 	return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
-		return ServerGroupsPage{pagination.SinglePageBase(r)}
+		return ServerGroupPage{pagination.SinglePageBase(r)}
 	})
 }
 
@@ -23,55 +21,37 @@
 // CreateOpts specifies a Server Group allocation request
 type CreateOpts struct {
 	// Name is the name of the server group
-	Name string
-
+	Name string `json:"name" required:"true"`
 	// Policies are the server group policies
-	Policies []string
+	Policies []string `json:"policies" required:"true"`
 }
 
 // ToServerGroupCreateMap constructs a request body from CreateOpts.
 func (opts CreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) {
-	if opts.Name == "" {
-		return nil, errors.New("Missing field required for server group creation: Name")
-	}
-
-	if len(opts.Policies) < 1 {
-		return nil, errors.New("Missing field required for server group creation: Policies")
-	}
-
-	serverGroup := make(map[string]interface{})
-	serverGroup["name"] = opts.Name
-	serverGroup["policies"] = opts.Policies
-
-	return map[string]interface{}{"server_group": serverGroup}, nil
+	return gophercloud.BuildRequestBody(opts, "server_group")
 }
 
 // Create requests the creation of a new Server Group
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToServerGroupCreateMap()
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToServerGroupCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // Get returns data about a previously created ServerGroup.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return
 }
 
 // Delete requests the deletion of a previously allocated ServerGroup.
-func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, id), nil)
-	return res
+func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = client.Delete(deleteURL(client, id), nil)
+	return
 }
diff --git a/openstack/compute/v2/extensions/servergroups/requests_test.go b/openstack/compute/v2/extensions/servergroups/requests_test.go
deleted file mode 100644
index 07fec51..0000000
--- a/openstack/compute/v2/extensions/servergroups/requests_test.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package servergroups
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListSuccessfully(t)
-
-	count := 0
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractServerGroups(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, ExpectedServerGroupSlice, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreateSuccessfully(t)
-
-	actual, err := Create(client.ServiceClient(), CreateOpts{
-		Name:     "test",
-		Policies: []string{"anti-affinity"},
-	}).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &CreatedServerGroup, actual)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t)
-
-	actual, err := Get(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &FirstServerGroup, actual)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDeleteSuccessfully(t)
-
-	err := Delete(client.ServiceClient(), "616fb98f-46ca-475e-917e-2563e5a8cd19").ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/compute/v2/extensions/servergroups/results.go b/openstack/compute/v2/extensions/servergroups/results.go
index d74ee5d..ab49b35 100644
--- a/openstack/compute/v2/extensions/servergroups/results.go
+++ b/openstack/compute/v2/extensions/servergroups/results.go
@@ -1,52 +1,48 @@
 package servergroups
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // A ServerGroup creates a policy for instance placement in the cloud
 type ServerGroup struct {
 	// ID is the unique ID of the Server Group.
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Name is the common name of the server group.
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 
 	// Polices are the group policies.
-	Policies []string `mapstructure:"policies"`
+	Policies []string `json:"policies"`
 
 	// Members are the members of the server group.
-	Members []string `mapstructure:"members"`
+	Members []string `json:"members"`
 
 	// Metadata includes a list of all user-specified key-value pairs attached to the Server Group.
 	Metadata map[string]interface{}
 }
 
-// ServerGroupsPage stores a single, only page of ServerGroups
+// ServerGroupPage stores a single, only page of ServerGroups
 // results from a List call.
-type ServerGroupsPage struct {
+type ServerGroupPage struct {
 	pagination.SinglePageBase
 }
 
 // IsEmpty determines whether or not a ServerGroupsPage is empty.
-func (page ServerGroupsPage) IsEmpty() (bool, error) {
+func (page ServerGroupPage) IsEmpty() (bool, error) {
 	va, err := ExtractServerGroups(page)
 	return len(va) == 0, err
 }
 
 // ExtractServerGroups interprets a page of results as a slice of
 // ServerGroups.
-func ExtractServerGroups(page pagination.Page) ([]ServerGroup, error) {
-	casted := page.(ServerGroupsPage).Body
-	var response struct {
-		ServerGroups []ServerGroup `mapstructure:"server_groups"`
+func ExtractServerGroups(r pagination.Page) ([]ServerGroup, error) {
+	var s struct {
+		ServerGroups []ServerGroup `json:"server_groups"`
 	}
-
-	err := mapstructure.WeakDecode(casted, &response)
-
-	return response.ServerGroups, err
+	err := (r.(ServerGroupPage)).ExtractInto(&s)
+	return s.ServerGroups, err
 }
 
 type ServerGroupResult struct {
@@ -56,16 +52,11 @@
 // Extract is a method that attempts to interpret any Server Group resource
 // response as a ServerGroup struct.
 func (r ServerGroupResult) Extract() (*ServerGroup, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		ServerGroup *ServerGroup `json:"server_group"`
 	}
-
-	var res struct {
-		ServerGroup *ServerGroup `json:"server_group" mapstructure:"server_group"`
-	}
-
-	err := mapstructure.WeakDecode(r.Body, &res)
-	return res.ServerGroup, err
+	err := r.ExtractInto(&s)
+	return s.ServerGroup, err
 }
 
 // CreateResult is the response from a Create operation. Call its Extract method to interpret it
diff --git a/openstack/compute/v2/extensions/servergroups/testing/doc.go b/openstack/compute/v2/extensions/servergroups/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/servergroups/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/servergroups/testing/fixtures.go b/openstack/compute/v2/extensions/servergroups/testing/fixtures.go
new file mode 100644
index 0000000..b53757a
--- /dev/null
+++ b/openstack/compute/v2/extensions/servergroups/testing/fixtures.go
@@ -0,0 +1,160 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ListOutput is a sample response to a List call.
+const ListOutput = `
+{
+    "server_groups": [
+        {
+            "id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
+            "name": "test",
+            "policies": [
+                "anti-affinity"
+            ],
+            "members": [],
+            "metadata": {}
+        },
+        {
+            "id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
+            "name": "test2",
+            "policies": [
+                "affinity"
+            ],
+            "members": [],
+            "metadata": {}
+        }
+    ]
+}
+`
+
+// GetOutput is a sample response to a Get call.
+const GetOutput = `
+{
+    "server_group": {
+        "id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
+        "name": "test",
+        "policies": [
+            "anti-affinity"
+        ],
+        "members": [],
+        "metadata": {}
+    }
+}
+`
+
+// CreateOutput is a sample response to a Post call
+const CreateOutput = `
+{
+    "server_group": {
+        "id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
+        "name": "test",
+        "policies": [
+            "anti-affinity"
+        ],
+        "members": [],
+        "metadata": {}
+    }
+}
+`
+
+// FirstServerGroup is the first result in ListOutput.
+var FirstServerGroup = servergroups.ServerGroup{
+	ID:   "616fb98f-46ca-475e-917e-2563e5a8cd19",
+	Name: "test",
+	Policies: []string{
+		"anti-affinity",
+	},
+	Members:  []string{},
+	Metadata: map[string]interface{}{},
+}
+
+// SecondServerGroup is the second result in ListOutput.
+var SecondServerGroup = servergroups.ServerGroup{
+	ID:   "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
+	Name: "test2",
+	Policies: []string{
+		"affinity",
+	},
+	Members:  []string{},
+	Metadata: map[string]interface{}{},
+}
+
+// ExpectedServerGroupSlice is the slice of results that should be parsed
+// from ListOutput, in the expected order.
+var ExpectedServerGroupSlice = []servergroups.ServerGroup{FirstServerGroup, SecondServerGroup}
+
+// CreatedServerGroup is the parsed result from CreateOutput.
+var CreatedServerGroup = servergroups.ServerGroup{
+	ID:   "616fb98f-46ca-475e-917e-2563e5a8cd19",
+	Name: "test",
+	Policies: []string{
+		"anti-affinity",
+	},
+	Members:  []string{},
+	Metadata: map[string]interface{}{},
+}
+
+// HandleListSuccessfully configures the test server to respond to a List request.
+func HandleListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-server-groups", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, ListOutput)
+	})
+}
+
+// HandleGetSuccessfully configures the test server to respond to a Get request
+// for an existing server group
+func HandleGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-server-groups/4d8c3732-a248-40ed-bebc-539a6ffd25c0", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, GetOutput)
+	})
+}
+
+// HandleCreateSuccessfully configures the test server to respond to a Create request
+// for a new server group
+func HandleCreateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-server-groups", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `
+{
+    "server_group": {
+        "name": "test",
+        "policies": [
+            "anti-affinity"
+        ]
+    }
+}
+`)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, CreateOutput)
+	})
+}
+
+// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
+// an existing server group
+func HandleDeleteSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-server-groups/616fb98f-46ca-475e-917e-2563e5a8cd19", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
diff --git a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go
new file mode 100644
index 0000000..d86fa56
--- /dev/null
+++ b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go
@@ -0,0 +1,60 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListSuccessfully(t)
+
+	count := 0
+	err := servergroups.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := servergroups.ExtractServerGroups(page)
+		th.AssertNoErr(t, err)
+		th.CheckDeepEquals(t, ExpectedServerGroupSlice, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, 1, count)
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleCreateSuccessfully(t)
+
+	actual, err := servergroups.Create(client.ServiceClient(), servergroups.CreateOpts{
+		Name:     "test",
+		Policies: []string{"anti-affinity"},
+	}).Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &CreatedServerGroup, actual)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t)
+
+	actual, err := servergroups.Get(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0").Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &FirstServerGroup, actual)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDeleteSuccessfully(t)
+
+	err := servergroups.Delete(client.ServiceClient(), "616fb98f-46ca-475e-917e-2563e5a8cd19").ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/compute/v2/extensions/servergroups/urls.go b/openstack/compute/v2/extensions/servergroups/urls.go
index 074a16c..9a1f99b 100644
--- a/openstack/compute/v2/extensions/servergroups/urls.go
+++ b/openstack/compute/v2/extensions/servergroups/urls.go
@@ -1,6 +1,6 @@
 package servergroups
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const resourcePath = "os-server-groups"
 
diff --git a/openstack/compute/v2/extensions/servergroups/urls_test.go b/openstack/compute/v2/extensions/servergroups/urls_test.go
deleted file mode 100644
index bff4dfc..0000000
--- a/openstack/compute/v2/extensions/servergroups/urls_test.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package servergroups
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-server-groups", listURL(c))
-}
-
-func TestCreateURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-server-groups", createURL(c))
-}
-
-func TestGetURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	id := "1"
-
-	th.CheckEquals(t, c.Endpoint+"os-server-groups/"+id, getURL(c, id))
-}
-
-func TestDeleteURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	id := "1"
-
-	th.CheckEquals(t, c.Endpoint+"os-server-groups/"+id, deleteURL(c, id))
-}
diff --git a/openstack/compute/v2/extensions/startstop/fixtures.go b/openstack/compute/v2/extensions/startstop/fixtures.go
deleted file mode 100644
index e2c33fe..0000000
--- a/openstack/compute/v2/extensions/startstop/fixtures.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// +build fixtures
-
-package startstop
-
-import (
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func mockStartServerResponse(t *testing.T, id string) {
-	th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{"os-start": null}`)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockStopServerResponse(t *testing.T, id string) {
-	th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{"os-stop": null}`)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/openstack/compute/v2/extensions/startstop/requests.go b/openstack/compute/v2/extensions/startstop/requests.go
index 0e090e6..1d8a593 100644
--- a/openstack/compute/v2/extensions/startstop/requests.go
+++ b/openstack/compute/v2/extensions/startstop/requests.go
@@ -1,23 +1,19 @@
 package startstop
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func actionURL(client *gophercloud.ServiceClient, id string) string {
 	return client.ServiceURL("servers", id, "action")
 }
 
 // Start is the operation responsible for starting a Compute server.
-func Start(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
-	var res gophercloud.ErrResult
-	reqBody := map[string]interface{}{"os-start": nil}
-	_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
-	return res
+func Start(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) {
+	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil)
+	return
 }
 
 // Stop is the operation responsible for stopping a Compute server.
-func Stop(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
-	var res gophercloud.ErrResult
-	reqBody := map[string]interface{}{"os-stop": nil}
-	_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
-	return res
+func Stop(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) {
+	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil)
+	return
 }
diff --git a/openstack/compute/v2/extensions/startstop/requests_test.go b/openstack/compute/v2/extensions/startstop/requests_test.go
deleted file mode 100644
index 97a121b..0000000
--- a/openstack/compute/v2/extensions/startstop/requests_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package startstop
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const serverID = "{serverId}"
-
-func TestStart(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockStartServerResponse(t, serverID)
-
-	err := Start(client.ServiceClient(), serverID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestStop(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockStopServerResponse(t, serverID)
-
-	err := Stop(client.ServiceClient(), serverID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/compute/v2/extensions/startstop/testing/doc.go b/openstack/compute/v2/extensions/startstop/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/startstop/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/startstop/testing/fixtures.go b/openstack/compute/v2/extensions/startstop/testing/fixtures.go
new file mode 100644
index 0000000..1086b0e
--- /dev/null
+++ b/openstack/compute/v2/extensions/startstop/testing/fixtures.go
@@ -0,0 +1,27 @@
+package testing
+
+import (
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func mockStartServerResponse(t *testing.T, id string) {
+	th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{"os-start": null}`)
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+func mockStopServerResponse(t *testing.T, id string) {
+	th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{"os-stop": null}`)
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
diff --git a/openstack/compute/v2/extensions/startstop/testing/requests_test.go b/openstack/compute/v2/extensions/startstop/testing/requests_test.go
new file mode 100644
index 0000000..be45bf5
--- /dev/null
+++ b/openstack/compute/v2/extensions/startstop/testing/requests_test.go
@@ -0,0 +1,31 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+const serverID = "{serverId}"
+
+func TestStart(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockStartServerResponse(t, serverID)
+
+	err := startstop.Start(client.ServiceClient(), serverID).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestStop(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockStopServerResponse(t, serverID)
+
+	err := startstop.Stop(client.ServiceClient(), serverID).ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/compute/v2/extensions/tenantnetworks/fixtures.go b/openstack/compute/v2/extensions/tenantnetworks/fixtures.go
deleted file mode 100644
index 0cfa72a..0000000
--- a/openstack/compute/v2/extensions/tenantnetworks/fixtures.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// +build fixtures
-
-package tenantnetworks
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ListOutput is a sample response to a List call.
-const ListOutput = `
-{
-    "networks": [
-        {
-            "cidr": "10.0.0.0/29",
-            "id": "20c8acc0-f747-4d71-a389-46d078ebf047",
-            "label": "mynet_0"
-        },
-        {
-            "cidr": "10.0.0.10/29",
-            "id": "20c8acc0-f747-4d71-a389-46d078ebf000",
-            "label": "mynet_1"
-        }
-    ]
-}
-`
-
-// GetOutput is a sample response to a Get call.
-const GetOutput = `
-{
-    "network": {
-			"cidr": "10.0.0.10/29",
-			"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
-			"label": "mynet_1"
-		}
-}
-`
-
-// FirstNetwork is the first result in ListOutput.
-var nilTime time.Time
-var FirstNetwork = Network{
-	CIDR: "10.0.0.0/29",
-	ID:   "20c8acc0-f747-4d71-a389-46d078ebf047",
-	Name: "mynet_0",
-}
-
-// SecondNetwork is the second result in ListOutput.
-var SecondNetwork = Network{
-	CIDR: "10.0.0.10/29",
-	ID:   "20c8acc0-f747-4d71-a389-46d078ebf000",
-	Name: "mynet_1",
-}
-
-// ExpectedNetworkSlice is the slice of results that should be parsed
-// from ListOutput, in the expected order.
-var ExpectedNetworkSlice = []Network{FirstNetwork, SecondNetwork}
-
-// HandleListSuccessfully configures the test server to respond to a List request.
-func HandleListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-tenant-networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, ListOutput)
-	})
-}
-
-// HandleGetSuccessfully configures the test server to respond to a Get request
-// for an existing network.
-func HandleGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/os-tenant-networks/20c8acc0-f747-4d71-a389-46d078ebf000", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, GetOutput)
-	})
-}
diff --git a/openstack/compute/v2/extensions/tenantnetworks/requests.go b/openstack/compute/v2/extensions/tenantnetworks/requests.go
index 3ec13d3..82836d4 100644
--- a/openstack/compute/v2/extensions/tenantnetworks/requests.go
+++ b/openstack/compute/v2/extensions/tenantnetworks/requests.go
@@ -1,22 +1,19 @@
 package tenantnetworks
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // List returns a Pager that allows you to iterate over a collection of Network.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	url := listURL(client)
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
 		return NetworkPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, url, createPage)
+	})
 }
 
 // Get returns data about a previously created Network.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return
 }
diff --git a/openstack/compute/v2/extensions/tenantnetworks/requests_test.go b/openstack/compute/v2/extensions/tenantnetworks/requests_test.go
deleted file mode 100644
index fc4ee4f..0000000
--- a/openstack/compute/v2/extensions/tenantnetworks/requests_test.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package tenantnetworks
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListSuccessfully(t)
-
-	count := 0
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNetworks(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, ExpectedNetworkSlice, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t)
-
-	actual, err := Get(client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &SecondNetwork, actual)
-}
diff --git a/openstack/compute/v2/extensions/tenantnetworks/results.go b/openstack/compute/v2/extensions/tenantnetworks/results.go
index 8050092..88cbc80 100644
--- a/openstack/compute/v2/extensions/tenantnetworks/results.go
+++ b/openstack/compute/v2/extensions/tenantnetworks/results.go
@@ -1,21 +1,20 @@
 package tenantnetworks
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // A Network represents a nova-network that an instance communicates on
 type Network struct {
 	// CIDR is the IPv4 subnet.
-	CIDR string `mapstructure:"cidr"`
+	CIDR string `json:"cidr"`
 
 	// ID is the UUID of the network.
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Name is the common name that the network has.
-	Name string `mapstructure:"label"`
+	Name string `json:"label"`
 }
 
 // NetworkPage stores a single, only page of Networks
@@ -31,15 +30,12 @@
 }
 
 // ExtractNetworks interprets a page of results as a slice of Networks
-func ExtractNetworks(page pagination.Page) ([]Network, error) {
-	networks := page.(NetworkPage).Body
-	var res struct {
-		Networks []Network `mapstructure:"networks"`
+func ExtractNetworks(r pagination.Page) ([]Network, error) {
+	var s struct {
+		Networks []Network `json:"networks"`
 	}
-
-	err := mapstructure.WeakDecode(networks, &res)
-
-	return res.Networks, err
+	err := (r.(NetworkPage)).ExtractInto(&s)
+	return s.Networks, err
 }
 
 type NetworkResult struct {
@@ -49,16 +45,11 @@
 // Extract is a method that attempts to interpret any Network resource
 // response as a Network struct.
 func (r NetworkResult) Extract() (*Network, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Network *Network `json:"network"`
 	}
-
-	var res struct {
-		Network *Network `json:"network" mapstructure:"network"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-	return res.Network, err
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // GetResult is the response from a Get operation. Call its Extract method to interpret it
diff --git a/openstack/compute/v2/extensions/tenantnetworks/testing/doc.go b/openstack/compute/v2/extensions/tenantnetworks/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/tenantnetworks/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/tenantnetworks/testing/fixtures.go b/openstack/compute/v2/extensions/tenantnetworks/testing/fixtures.go
new file mode 100644
index 0000000..ae679b4
--- /dev/null
+++ b/openstack/compute/v2/extensions/tenantnetworks/testing/fixtures.go
@@ -0,0 +1,83 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ListOutput is a sample response to a List call.
+const ListOutput = `
+{
+    "networks": [
+        {
+            "cidr": "10.0.0.0/29",
+            "id": "20c8acc0-f747-4d71-a389-46d078ebf047",
+            "label": "mynet_0"
+        },
+        {
+            "cidr": "10.0.0.10/29",
+            "id": "20c8acc0-f747-4d71-a389-46d078ebf000",
+            "label": "mynet_1"
+        }
+    ]
+}
+`
+
+// GetOutput is a sample response to a Get call.
+const GetOutput = `
+{
+    "network": {
+			"cidr": "10.0.0.10/29",
+			"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
+			"label": "mynet_1"
+		}
+}
+`
+
+// FirstNetwork is the first result in ListOutput.
+var nilTime time.Time
+var FirstNetwork = tenantnetworks.Network{
+	CIDR: "10.0.0.0/29",
+	ID:   "20c8acc0-f747-4d71-a389-46d078ebf047",
+	Name: "mynet_0",
+}
+
+// SecondNetwork is the second result in ListOutput.
+var SecondNetwork = tenantnetworks.Network{
+	CIDR: "10.0.0.10/29",
+	ID:   "20c8acc0-f747-4d71-a389-46d078ebf000",
+	Name: "mynet_1",
+}
+
+// ExpectedNetworkSlice is the slice of results that should be parsed
+// from ListOutput, in the expected order.
+var ExpectedNetworkSlice = []tenantnetworks.Network{FirstNetwork, SecondNetwork}
+
+// HandleListSuccessfully configures the test server to respond to a List request.
+func HandleListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-tenant-networks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, ListOutput)
+	})
+}
+
+// HandleGetSuccessfully configures the test server to respond to a Get request
+// for an existing network.
+func HandleGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/os-tenant-networks/20c8acc0-f747-4d71-a389-46d078ebf000", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, GetOutput)
+	})
+}
diff --git a/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go b/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go
new file mode 100644
index 0000000..703c846
--- /dev/null
+++ b/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go
@@ -0,0 +1,38 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListSuccessfully(t)
+
+	count := 0
+	err := tenantnetworks.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := tenantnetworks.ExtractNetworks(page)
+		th.AssertNoErr(t, err)
+		th.CheckDeepEquals(t, ExpectedNetworkSlice, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, 1, count)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t)
+
+	actual, err := tenantnetworks.Get(client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &SecondNetwork, actual)
+}
diff --git a/openstack/compute/v2/extensions/tenantnetworks/urls.go b/openstack/compute/v2/extensions/tenantnetworks/urls.go
index 2401a5d..683041d 100644
--- a/openstack/compute/v2/extensions/tenantnetworks/urls.go
+++ b/openstack/compute/v2/extensions/tenantnetworks/urls.go
@@ -1,6 +1,6 @@
 package tenantnetworks
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const resourcePath = "os-tenant-networks"
 
diff --git a/openstack/compute/v2/extensions/tenantnetworks/urls_test.go b/openstack/compute/v2/extensions/tenantnetworks/urls_test.go
deleted file mode 100644
index 39c464e..0000000
--- a/openstack/compute/v2/extensions/tenantnetworks/urls_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package tenantnetworks
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-
-	th.CheckEquals(t, c.Endpoint+"os-tenant-networks", listURL(c))
-}
-
-func TestGetURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	id := "1"
-
-	th.CheckEquals(t, c.Endpoint+"os-tenant-networks/"+id, getURL(c, id))
-}
diff --git a/openstack/compute/v2/extensions/testing/delegate_test.go b/openstack/compute/v2/extensions/testing/delegate_test.go
new file mode 100644
index 0000000..822093f
--- /dev/null
+++ b/openstack/compute/v2/extensions/testing/delegate_test.go
@@ -0,0 +1,56 @@
+package testing
+
+import (
+	"testing"
+
+	common "github.com/gophercloud/gophercloud/openstack/common/extensions"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleListExtensionsSuccessfully(t)
+
+	count := 0
+	extensions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := extensions.ExtractExtensions(page)
+		th.AssertNoErr(t, err)
+
+		expected := []common.Extension{
+			common.Extension{
+				Updated:     "2013-01-20T00:00:00-00:00",
+				Name:        "Neutron Service Type Management",
+				Links:       []interface{}{},
+				Namespace:   "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
+				Alias:       "service-type",
+				Description: "API for retrieving service providers for Neutron advanced services",
+			},
+		}
+		th.AssertDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+	th.CheckEquals(t, 1, count)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleGetExtensionsSuccessfully(t)
+
+	ext, err := extensions.Get(client.ServiceClient(), "agent").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, ext.Updated, "2013-02-03T10:00:00-00:00")
+	th.AssertEquals(t, ext.Name, "agent")
+	th.AssertEquals(t, ext.Namespace, "http://docs.openstack.org/ext/agent/api/v2.0")
+	th.AssertEquals(t, ext.Alias, "agent")
+	th.AssertEquals(t, ext.Description, "The agent management extension.")
+}
diff --git a/openstack/compute/v2/extensions/testing/doc.go b/openstack/compute/v2/extensions/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/extensions/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/extensions/testing/fixtures.go b/openstack/compute/v2/extensions/testing/fixtures.go
new file mode 100644
index 0000000..2a3fb69
--- /dev/null
+++ b/openstack/compute/v2/extensions/testing/fixtures.go
@@ -0,0 +1,57 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func HandleListExtensionsSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/extensions", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+
+		fmt.Fprintf(w, `
+{
+		"extensions": [
+				{
+						"updated": "2013-01-20T00:00:00-00:00",
+						"name": "Neutron Service Type Management",
+						"links": [],
+						"namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
+						"alias": "service-type",
+						"description": "API for retrieving service providers for Neutron advanced services"
+				}
+		]
+}
+			`)
+	})
+}
+
+func HandleGetExtensionsSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/extensions/agent", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+		"extension": {
+				"updated": "2013-02-03T10:00:00-00:00",
+				"name": "agent",
+				"links": [],
+				"namespace": "http://docs.openstack.org/ext/agent/api/v2.0",
+				"alias": "agent",
+				"description": "The agent management extension."
+		}
+}
+		`)
+	})
+}
diff --git a/openstack/compute/v2/extensions/volumeattach/requests.go b/openstack/compute/v2/extensions/volumeattach/requests.go
index b4ebede..ee4d62d 100644
--- a/openstack/compute/v2/extensions/volumeattach/requests.go
+++ b/openstack/compute/v2/extensions/volumeattach/requests.go
@@ -1,16 +1,14 @@
 package volumeattach
 
 import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // List returns a Pager that allows you to iterate over a collection of VolumeAttachments.
-func List(client *gophercloud.ServiceClient, serverId string) pagination.Pager {
-	return pagination.NewPager(client, listURL(client, serverId), func(r pagination.PageResult) pagination.Page {
-		return VolumeAttachmentsPage{pagination.SinglePageBase(r)}
+func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
+	return pagination.NewPager(client, listURL(client, serverID), func(r pagination.PageResult) pagination.Page {
+		return VolumeAttachmentPage{pagination.SinglePageBase(r)}
 	})
 }
 
@@ -23,53 +21,37 @@
 // CreateOpts specifies volume attachment creation or import parameters.
 type CreateOpts struct {
 	// Device is the device that the volume will attach to the instance as. Omit for "auto"
-	Device string
-
+	Device string `json:"device,omitempty"`
 	// VolumeID is the ID of the volume to attach to the instance
-	VolumeID string
+	VolumeID string `json:"volumeId" required:"true"`
 }
 
 // ToVolumeAttachmentCreateMap constructs a request body from CreateOpts.
 func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]interface{}, error) {
-	if opts.VolumeID == "" {
-		return nil, errors.New("Missing field required for volume attachment creation: VolumeID")
-	}
-
-	volumeAttachment := make(map[string]interface{})
-	volumeAttachment["volumeId"] = opts.VolumeID
-	if opts.Device != "" {
-		volumeAttachment["device"] = opts.Device
-	}
-
-	return map[string]interface{}{"volumeAttachment": volumeAttachment}, nil
+	return gophercloud.BuildRequestBody(opts, "volumeAttachment")
 }
 
 // Create requests the creation of a new volume attachment on the server
-func Create(client *gophercloud.ServiceClient, serverId string, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToVolumeAttachmentCreateMap()
+func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToVolumeAttachmentCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Post(createURL(client, serverId), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // Get returns public data about a previously created VolumeAttachment.
-func Get(client *gophercloud.ServiceClient, serverId, aId string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, serverId, aId), &res.Body, nil)
-	return res
+func Get(client *gophercloud.ServiceClient, serverID, attachmentID string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, serverID, attachmentID), &r.Body, nil)
+	return
 }
 
 // Delete requests the deletion of a previous stored VolumeAttachment from the server.
-func Delete(client *gophercloud.ServiceClient, serverId, aId string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, serverId, aId), nil)
-	return res
+func Delete(client *gophercloud.ServiceClient, serverID, attachmentID string) (r DeleteResult) {
+	_, r.Err = client.Delete(deleteURL(client, serverID, attachmentID), nil)
+	return
 }
diff --git a/openstack/compute/v2/extensions/volumeattach/requests_test.go b/openstack/compute/v2/extensions/volumeattach/requests_test.go
deleted file mode 100644
index b0a765b..0000000
--- a/openstack/compute/v2/extensions/volumeattach/requests_test.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package volumeattach
-
-import (
-	"testing"
-
-	fixtures "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/testing"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// FirstVolumeAttachment is the first result in ListOutput.
-var FirstVolumeAttachment = VolumeAttachment{
-	Device:   "/dev/vdd",
-	ID:       "a26887c6-c47b-4654-abb5-dfadf7d3f803",
-	ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-	VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f803",
-}
-
-// SecondVolumeAttachment is the first result in ListOutput.
-var SecondVolumeAttachment = VolumeAttachment{
-	Device:   "/dev/vdc",
-	ID:       "a26887c6-c47b-4654-abb5-dfadf7d3f804",
-	ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-	VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
-}
-
-// ExpectedVolumeAttachmentSlide is the slice of results that should be parsed
-// from ListOutput, in the expected order.
-var ExpectedVolumeAttachmentSlice = []VolumeAttachment{FirstVolumeAttachment, SecondVolumeAttachment}
-
-//CreatedVolumeAttachment is the parsed result from CreatedOutput.
-var CreatedVolumeAttachment = VolumeAttachment{
-	Device:   "/dev/vdc",
-	ID:       "a26887c6-c47b-4654-abb5-dfadf7d3f804",
-	ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-	VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixtures.HandleListSuccessfully(t)
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	count := 0
-	err := List(client.ServiceClient(), serverId).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractVolumeAttachments(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, ExpectedVolumeAttachmentSlice, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixtures.HandleCreateSuccessfully(t)
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	actual, err := Create(client.ServiceClient(), serverId, CreateOpts{
-		Device:   "/dev/vdc",
-		VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &CreatedVolumeAttachment, actual)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixtures.HandleGetSuccessfully(t)
-	aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	actual, err := Get(client.ServiceClient(), serverId, aId).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &SecondVolumeAttachment, actual)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixtures.HandleDeleteSuccessfully(t)
-	aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	err := Delete(client.ServiceClient(), serverId, aId).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/compute/v2/extensions/volumeattach/results.go b/openstack/compute/v2/extensions/volumeattach/results.go
index 26be39e..53faf5d 100644
--- a/openstack/compute/v2/extensions/volumeattach/results.go
+++ b/openstack/compute/v2/extensions/volumeattach/results.go
@@ -1,51 +1,48 @@
 package volumeattach
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// VolumeAttach controls the attachment of a volume to an instance.
+// VolumeAttachment controls the attachment of a volume to an instance.
 type VolumeAttachment struct {
 	// ID is a unique id of the attachment
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Device is what device the volume is attached as
-	Device string `mapstructure:"device"`
+	Device string `json:"device"`
 
 	// VolumeID is the ID of the attached volume
-	VolumeID string `mapstructure:"volumeId"`
+	VolumeID string `json:"volumeId"`
 
 	// ServerID is the ID of the instance that has the volume attached
-	ServerID string `mapstructure:"serverId"`
+	ServerID string `json:"serverId"`
 }
 
-// VolumeAttachmentsPage stores a single, only page of VolumeAttachments
+// VolumeAttachmentPage stores a single, only page of VolumeAttachments
 // results from a List call.
-type VolumeAttachmentsPage struct {
+type VolumeAttachmentPage struct {
 	pagination.SinglePageBase
 }
 
 // IsEmpty determines whether or not a VolumeAttachmentsPage is empty.
-func (page VolumeAttachmentsPage) IsEmpty() (bool, error) {
+func (page VolumeAttachmentPage) IsEmpty() (bool, error) {
 	va, err := ExtractVolumeAttachments(page)
 	return len(va) == 0, err
 }
 
 // ExtractVolumeAttachments interprets a page of results as a slice of
 // VolumeAttachments.
-func ExtractVolumeAttachments(page pagination.Page) ([]VolumeAttachment, error) {
-	casted := page.(VolumeAttachmentsPage).Body
-	var response struct {
-		VolumeAttachments []VolumeAttachment `mapstructure:"volumeAttachments"`
+func ExtractVolumeAttachments(r pagination.Page) ([]VolumeAttachment, error) {
+	var s struct {
+		VolumeAttachments []VolumeAttachment `json:"volumeAttachments"`
 	}
-
-	err := mapstructure.WeakDecode(casted, &response)
-
-	return response.VolumeAttachments, err
+	err := (r.(VolumeAttachmentPage)).ExtractInto(&s)
+	return s.VolumeAttachments, err
 }
 
+// VolumeAttachmentResult is the result from a volume attachment operation.
 type VolumeAttachmentResult struct {
 	gophercloud.Result
 }
@@ -53,16 +50,11 @@
 // Extract is a method that attempts to interpret any VolumeAttachment resource
 // response as a VolumeAttachment struct.
 func (r VolumeAttachmentResult) Extract() (*VolumeAttachment, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		VolumeAttachment *VolumeAttachment `json:"volumeAttachment"`
 	}
-
-	var res struct {
-		VolumeAttachment *VolumeAttachment `json:"volumeAttachment" mapstructure:"volumeAttachment"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-	return res.VolumeAttachment, err
+	err := r.ExtractInto(&s)
+	return s.VolumeAttachment, err
 }
 
 // CreateResult is the response from a Create operation. Call its Extract method to interpret it
diff --git a/openstack/compute/v2/extensions/volumeattach/testing/doc.go b/openstack/compute/v2/extensions/volumeattach/testing/doc.go
index 183391a..29faced 100644
--- a/openstack/compute/v2/extensions/volumeattach/testing/doc.go
+++ b/openstack/compute/v2/extensions/volumeattach/testing/doc.go
@@ -1,7 +1,7 @@
 /*
-This is package created is to hold fixtures (which imports testing),
+Package testing holds fixtures (which imports testing),
 so that importing volumeattach package does not inadvertently import testing into production code
 More information here:
-https://github.com/rackspace/gophercloud/issues/473
+https://github.com/gophercloud/gophercloud/issues/473
 */
 package testing
diff --git a/openstack/compute/v2/extensions/volumeattach/testing/fixtures.go b/openstack/compute/v2/extensions/volumeattach/testing/fixtures.go
index c469bfb..4f99610 100644
--- a/openstack/compute/v2/extensions/volumeattach/testing/fixtures.go
+++ b/openstack/compute/v2/extensions/volumeattach/testing/fixtures.go
@@ -1,5 +1,3 @@
-// +build fixtures
-
 package testing
 
 import (
@@ -7,8 +5,8 @@
 	"net/http"
 	"testing"
 
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
 )
 
 // ListOutput is a sample response to a List call.
diff --git a/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go b/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go
new file mode 100644
index 0000000..9486f9b
--- /dev/null
+++ b/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go
@@ -0,0 +1,102 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// FirstVolumeAttachment is the first result in ListOutput.
+var FirstVolumeAttachment = volumeattach.VolumeAttachment{
+	Device:   "/dev/vdd",
+	ID:       "a26887c6-c47b-4654-abb5-dfadf7d3f803",
+	ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
+	VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f803",
+}
+
+// SecondVolumeAttachment is the first result in ListOutput.
+var SecondVolumeAttachment = volumeattach.VolumeAttachment{
+	Device:   "/dev/vdc",
+	ID:       "a26887c6-c47b-4654-abb5-dfadf7d3f804",
+	ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
+	VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
+}
+
+// ExpectedVolumeAttachmentSlide is the slice of results that should be parsed
+// from ListOutput, in the expected order.
+var ExpectedVolumeAttachmentSlice = []volumeattach.VolumeAttachment{FirstVolumeAttachment, SecondVolumeAttachment}
+
+//CreatedVolumeAttachment is the parsed result from CreatedOutput.
+var CreatedVolumeAttachment = volumeattach.VolumeAttachment{
+	Device:   "/dev/vdc",
+	ID:       "a26887c6-c47b-4654-abb5-dfadf7d3f804",
+	ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
+	VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
+}
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleListSuccessfully(t)
+
+	serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
+
+	count := 0
+	err := volumeattach.List(client.ServiceClient(), serverID).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := volumeattach.ExtractVolumeAttachments(page)
+		th.AssertNoErr(t, err)
+		th.CheckDeepEquals(t, ExpectedVolumeAttachmentSlice, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, 1, count)
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleCreateSuccessfully(t)
+
+	serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
+
+	actual, err := volumeattach.Create(client.ServiceClient(), serverID, volumeattach.CreateOpts{
+		Device:   "/dev/vdc",
+		VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
+	}).Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &CreatedVolumeAttachment, actual)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleGetSuccessfully(t)
+
+	aID := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
+	serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
+
+	actual, err := volumeattach.Get(client.ServiceClient(), serverID, aID).Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, &SecondVolumeAttachment, actual)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleDeleteSuccessfully(t)
+
+	aID := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
+	serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
+
+	err := volumeattach.Delete(client.ServiceClient(), serverID, aID).ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/compute/v2/extensions/volumeattach/urls.go b/openstack/compute/v2/extensions/volumeattach/urls.go
index 9d9d178..083f8dc 100644
--- a/openstack/compute/v2/extensions/volumeattach/urls.go
+++ b/openstack/compute/v2/extensions/volumeattach/urls.go
@@ -1,25 +1,25 @@
 package volumeattach
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const resourcePath = "os-volume_attachments"
 
-func resourceURL(c *gophercloud.ServiceClient, serverId string) string {
-	return c.ServiceURL("servers", serverId, resourcePath)
+func resourceURL(c *gophercloud.ServiceClient, serverID string) string {
+	return c.ServiceURL("servers", serverID, resourcePath)
 }
 
-func listURL(c *gophercloud.ServiceClient, serverId string) string {
-	return resourceURL(c, serverId)
+func listURL(c *gophercloud.ServiceClient, serverID string) string {
+	return resourceURL(c, serverID)
 }
 
-func createURL(c *gophercloud.ServiceClient, serverId string) string {
-	return resourceURL(c, serverId)
+func createURL(c *gophercloud.ServiceClient, serverID string) string {
+	return resourceURL(c, serverID)
 }
 
-func getURL(c *gophercloud.ServiceClient, serverId, aId string) string {
-	return c.ServiceURL("servers", serverId, resourcePath, aId)
+func getURL(c *gophercloud.ServiceClient, serverID, aID string) string {
+	return c.ServiceURL("servers", serverID, resourcePath, aID)
 }
 
-func deleteURL(c *gophercloud.ServiceClient, serverId, aId string) string {
-	return getURL(c, serverId, aId)
+func deleteURL(c *gophercloud.ServiceClient, serverID, aID string) string {
+	return getURL(c, serverID, aID)
 }
diff --git a/openstack/compute/v2/extensions/volumeattach/urls_test.go b/openstack/compute/v2/extensions/volumeattach/urls_test.go
deleted file mode 100644
index 8ee0e42..0000000
--- a/openstack/compute/v2/extensions/volumeattach/urls_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package volumeattach
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/os-volume_attachments", listURL(c, serverId))
-}
-
-func TestCreateURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/os-volume_attachments", createURL(c, serverId))
-}
-
-func TestGetURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-	aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
-
-	th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/os-volume_attachments/"+aId, getURL(c, serverId, aId))
-}
-
-func TestDeleteURL(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	c := client.ServiceClient()
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-	aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
-
-	th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/os-volume_attachments/"+aId, deleteURL(c, serverId, aId))
-}
diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go
index 59123aa..ef133ff 100644
--- a/openstack/compute/v2/flavors/requests.go
+++ b/openstack/compute/v2/flavors/requests.go
@@ -1,10 +1,8 @@
 package flavors
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -36,10 +34,7 @@
 // ToFlavorListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToFlavorListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // ListDetail instructs OpenStack to provide a list of flavors.
@@ -54,50 +49,52 @@
 		}
 		url += query
 	}
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
 		return FlavorPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, url, createPage)
+	})
 }
 
 // Get instructs OpenStack to provide details on a single flavor, identified by its ID.
 // Use ExtractFlavor to convert its result into a Flavor.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return
 }
 
 // IDFromName is a convienience function that returns a flavor's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
-	flavorCount := 0
-	flavorID := ""
-	if name == "" {
-		return "", fmt.Errorf("A flavor name must be provided.")
+	count := 0
+	id := ""
+	allPages, err := ListDetail(client, nil).AllPages()
+	if err != nil {
+		return "", err
 	}
-	pager := ListDetail(client, nil)
-	pager.EachPage(func(page pagination.Page) (bool, error) {
-		flavorList, err := ExtractFlavors(page)
-		if err != nil {
-			return false, err
-		}
 
-		for _, f := range flavorList {
-			if f.Name == name {
-				flavorCount++
-				flavorID = f.ID
-			}
-		}
-		return true, nil
-	})
+	all, err := ExtractFlavors(allPages)
+	if err != nil {
+		return "", err
+	}
 
-	switch flavorCount {
+	for _, f := range all {
+		if f.Name == name {
+			count++
+			id = f.ID
+		}
+	}
+
+	switch count {
 	case 0:
-		return "", fmt.Errorf("Unable to find flavor: %s", name)
+		err := &gophercloud.ErrResourceNotFound{}
+		err.ResourceType = "flavor"
+		err.Name = name
+		return "", err
 	case 1:
-		return flavorID, nil
+		return id, nil
 	default:
-		return "", fmt.Errorf("Found %d flavors matching %s", flavorCount, name)
+		err := &gophercloud.ErrMultipleResourcesFound{}
+		err.ResourceType = "flavor"
+		err.Name = name
+		err.Count = count
+		return "", err
 	}
 }
diff --git a/openstack/compute/v2/flavors/requests_test.go b/openstack/compute/v2/flavors/requests_test.go
deleted file mode 100644
index fbd7c33..0000000
--- a/openstack/compute/v2/flavors/requests_test.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package flavors
-
-import (
-	"fmt"
-	"net/http"
-	"reflect"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const tokenID = "blerb"
-
-func TestListFlavors(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/flavors/detail", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, `
-					{
-						"flavors": [
-							{
-								"id": "1",
-								"name": "m1.tiny",
-								"disk": 1,
-								"ram": 512,
-								"vcpus": 1
-							},
-							{
-								"id": "2",
-								"name": "m2.small",
-								"disk": 10,
-								"ram": 1024,
-								"vcpus": 2
-							}
-						],
-						"flavors_links": [
-							{
-								"href": "%s/flavors/detail?marker=2",
-								"rel": "next"
-							}
-						]
-					}
-				`, th.Server.URL)
-		case "2":
-			fmt.Fprintf(w, `{ "flavors": [] }`)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-
-	pages := 0
-	err := ListDetail(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractFlavors(page)
-		if err != nil {
-			return false, err
-		}
-
-		expected := []Flavor{
-			Flavor{ID: "1", Name: "m1.tiny", Disk: 1, RAM: 512, VCPUs: 1},
-			Flavor{ID: "2", Name: "m2.small", Disk: 10, RAM: 1024, VCPUs: 2},
-		}
-
-		if !reflect.DeepEqual(expected, actual) {
-			t.Errorf("Expected %#v, but was %#v", expected, actual)
-		}
-
-		return true, nil
-	})
-	if err != nil {
-		t.Fatal(err)
-	}
-	if pages != 1 {
-		t.Errorf("Expected one page, got %d", pages)
-	}
-}
-
-func TestGetFlavor(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/flavors/12345", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `
-			{
-				"flavor": {
-					"id": "1",
-					"name": "m1.tiny",
-					"disk": 1,
-					"ram": 512,
-					"vcpus": 1,
-					"rxtx_factor": 1
-				}
-			}
-		`)
-	})
-
-	actual, err := Get(fake.ServiceClient(), "12345").Extract()
-	if err != nil {
-		t.Fatalf("Unable to get flavor: %v", err)
-	}
-
-	expected := &Flavor{
-		ID:         "1",
-		Name:       "m1.tiny",
-		Disk:       1,
-		RAM:        512,
-		VCPUs:      1,
-		RxTxFactor: 1,
-	}
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("Expected %#v, but was %#v", expected, actual)
-	}
-}
diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go
index 8dddd70..0edc7d2 100644
--- a/openstack/compute/v2/flavors/results.go
+++ b/openstack/compute/v2/flavors/results.go
@@ -2,11 +2,9 @@
 
 import (
 	"errors"
-	"reflect"
 
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ErrCannotInterpret is returned by an Extract call if the response body doesn't have the expected structure.
@@ -18,48 +16,35 @@
 }
 
 // Extract provides access to the individual Flavor returned by the Get function.
-func (gr GetResult) Extract() (*Flavor, error) {
-	if gr.Err != nil {
-		return nil, gr.Err
+func (r GetResult) Extract() (*Flavor, error) {
+	var s struct {
+		Flavor *Flavor `json:"flavor"`
 	}
-
-	var result struct {
-		Flavor Flavor `mapstructure:"flavor"`
-	}
-
-	cfg := &mapstructure.DecoderConfig{
-		DecodeHook: defaulter,
-		Result:     &result,
-	}
-	decoder, err := mapstructure.NewDecoder(cfg)
-	if err != nil {
-		return nil, err
-	}
-	err = decoder.Decode(gr.Body)
-	return &result.Flavor, err
+	err := r.ExtractInto(&s)
+	return s.Flavor, err
 }
 
 // Flavor records represent (virtual) hardware configurations for server resources in a region.
 type Flavor struct {
 	// The Id field contains the flavor's unique identifier.
 	// For example, this identifier will be useful when specifying which hardware configuration to use for a new server instance.
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 
 	// The Disk and RA< fields provide a measure of storage space offered by the flavor, in GB and MB, respectively.
-	Disk int `mapstructure:"disk"`
-	RAM  int `mapstructure:"ram"`
+	Disk int `json:"disk"`
+	RAM  int `json:"ram"`
 
 	// The Name field provides a human-readable moniker for the flavor.
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 
-	RxTxFactor float64 `mapstructure:"rxtx_factor"`
+	RxTxFactor float64 `json:"rxtx_factor"`
 
 	// Swap indicates how much space is reserved for swap.
 	// If not provided, this field will be set to 0.
-	Swap int `mapstructure:"swap"`
+	Swap int `json:"swap"`
 
 	// VCPUs indicates how many (virtual) CPUs are available for this flavor.
-	VCPUs int `mapstructure:"vcpus"`
+	VCPUs int `json:"vcpus"`
 }
 
 // FlavorPage contains a single page of the response from a List call.
@@ -68,55 +53,28 @@
 }
 
 // IsEmpty determines if a page contains any results.
-func (p FlavorPage) IsEmpty() (bool, error) {
-	flavors, err := ExtractFlavors(p)
-	if err != nil {
-		return true, err
-	}
-	return len(flavors) == 0, nil
+func (page FlavorPage) IsEmpty() (bool, error) {
+	flavors, err := ExtractFlavors(page)
+	return len(flavors) == 0, err
 }
 
 // NextPageURL uses the response's embedded link reference to navigate to the next page of results.
-func (p FlavorPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"flavors_links"`
+func (page FlavorPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"flavors_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
-}
-
-func defaulter(from, to reflect.Kind, v interface{}) (interface{}, error) {
-	if (from == reflect.String) && (to == reflect.Int) {
-		return 0, nil
-	}
-	return v, nil
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // ExtractFlavors provides access to the list of flavors in a page acquired from the List operation.
-func ExtractFlavors(page pagination.Page) ([]Flavor, error) {
-	casted := page.(FlavorPage).Body
-	var container struct {
-		Flavors []Flavor `mapstructure:"flavors"`
+func ExtractFlavors(r pagination.Page) ([]Flavor, error) {
+	var s struct {
+		Flavors []Flavor `json:"flavors"`
 	}
-
-	cfg := &mapstructure.DecoderConfig{
-		DecodeHook: defaulter,
-		Result:     &container,
-	}
-	decoder, err := mapstructure.NewDecoder(cfg)
-	if err != nil {
-		return container.Flavors, err
-	}
-	err = decoder.Decode(casted)
-	if err != nil {
-		return container.Flavors, err
-	}
-
-	return container.Flavors, nil
+	err := (r.(FlavorPage)).ExtractInto(&s)
+	return s.Flavors, err
 }
diff --git a/openstack/compute/v2/flavors/testing/doc.go b/openstack/compute/v2/flavors/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/flavors/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go
new file mode 100644
index 0000000..e86512a
--- /dev/null
+++ b/openstack/compute/v2/flavors/testing/requests_test.go
@@ -0,0 +1,130 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+const tokenID = "blerb"
+
+func TestListFlavors(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/flavors/detail", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, `
+					{
+						"flavors": [
+							{
+								"id": "1",
+								"name": "m1.tiny",
+								"disk": 1,
+								"ram": 512,
+								"vcpus": 1
+							},
+							{
+								"id": "2",
+								"name": "m2.small",
+								"disk": 10,
+								"ram": 1024,
+								"vcpus": 2
+							}
+						],
+						"flavors_links": [
+							{
+								"href": "%s/flavors/detail?marker=2",
+								"rel": "next"
+							}
+						]
+					}
+				`, th.Server.URL)
+		case "2":
+			fmt.Fprintf(w, `{ "flavors": [] }`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+
+	pages := 0
+	err := flavors.ListDetail(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := flavors.ExtractFlavors(page)
+		if err != nil {
+			return false, err
+		}
+
+		expected := []flavors.Flavor{
+			{ID: "1", Name: "m1.tiny", Disk: 1, RAM: 512, VCPUs: 1},
+			{ID: "2", Name: "m2.small", Disk: 10, RAM: 1024, VCPUs: 2},
+		}
+
+		if !reflect.DeepEqual(expected, actual) {
+			t.Errorf("Expected %#v, but was %#v", expected, actual)
+		}
+
+		return true, nil
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	if pages != 1 {
+		t.Errorf("Expected one page, got %d", pages)
+	}
+}
+
+func TestGetFlavor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/flavors/12345", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `
+			{
+				"flavor": {
+					"id": "1",
+					"name": "m1.tiny",
+					"disk": 1,
+					"ram": 512,
+					"vcpus": 1,
+					"rxtx_factor": 1
+				}
+			}
+		`)
+	})
+
+	actual, err := flavors.Get(fake.ServiceClient(), "12345").Extract()
+	if err != nil {
+		t.Fatalf("Unable to get flavor: %v", err)
+	}
+
+	expected := &flavors.Flavor{
+		ID:         "1",
+		Name:       "m1.tiny",
+		Disk:       1,
+		RAM:        512,
+		VCPUs:      1,
+		RxTxFactor: 1,
+	}
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Expected %#v, but was %#v", expected, actual)
+	}
+}
diff --git a/openstack/compute/v2/flavors/urls.go b/openstack/compute/v2/flavors/urls.go
index 683c107..ee0dfdb 100644
--- a/openstack/compute/v2/flavors/urls.go
+++ b/openstack/compute/v2/flavors/urls.go
@@ -1,7 +1,7 @@
 package flavors
 
 import (
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 func getURL(client *gophercloud.ServiceClient, id string) string {
diff --git a/openstack/compute/v2/flavors/urls_test.go b/openstack/compute/v2/flavors/urls_test.go
deleted file mode 100644
index 069da24..0000000
--- a/openstack/compute/v2/flavors/urls_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package flavors
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "flavors/foo"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient())
-	expected := endpoint + "flavors/detail"
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/openstack/compute/v2/images/requests.go b/openstack/compute/v2/images/requests.go
index 1e021ad..df9f1da 100644
--- a/openstack/compute/v2/images/requests.go
+++ b/openstack/compute/v2/images/requests.go
@@ -1,10 +1,8 @@
 package images
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -34,10 +32,7 @@
 // ToImageListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToImageListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // ListDetail enumerates the available images.
@@ -50,60 +45,58 @@
 		}
 		url += query
 	}
-
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
 		return ImagePage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, url, createPage)
+	})
 }
 
 // Get acquires additional detail about a specific image by ID.
 // Use ExtractImage() to interpret the result as an openstack Image.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(getURL(client, id), &result.Body, nil)
-	return result
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return
 }
 
 // Delete deletes the specified image ID.
-func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var result DeleteResult
-	_, result.Err = client.Delete(deleteURL(client, id), nil)
-	return result
+func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = client.Delete(deleteURL(client, id), nil)
+	return
 }
 
 // IDFromName is a convienience function that returns an image's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
-	imageCount := 0
-	imageID := ""
-	if name == "" {
-		return "", fmt.Errorf("An image name must be provided.")
+	count := 0
+	id := ""
+	allPages, err := ListDetail(client, nil).AllPages()
+	if err != nil {
+		return "", err
 	}
-	pager := ListDetail(client, &ListOpts{
-		Name: name,
-	})
-	pager.EachPage(func(page pagination.Page) (bool, error) {
-		imageList, err := ExtractImages(page)
-		if err != nil {
-			return false, err
-		}
 
-		for _, i := range imageList {
-			if i.Name == name {
-				imageCount++
-				imageID = i.ID
-			}
-		}
-		return true, nil
-	})
+	all, err := ExtractImages(allPages)
+	if err != nil {
+		return "", err
+	}
 
-	switch imageCount {
+	for _, f := range all {
+		if f.Name == name {
+			count++
+			id = f.ID
+		}
+	}
+
+	switch count {
 	case 0:
-		return "", fmt.Errorf("Unable to find image: %s", name)
+		err := &gophercloud.ErrResourceNotFound{}
+		err.ResourceType = "image"
+		err.Name = name
+		return "", err
 	case 1:
-		return imageID, nil
+		return id, nil
 	default:
-		return "", fmt.Errorf("Found %d images matching %s", imageCount, name)
+		err := &gophercloud.ErrMultipleResourcesFound{}
+		err.ResourceType = "image"
+		err.Name = name
+		err.Count = count
+		return "", err
 	}
 }
diff --git a/openstack/compute/v2/images/requests_test.go b/openstack/compute/v2/images/requests_test.go
deleted file mode 100644
index 21e8296..0000000
--- a/openstack/compute/v2/images/requests_test.go
+++ /dev/null
@@ -1,188 +0,0 @@
-package images
-
-import (
-	"encoding/json"
-	"fmt"
-	"net/http"
-	"reflect"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListImages(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/images/detail", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, `
-				{
-					"images": [
-						{
-							"status": "ACTIVE",
-							"updated": "2014-09-23T12:54:56Z",
-							"id": "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7",
-							"OS-EXT-IMG-SIZE:size": 476704768,
-							"name": "F17-x86_64-cfntools",
-							"created": "2014-09-23T12:54:52Z",
-							"minDisk": 0,
-							"progress": 100,
-							"minRam": 0
-						},
-						{
-							"status": "ACTIVE",
-							"updated": "2014-09-23T12:51:43Z",
-							"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
-							"OS-EXT-IMG-SIZE:size": 13167616,
-							"name": "cirros-0.3.2-x86_64-disk",
-							"created": "2014-09-23T12:51:42Z",
-							"minDisk": 0,
-							"progress": 100,
-							"minRam": 0
-						}
-					]
-				}
-			`)
-		case "2":
-			fmt.Fprintf(w, `{ "images": [] }`)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-
-	pages := 0
-	options := &ListOpts{Limit: 2}
-	err := ListDetail(fake.ServiceClient(), options).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractImages(page)
-		if err != nil {
-			return false, err
-		}
-
-		expected := []Image{
-			Image{
-				ID:       "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7",
-				Name:     "F17-x86_64-cfntools",
-				Created:  "2014-09-23T12:54:52Z",
-				Updated:  "2014-09-23T12:54:56Z",
-				MinDisk:  0,
-				MinRAM:   0,
-				Progress: 100,
-				Status:   "ACTIVE",
-			},
-			Image{
-				ID:       "f90f6034-2570-4974-8351-6b49732ef2eb",
-				Name:     "cirros-0.3.2-x86_64-disk",
-				Created:  "2014-09-23T12:51:42Z",
-				Updated:  "2014-09-23T12:51:43Z",
-				MinDisk:  0,
-				MinRAM:   0,
-				Progress: 100,
-				Status:   "ACTIVE",
-			},
-		}
-
-		if !reflect.DeepEqual(expected, actual) {
-			t.Errorf("Unexpected page contents: expected %#v, got %#v", expected, actual)
-		}
-
-		return false, nil
-	})
-
-	if err != nil {
-		t.Fatalf("EachPage error: %v", err)
-	}
-	if pages != 1 {
-		t.Errorf("Expected one page, got %d", pages)
-	}
-}
-
-func TestGetImage(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/images/12345678", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `
-			{
-				"image": {
-					"status": "ACTIVE",
-					"updated": "2014-09-23T12:54:56Z",
-					"id": "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7",
-					"OS-EXT-IMG-SIZE:size": 476704768,
-					"name": "F17-x86_64-cfntools",
-					"created": "2014-09-23T12:54:52Z",
-					"minDisk": 0,
-					"progress": 100,
-					"minRam": 0
-				}
-			}
-		`)
-	})
-
-	actual, err := Get(fake.ServiceClient(), "12345678").Extract()
-	if err != nil {
-		t.Fatalf("Unexpected error from Get: %v", err)
-	}
-
-	expected := &Image{
-		Status:   "ACTIVE",
-		Updated:  "2014-09-23T12:54:56Z",
-		ID:       "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7",
-		Name:     "F17-x86_64-cfntools",
-		Created:  "2014-09-23T12:54:52Z",
-		MinDisk:  0,
-		Progress: 100,
-		MinRAM:   0,
-	}
-
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("Expected %#v, but got %#v", expected, actual)
-	}
-}
-
-func TestNextPageURL(t *testing.T) {
-	var page ImagePage
-	var body map[string]interface{}
-	bodyString := []byte(`{"images":{"links":[{"href":"http://192.154.23.87/12345/images/image3","rel":"bookmark"}]}, "images_links":[{"href":"http://192.154.23.87/12345/images/image4","rel":"next"}]}`)
-	err := json.Unmarshal(bodyString, &body)
-	if err != nil {
-		t.Fatalf("Error unmarshaling data into page body: %v", err)
-	}
-	page.Body = body
-
-	expected := "http://192.154.23.87/12345/images/image4"
-	actual, err := page.NextPageURL()
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, expected, actual)
-}
-
-// Test Image delete
-func TestDeleteImage(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/images/12345678", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "12345678")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/compute/v2/images/results.go b/openstack/compute/v2/images/results.go
index 482e7d6..a55b8f1 100644
--- a/openstack/compute/v2/images/results.go
+++ b/openstack/compute/v2/images/results.go
@@ -1,9 +1,8 @@
 package images
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // GetResult temporarily stores a Get response.
@@ -17,17 +16,12 @@
 }
 
 // Extract interprets a GetResult as an Image.
-func (gr GetResult) Extract() (*Image, error) {
-	if gr.Err != nil {
-		return nil, gr.Err
+func (r GetResult) Extract() (*Image, error) {
+	var s struct {
+		Image *Image `json:"image"`
 	}
-
-	var decoded struct {
-		Image Image `mapstructure:"image"`
-	}
-
-	err := mapstructure.Decode(gr.Body, &decoded)
-	return &decoded.Image, err
+	err := r.ExtractInto(&s)
+	return s.Image, err
 }
 
 // Image is used for JSON (un)marshalling.
@@ -64,34 +58,26 @@
 // IsEmpty returns true if a page contains no Image results.
 func (page ImagePage) IsEmpty() (bool, error) {
 	images, err := ExtractImages(page)
-	if err != nil {
-		return true, err
-	}
-	return len(images) == 0, nil
+	return len(images) == 0, err
 }
 
 // NextPageURL uses the response's embedded link reference to navigate to the next page of results.
 func (page ImagePage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"images_links"`
+	var s struct {
+		Links []gophercloud.Link `json:"images_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(page.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // ExtractImages converts a page of List results into a slice of usable Image structs.
-func ExtractImages(page pagination.Page) ([]Image, error) {
-	casted := page.(ImagePage).Body
-	var results struct {
-		Images []Image `mapstructure:"images"`
+func ExtractImages(r pagination.Page) ([]Image, error) {
+	var s struct {
+		Images []Image `json:"images"`
 	}
-
-	err := mapstructure.Decode(casted, &results)
-	return results.Images, err
+	err := (r.(ImagePage)).ExtractInto(&s)
+	return s.Images, err
 }
diff --git a/openstack/compute/v2/images/testing/doc.go b/openstack/compute/v2/images/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/images/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/images/testing/requests_test.go b/openstack/compute/v2/images/testing/requests_test.go
new file mode 100644
index 0000000..e2d64d3
--- /dev/null
+++ b/openstack/compute/v2/images/testing/requests_test.go
@@ -0,0 +1,189 @@
+package testing
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestListImages(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/images/detail", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, `
+				{
+					"images": [
+						{
+							"status": "ACTIVE",
+							"updated": "2014-09-23T12:54:56Z",
+							"id": "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7",
+							"OS-EXT-IMG-SIZE:size": 476704768,
+							"name": "F17-x86_64-cfntools",
+							"created": "2014-09-23T12:54:52Z",
+							"minDisk": 0,
+							"progress": 100,
+							"minRam": 0
+						},
+						{
+							"status": "ACTIVE",
+							"updated": "2014-09-23T12:51:43Z",
+							"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
+							"OS-EXT-IMG-SIZE:size": 13167616,
+							"name": "cirros-0.3.2-x86_64-disk",
+							"created": "2014-09-23T12:51:42Z",
+							"minDisk": 0,
+							"progress": 100,
+							"minRam": 0
+						}
+					]
+				}
+			`)
+		case "2":
+			fmt.Fprintf(w, `{ "images": [] }`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+
+	pages := 0
+	options := &images.ListOpts{Limit: 2}
+	err := images.ListDetail(fake.ServiceClient(), options).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := images.ExtractImages(page)
+		if err != nil {
+			return false, err
+		}
+
+		expected := []images.Image{
+			{
+				ID:       "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7",
+				Name:     "F17-x86_64-cfntools",
+				Created:  "2014-09-23T12:54:52Z",
+				Updated:  "2014-09-23T12:54:56Z",
+				MinDisk:  0,
+				MinRAM:   0,
+				Progress: 100,
+				Status:   "ACTIVE",
+			},
+			{
+				ID:       "f90f6034-2570-4974-8351-6b49732ef2eb",
+				Name:     "cirros-0.3.2-x86_64-disk",
+				Created:  "2014-09-23T12:51:42Z",
+				Updated:  "2014-09-23T12:51:43Z",
+				MinDisk:  0,
+				MinRAM:   0,
+				Progress: 100,
+				Status:   "ACTIVE",
+			},
+		}
+
+		if !reflect.DeepEqual(expected, actual) {
+			t.Errorf("Unexpected page contents: expected %#v, got %#v", expected, actual)
+		}
+
+		return false, nil
+	})
+
+	if err != nil {
+		t.Fatalf("EachPage error: %v", err)
+	}
+	if pages != 1 {
+		t.Errorf("Expected one page, got %d", pages)
+	}
+}
+
+func TestGetImage(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/images/12345678", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `
+			{
+				"image": {
+					"status": "ACTIVE",
+					"updated": "2014-09-23T12:54:56Z",
+					"id": "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7",
+					"OS-EXT-IMG-SIZE:size": 476704768,
+					"name": "F17-x86_64-cfntools",
+					"created": "2014-09-23T12:54:52Z",
+					"minDisk": 0,
+					"progress": 100,
+					"minRam": 0
+				}
+			}
+		`)
+	})
+
+	actual, err := images.Get(fake.ServiceClient(), "12345678").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected error from Get: %v", err)
+	}
+
+	expected := &images.Image{
+		Status:   "ACTIVE",
+		Updated:  "2014-09-23T12:54:56Z",
+		ID:       "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7",
+		Name:     "F17-x86_64-cfntools",
+		Created:  "2014-09-23T12:54:52Z",
+		MinDisk:  0,
+		Progress: 100,
+		MinRAM:   0,
+	}
+
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("Expected %#v, but got %#v", expected, actual)
+	}
+}
+
+func TestNextPageURL(t *testing.T) {
+	var page images.ImagePage
+	var body map[string]interface{}
+	bodyString := []byte(`{"images":{"links":[{"href":"http://192.154.23.87/12345/images/image3","rel":"bookmark"}]}, "images_links":[{"href":"http://192.154.23.87/12345/images/image4","rel":"next"}]}`)
+	err := json.Unmarshal(bodyString, &body)
+	if err != nil {
+		t.Fatalf("Error unmarshaling data into page body: %v", err)
+	}
+	page.Body = body
+
+	expected := "http://192.154.23.87/12345/images/image4"
+	actual, err := page.NextPageURL()
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, expected, actual)
+}
+
+// Test Image delete
+func TestDeleteImage(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/images/12345678", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := images.Delete(fake.ServiceClient(), "12345678")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/compute/v2/images/urls.go b/openstack/compute/v2/images/urls.go
index b1bf103..57787fb 100644
--- a/openstack/compute/v2/images/urls.go
+++ b/openstack/compute/v2/images/urls.go
@@ -1,6 +1,6 @@
 package images
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func listDetailURL(client *gophercloud.ServiceClient) string {
 	return client.ServiceURL("images", "detail")
diff --git a/openstack/compute/v2/images/urls_test.go b/openstack/compute/v2/images/urls_test.go
deleted file mode 100644
index b1ab3d6..0000000
--- a/openstack/compute/v2/images/urls_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package images
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "images/foo"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestListDetailURL(t *testing.T) {
-	actual := listDetailURL(endpointClient())
-	expected := endpoint + "images/detail"
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/openstack/compute/v2/servers/errors.go b/openstack/compute/v2/servers/errors.go
new file mode 100644
index 0000000..c9f0e3c
--- /dev/null
+++ b/openstack/compute/v2/servers/errors.go
@@ -0,0 +1,71 @@
+package servers
+
+import (
+	"fmt"
+
+	"github.com/gophercloud/gophercloud"
+)
+
+// ErrNeitherImageIDNorImageNameProvided is the error when neither the image
+// ID nor the image name is provided for a server operation
+type ErrNeitherImageIDNorImageNameProvided struct{ gophercloud.ErrMissingInput }
+
+func (e ErrNeitherImageIDNorImageNameProvided) Error() string {
+	return "One and only one of the image ID and the image name must be provided."
+}
+
+// ErrNeitherFlavorIDNorFlavorNameProvided is the error when neither the flavor
+// ID nor the flavor name is provided for a server operation
+type ErrNeitherFlavorIDNorFlavorNameProvided struct{ gophercloud.ErrMissingInput }
+
+func (e ErrNeitherFlavorIDNorFlavorNameProvided) Error() string {
+	return "One and only one of the flavor ID and the flavor name must be provided."
+}
+
+type ErrNoClientProvidedForIDByName struct{ gophercloud.ErrMissingInput }
+
+func (e ErrNoClientProvidedForIDByName) Error() string {
+	return "A service client must be provided to find a resource ID by name."
+}
+
+// ErrInvalidHowParameterProvided is the error when an unknown value is given
+// for the `how` argument
+type ErrInvalidHowParameterProvided struct{ gophercloud.ErrInvalidInput }
+
+// ErrNoAdminPassProvided is the error when an administrative password isn't
+// provided for a server operation
+type ErrNoAdminPassProvided struct{ gophercloud.ErrMissingInput }
+
+// ErrNoImageIDProvided is the error when an image ID isn't provided for a server
+// operation
+type ErrNoImageIDProvided struct{ gophercloud.ErrMissingInput }
+
+// ErrNoIDProvided is the error when a server ID isn't provided for a server
+// operation
+type ErrNoIDProvided struct{ gophercloud.ErrMissingInput }
+
+// ErrServer is a generic error type for servers HTTP operations.
+type ErrServer struct {
+	gophercloud.ErrUnexpectedResponseCode
+	ID string
+}
+
+func (se ErrServer) Error() string {
+	return fmt.Sprintf("Error while executing HTTP request for server [%s]", se.ID)
+}
+
+// Error404 overrides the generic 404 error message.
+func (se ErrServer) Error404(e gophercloud.ErrUnexpectedResponseCode) error {
+	se.ErrUnexpectedResponseCode = e
+	return &ErrServerNotFound{se}
+}
+
+// ErrServerNotFound is the error when a 404 is received during server HTTP
+// operations.
+type ErrServerNotFound struct {
+	ErrServer
+}
+
+func (e ErrServerNotFound) Error() string {
+	return fmt.Sprintf("I couldn't find server [%s]", e.ID)
+}
diff --git a/openstack/compute/v2/servers/fixtures.go b/openstack/compute/v2/servers/fixtures.go
deleted file mode 100644
index 85cea70..0000000
--- a/openstack/compute/v2/servers/fixtures.go
+++ /dev/null
@@ -1,692 +0,0 @@
-// +build fixtures
-
-package servers
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ServerListBody contains the canned body of a servers.List response.
-const ServerListBody = `
-{
-	"servers": [
-		{
-			"status": "ACTIVE",
-			"updated": "2014-09-25T13:10:10Z",
-			"hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
-			"OS-EXT-SRV-ATTR:host": "devstack",
-			"addresses": {
-				"private": [
-					{
-						"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b",
-						"version": 4,
-						"addr": "10.0.0.32",
-						"OS-EXT-IPS:type": "fixed"
-					}
-				]
-			},
-			"links": [
-				{
-					"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
-					"rel": "self"
-				},
-				{
-					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
-					"rel": "bookmark"
-				}
-			],
-			"key_name": null,
-			"image": {
-				"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
-				"links": [
-					{
-						"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
-						"rel": "bookmark"
-					}
-				]
-			},
-			"OS-EXT-STS:task_state": null,
-			"OS-EXT-STS:vm_state": "active",
-			"OS-EXT-SRV-ATTR:instance_name": "instance-0000001e",
-			"OS-SRV-USG:launched_at": "2014-09-25T13:10:10.000000",
-			"OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
-			"flavor": {
-				"id": "1",
-				"links": [
-					{
-						"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
-						"rel": "bookmark"
-					}
-				]
-			},
-			"id": "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
-			"security_groups": [
-				{
-					"name": "default"
-				}
-			],
-			"OS-SRV-USG:terminated_at": null,
-			"OS-EXT-AZ:availability_zone": "nova",
-			"user_id": "9349aff8be7545ac9d2f1d00999a23cd",
-			"name": "herp",
-			"created": "2014-09-25T13:10:02Z",
-			"tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
-			"OS-DCF:diskConfig": "MANUAL",
-			"os-extended-volumes:volumes_attached": [],
-			"accessIPv4": "",
-			"accessIPv6": "",
-			"progress": 0,
-			"OS-EXT-STS:power_state": 1,
-			"config_drive": "",
-			"metadata": {}
-		},
-		{
-			"status": "ACTIVE",
-			"updated": "2014-09-25T13:04:49Z",
-			"hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
-			"OS-EXT-SRV-ATTR:host": "devstack",
-			"addresses": {
-				"private": [
-					{
-						"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
-						"version": 4,
-						"addr": "10.0.0.31",
-						"OS-EXT-IPS:type": "fixed"
-					}
-				]
-			},
-			"links": [
-				{
-					"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
-					"rel": "self"
-				},
-				{
-					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
-					"rel": "bookmark"
-				}
-			],
-			"key_name": null,
-			"image": {
-				"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
-				"links": [
-					{
-						"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
-						"rel": "bookmark"
-					}
-				]
-			},
-			"OS-EXT-STS:task_state": null,
-			"OS-EXT-STS:vm_state": "active",
-			"OS-EXT-SRV-ATTR:instance_name": "instance-0000001d",
-			"OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000",
-			"OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
-			"flavor": {
-				"id": "1",
-				"links": [
-					{
-						"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
-						"rel": "bookmark"
-					}
-				]
-			},
-			"id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
-			"security_groups": [
-				{
-					"name": "default"
-				}
-			],
-			"OS-SRV-USG:terminated_at": null,
-			"OS-EXT-AZ:availability_zone": "nova",
-			"user_id": "9349aff8be7545ac9d2f1d00999a23cd",
-			"name": "derp",
-			"created": "2014-09-25T13:04:41Z",
-			"tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
-			"OS-DCF:diskConfig": "MANUAL",
-			"os-extended-volumes:volumes_attached": [],
-			"accessIPv4": "",
-			"accessIPv6": "",
-			"progress": 0,
-			"OS-EXT-STS:power_state": 1,
-			"config_drive": "",
-			"metadata": {}
-		}
-	]
-}
-`
-
-// SingleServerBody is the canned body of a Get request on an existing server.
-const SingleServerBody = `
-{
-	"server": {
-		"status": "ACTIVE",
-		"updated": "2014-09-25T13:04:49Z",
-		"hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
-		"OS-EXT-SRV-ATTR:host": "devstack",
-		"addresses": {
-			"private": [
-				{
-					"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
-					"version": 4,
-					"addr": "10.0.0.31",
-					"OS-EXT-IPS:type": "fixed"
-				}
-			]
-		},
-		"links": [
-			{
-				"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
-				"rel": "self"
-			},
-			{
-				"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
-				"rel": "bookmark"
-			}
-		],
-		"key_name": null,
-		"image": {
-			"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
-			"links": [
-				{
-					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
-					"rel": "bookmark"
-				}
-			]
-		},
-		"OS-EXT-STS:task_state": null,
-		"OS-EXT-STS:vm_state": "active",
-		"OS-EXT-SRV-ATTR:instance_name": "instance-0000001d",
-		"OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000",
-		"OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
-		"flavor": {
-			"id": "1",
-			"links": [
-				{
-					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
-					"rel": "bookmark"
-				}
-			]
-		},
-		"id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
-		"security_groups": [
-			{
-				"name": "default"
-			}
-		],
-		"OS-SRV-USG:terminated_at": null,
-		"OS-EXT-AZ:availability_zone": "nova",
-		"user_id": "9349aff8be7545ac9d2f1d00999a23cd",
-		"name": "derp",
-		"created": "2014-09-25T13:04:41Z",
-		"tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
-		"OS-DCF:diskConfig": "MANUAL",
-		"os-extended-volumes:volumes_attached": [],
-		"accessIPv4": "",
-		"accessIPv6": "",
-		"progress": 0,
-		"OS-EXT-STS:power_state": 1,
-		"config_drive": "",
-		"metadata": {}
-	}
-}
-`
-
-const ServerPasswordBody = `
-{
-    "password": "xlozO3wLCBRWAa2yDjCCVx8vwNPypxnypmRYDa/zErlQ+EzPe1S/Gz6nfmC52mOlOSCRuUOmG7kqqgejPof6M7bOezS387zjq4LSvvwp28zUknzy4YzfFGhnHAdai3TxUJ26pfQCYrq8UTzmKF2Bq8ioSEtVVzM0A96pDh8W2i7BOz6MdoiVyiev/I1K2LsuipfxSJR7Wdke4zNXJjHHP2RfYsVbZ/k9ANu+Nz4iIH8/7Cacud/pphH7EjrY6a4RZNrjQskrhKYed0YERpotyjYk1eDtRe72GrSiXteqCM4biaQ5w3ruS+AcX//PXk3uJ5kC7d67fPXaVz4WaQRYMg=="
-}
-`
-
-var (
-	// ServerHerp is a Server struct that should correspond to the first result in ServerListBody.
-	ServerHerp = Server{
-		Status:  "ACTIVE",
-		Updated: "2014-09-25T13:10:10Z",
-		HostID:  "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
-		Addresses: map[string]interface{}{
-			"private": []interface{}{
-				map[string]interface{}{
-					"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b",
-					"version":                 float64(4),
-					"addr":                    "10.0.0.32",
-					"OS-EXT-IPS:type":         "fixed",
-				},
-			},
-		},
-		Links: []interface{}{
-			map[string]interface{}{
-				"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
-				"rel":  "self",
-			},
-			map[string]interface{}{
-				"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
-				"rel":  "bookmark",
-			},
-		},
-		Image: map[string]interface{}{
-			"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
-			"links": []interface{}{
-				map[string]interface{}{
-					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
-					"rel":  "bookmark",
-				},
-			},
-		},
-		Flavor: map[string]interface{}{
-			"id": "1",
-			"links": []interface{}{
-				map[string]interface{}{
-					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
-					"rel":  "bookmark",
-				},
-			},
-		},
-		ID:       "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
-		UserID:   "9349aff8be7545ac9d2f1d00999a23cd",
-		Name:     "herp",
-		Created:  "2014-09-25T13:10:02Z",
-		TenantID: "fcad67a6189847c4aecfa3c81a05783b",
-		Metadata: map[string]interface{}{},
-		SecurityGroups: []map[string]interface{}{
-			map[string]interface{}{
-				"name": "default",
-			},
-		},
-	}
-
-	// ServerDerp is a Server struct that should correspond to the second server in ServerListBody.
-	ServerDerp = Server{
-		Status:  "ACTIVE",
-		Updated: "2014-09-25T13:04:49Z",
-		HostID:  "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
-		Addresses: map[string]interface{}{
-			"private": []interface{}{
-				map[string]interface{}{
-					"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
-					"version":                 float64(4),
-					"addr":                    "10.0.0.31",
-					"OS-EXT-IPS:type":         "fixed",
-				},
-			},
-		},
-		Links: []interface{}{
-			map[string]interface{}{
-				"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
-				"rel":  "self",
-			},
-			map[string]interface{}{
-				"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
-				"rel":  "bookmark",
-			},
-		},
-		Image: map[string]interface{}{
-			"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
-			"links": []interface{}{
-				map[string]interface{}{
-					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
-					"rel":  "bookmark",
-				},
-			},
-		},
-		Flavor: map[string]interface{}{
-			"id": "1",
-			"links": []interface{}{
-				map[string]interface{}{
-					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
-					"rel":  "bookmark",
-				},
-			},
-		},
-		ID:       "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
-		UserID:   "9349aff8be7545ac9d2f1d00999a23cd",
-		Name:     "derp",
-		Created:  "2014-09-25T13:04:41Z",
-		TenantID: "fcad67a6189847c4aecfa3c81a05783b",
-		Metadata: map[string]interface{}{},
-		SecurityGroups: []map[string]interface{}{
-			map[string]interface{}{
-				"name": "default",
-			},
-		},
-	}
-)
-
-// HandleServerCreationSuccessfully sets up the test server to respond to a server creation request
-// with a given response.
-func HandleServerCreationSuccessfully(t *testing.T, response string) {
-	th.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{
-			"server": {
-				"name": "derp",
-				"imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb",
-				"flavorRef": "1"
-			}
-		}`)
-
-		w.WriteHeader(http.StatusAccepted)
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, response)
-	})
-}
-
-// HandleServerListSuccessfully sets up the test server to respond to a server List request.
-func HandleServerListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, ServerListBody)
-		case "9e5476bd-a4ec-4653-93d6-72c93aa682ba":
-			fmt.Fprintf(w, `{ "servers": [] }`)
-		default:
-			t.Fatalf("/servers/detail invoked with unexpected marker=[%s]", marker)
-		}
-	})
-}
-
-// HandleServerDeletionSuccessfully sets up the test server to respond to a server deletion request.
-func HandleServerDeletionSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/asdfasdfasdf", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleServerForceDeletionSuccessfully sets up the test server to respond to a server force deletion
-// request.
-func HandleServerForceDeletionSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/asdfasdfasdf/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{ "forceDelete": "" }`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-// HandleServerGetSuccessfully sets up the test server to respond to a server Get request.
-func HandleServerGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		fmt.Fprintf(w, SingleServerBody)
-	})
-}
-
-// HandleServerUpdateSuccessfully sets up the test server to respond to a server Update request.
-func HandleServerUpdateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestJSONRequest(t, r, `{ "server": { "name": "new-name" } }`)
-
-		fmt.Fprintf(w, SingleServerBody)
-	})
-}
-
-// HandleAdminPasswordChangeSuccessfully sets up the test server to respond to a server password
-// change request.
-func HandleAdminPasswordChangeSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{ "changePassword": { "adminPass": "new-password" } }`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-// HandleRebootSuccessfully sets up the test server to respond to a reboot request with success.
-func HandleRebootSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{ "reboot": { "type": "SOFT" } }`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-// HandleRebuildSuccessfully sets up the test server to respond to a rebuild request with success.
-func HandleRebuildSuccessfully(t *testing.T, response string) {
-	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `
-			{
-				"rebuild": {
-					"name": "new-name",
-					"adminPass": "swordfish",
-					"imageRef": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
-					"accessIPv4": "1.2.3.4"
-				}
-			}
-		`)
-
-		w.WriteHeader(http.StatusAccepted)
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, response)
-	})
-}
-
-// HandleServerRescueSuccessfully sets up the test server to respond to a server Rescue request.
-func HandleServerRescueSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{ "rescue": { "adminPass": "1234567890" } }`)
-
-		w.WriteHeader(http.StatusOK)
-		w.Write([]byte(`{ "adminPass": "1234567890" }`))
-	})
-}
-
-// HandleMetadatumGetSuccessfully sets up the test server to respond to a metadatum Get request.
-func HandleMetadatumGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.WriteHeader(http.StatusOK)
-		w.Header().Add("Content-Type", "application/json")
-		w.Write([]byte(`{ "meta": {"foo":"bar"}}`))
-	})
-}
-
-// HandleMetadatumCreateSuccessfully sets up the test server to respond to a metadatum Create request.
-func HandleMetadatumCreateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{
-			"meta": {
-				"foo": "bar"
-			}
-		}`)
-
-		w.WriteHeader(http.StatusOK)
-		w.Header().Add("Content-Type", "application/json")
-		w.Write([]byte(`{ "meta": {"foo":"bar"}}`))
-	})
-}
-
-// HandleMetadatumDeleteSuccessfully sets up the test server to respond to a metadatum Delete request.
-func HandleMetadatumDeleteSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleMetadataGetSuccessfully sets up the test server to respond to a metadata Get request.
-func HandleMetadataGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.WriteHeader(http.StatusOK)
-		w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`))
-	})
-}
-
-// HandleMetadataResetSuccessfully sets up the test server to respond to a metadata Create request.
-func HandleMetadataResetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{
-				"metadata": {
-					"foo": "bar",
-					"this": "that"
-				}
-			}`)
-
-		w.WriteHeader(http.StatusOK)
-		w.Header().Add("Content-Type", "application/json")
-		w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`))
-	})
-}
-
-// HandleMetadataUpdateSuccessfully sets up the test server to respond to a metadata Update request.
-func HandleMetadataUpdateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{
-				"metadata": {
-					"foo": "baz",
-					"this": "those"
-				}
-			}`)
-
-		w.WriteHeader(http.StatusOK)
-		w.Header().Add("Content-Type", "application/json")
-		w.Write([]byte(`{ "metadata": {"foo":"baz", "this":"those"}}`))
-	})
-}
-
-// ListAddressesExpected represents an expected repsonse from a ListAddresses request.
-var ListAddressesExpected = map[string][]Address{
-	"public": []Address{
-		Address{
-			Version: 4,
-			Address: "80.56.136.39",
-		},
-		Address{
-			Version: 6,
-			Address: "2001:4800:790e:510:be76:4eff:fe04:82a8",
-		},
-	},
-	"private": []Address{
-		Address{
-			Version: 4,
-			Address: "10.880.3.154",
-		},
-	},
-}
-
-// HandleAddressListSuccessfully sets up the test server to respond to a ListAddresses request.
-func HandleAddressListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/asdfasdfasdf/ips", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `{
-			"addresses": {
-				"public": [
-				{
-					"version": 4,
-					"addr": "50.56.176.35"
-				},
-				{
-					"version": 6,
-					"addr": "2001:4800:780e:510:be76:4eff:fe04:84a8"
-				}
-				],
-				"private": [
-				{
-					"version": 4,
-					"addr": "10.180.3.155"
-				}
-				]
-			}
-		}`)
-	})
-}
-
-// ListNetworkAddressesExpected represents an expected repsonse from a ListAddressesByNetwork request.
-var ListNetworkAddressesExpected = []Address{
-	Address{
-		Version: 4,
-		Address: "50.56.176.35",
-	},
-	Address{
-		Version: 6,
-		Address: "2001:4800:780e:510:be76:4eff:fe04:84a8",
-	},
-}
-
-// HandleNetworkAddressListSuccessfully sets up the test server to respond to a ListAddressesByNetwork request.
-func HandleNetworkAddressListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/asdfasdfasdf/ips/public", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `{
-			"public": [
-			{
-				"version": 4,
-				"addr": "50.56.176.35"
-				},
-				{
-					"version": 6,
-					"addr": "2001:4800:780e:510:be76:4eff:fe04:84a8"
-				}
-			]
-			}`)
-	})
-}
-
-// HandleCreateServerImageSuccessfully sets up the test server to respond to a TestCreateServerImage request.
-func HandleCreateServerImageSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/serverimage/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		w.Header().Add("Location", "https://0.0.0.0/images/xxxx-xxxxx-xxxxx-xxxx")
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-// HandlePasswordGetSuccessfully sets up the test server to respond to a password Get request.
-func HandlePasswordGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/servers/1234asdf/os-server-password", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		fmt.Fprintf(w, ServerPasswordBody)
-	})
-}
diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go
index e649053..6e23ada 100644
--- a/openstack/compute/v2/servers/requests.go
+++ b/openstack/compute/v2/servers/requests.go
@@ -3,13 +3,11 @@
 import (
 	"encoding/base64"
 	"encoding/json"
-	"errors"
-	"fmt"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/images"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -57,16 +55,12 @@
 // ToServerListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToServerListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List makes a request against the API to list servers accessible to you.
 func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
 	url := listDetailURL(client)
-
 	if opts != nil {
 		query, err := opts.ToServerListQuery()
 		if err != nil {
@@ -74,12 +68,9 @@
 		}
 		url += query
 	}
-
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
 		return ServerPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, url, createPageFn)
+	})
 }
 
 // CreateOptsBuilder describes struct types that can be accepted by the Create call.
@@ -129,93 +120,76 @@
 
 // CreateOpts specifies server creation parameters.
 type CreateOpts struct {
-	// Name [required] is the name to assign to the newly launched server.
-	Name string
+	// Name is the name to assign to the newly launched server.
+	Name string `json:"name" required:"true"`
 
 	// ImageRef [optional; required if ImageName is not provided] is the ID or full
 	// URL to the image that contains the server's OS and initial state.
 	// Also optional if using the boot-from-volume extension.
-	ImageRef string
+	ImageRef string `json:"imageRef"`
 
 	// ImageName [optional; required if ImageRef is not provided] is the name of the
 	// image that contains the server's OS and initial state.
 	// Also optional if using the boot-from-volume extension.
-	ImageName string
+	ImageName string `json:"-"`
 
 	// FlavorRef [optional; required if FlavorName is not provided] is the ID or
 	// full URL to the flavor that describes the server's specs.
-	FlavorRef string
+	FlavorRef string `json:"flavorRef"`
 
 	// FlavorName [optional; required if FlavorRef is not provided] is the name of
 	// the flavor that describes the server's specs.
-	FlavorName string
+	FlavorName string `json:"-"`
 
-	// SecurityGroups [optional] lists the names of the security groups to which this server should belong.
-	SecurityGroups []string
+	// SecurityGroups lists the names of the security groups to which this server should belong.
+	SecurityGroups []string `json:"-"`
 
-	// UserData [optional] contains configuration information or scripts to use upon launch.
+	// UserData contains configuration information or scripts to use upon launch.
 	// Create will base64-encode it for you.
-	UserData []byte
+	UserData []byte `json:"-"`
 
-	// AvailabilityZone [optional] in which to launch the server.
-	AvailabilityZone string
+	// AvailabilityZone in which to launch the server.
+	AvailabilityZone string `json:"availability_zone,omitempty"`
 
-	// Networks [optional] dictates how this server will be attached to available networks.
+	// Networks dictates how this server will be attached to available networks.
 	// By default, the server will be attached to all isolated networks for the tenant.
-	Networks []Network
+	Networks []Network `json:"-"`
 
-	// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
-	Metadata map[string]string
+	// Metadata contains key-value pairs (up to 255 bytes each) to attach to the server.
+	Metadata map[string]string `json:"-"`
 
-	// Personality [optional] includes files to inject into the server at launch.
+	// Personality includes files to inject into the server at launch.
 	// Create will base64-encode file contents for you.
-	Personality Personality
+	Personality Personality `json:"-"`
 
-	// ConfigDrive [optional] enables metadata injection through a configuration drive.
-	ConfigDrive bool
+	// ConfigDrive enables metadata injection through a configuration drive.
+	ConfigDrive *bool `json:"config_drive,omitempty"`
 
-	// AdminPass [optional] sets the root user password. If not set, a randomly-generated
-	// password will be created and returned in the response.
-	AdminPass string
+	// AdminPass sets the root user password. If not set, a randomly-generated
+	// password will be created and returned in the rponse.
+	AdminPass string `json:"adminPass,omitempty"`
 
-	// AccessIPv4 [optional] specifies an IPv4 address for the instance.
-	AccessIPv4 string
+	// AccessIPv4 specifies an IPv4 address for the instance.
+	AccessIPv4 string `json:"accessIPv4,omitempty"`
 
-	// AccessIPv6 [optional] specifies an IPv6 address for the instance.
-	AccessIPv6 string
+	// AccessIPv6 pecifies an IPv6 address for the instance.
+	AccessIPv6 string `json:"accessIPv6,omitempty"`
+
+	// ServiceClient will allow calls to be made to retrieve an image or
+	// flavor ID by name.
+	ServiceClient *gophercloud.ServiceClient `json:"-"`
 }
 
 // ToServerCreateMap assembles a request body based on the contents of a CreateOpts.
 func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
-	server := make(map[string]interface{})
-
-	server["name"] = opts.Name
-	server["imageRef"] = opts.ImageRef
-	server["imageName"] = opts.ImageName
-	server["flavorRef"] = opts.FlavorRef
-	server["flavorName"] = opts.FlavorName
+	b, err := gophercloud.BuildRequestBody(opts, "")
+	if err != nil {
+		return nil, err
+	}
 
 	if opts.UserData != nil {
 		encoded := base64.StdEncoding.EncodeToString(opts.UserData)
-		server["user_data"] = &encoded
-	}
-	if opts.ConfigDrive {
-		server["config_drive"] = "true"
-	}
-	if opts.AvailabilityZone != "" {
-		server["availability_zone"] = opts.AvailabilityZone
-	}
-	if opts.Metadata != nil {
-		server["metadata"] = opts.Metadata
-	}
-	if opts.AdminPass != "" {
-		server["adminPass"] = opts.AdminPass
-	}
-	if opts.AccessIPv4 != "" {
-		server["accessIPv4"] = opts.AccessIPv4
-	}
-	if opts.AccessIPv6 != "" {
-		server["accessIPv6"] = opts.AccessIPv6
+		b["user_data"] = &encoded
 	}
 
 	if len(opts.SecurityGroups) > 0 {
@@ -223,7 +197,7 @@
 		for i, groupName := range opts.SecurityGroups {
 			securityGroups[i] = map[string]interface{}{"name": groupName}
 		}
-		server["security_groups"] = securityGroups
+		b["security_groups"] = securityGroups
 	}
 
 	if len(opts.Networks) > 0 {
@@ -240,172 +214,127 @@
 				networks[i]["fixed_ip"] = net.FixedIP
 			}
 		}
-		server["networks"] = networks
-	}
-
-	if len(opts.Personality) > 0 {
-		server["personality"] = opts.Personality
-	}
-
-	return map[string]interface{}{"server": server}, nil
-}
-
-// Create requests a server to be provisioned to the user in the current tenant.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToServerCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
+		b["networks"] = networks
 	}
 
 	// If ImageRef isn't provided, use ImageName to ascertain the image ID.
-	if reqBody["server"].(map[string]interface{})["imageRef"].(string) == "" {
-		imageName := reqBody["server"].(map[string]interface{})["imageName"].(string)
-		if imageName == "" {
-			res.Err = errors.New("One and only one of ImageRef and ImageName must be provided.")
-			return res
+	if opts.ImageRef == "" {
+		if opts.ImageName == "" {
+			err := ErrNeitherImageIDNorImageNameProvided{}
+			err.Argument = "ImageRef/ImageName"
+			return nil, err
 		}
-		imageID, err := images.IDFromName(client, imageName)
+		if opts.ServiceClient == nil {
+			err := ErrNoClientProvidedForIDByName{}
+			err.Argument = "ServiceClient"
+			return nil, err
+		}
+		imageID, err := images.IDFromName(opts.ServiceClient, opts.ImageName)
 		if err != nil {
-			res.Err = err
-			return res
+			return nil, err
 		}
-		reqBody["server"].(map[string]interface{})["imageRef"] = imageID
+		b["imageRef"] = imageID
 	}
-	delete(reqBody["server"].(map[string]interface{}), "imageName")
 
 	// If FlavorRef isn't provided, use FlavorName to ascertain the flavor ID.
-	if reqBody["server"].(map[string]interface{})["flavorRef"].(string) == "" {
-		flavorName := reqBody["server"].(map[string]interface{})["flavorName"].(string)
-		if flavorName == "" {
-			res.Err = errors.New("One and only one of FlavorRef and FlavorName must be provided.")
-			return res
+	if opts.FlavorRef == "" {
+		if opts.FlavorName == "" {
+			err := ErrNeitherFlavorIDNorFlavorNameProvided{}
+			err.Argument = "FlavorRef/FlavorName"
+			return nil, err
 		}
-		flavorID, err := flavors.IDFromName(client, flavorName)
+		if opts.ServiceClient == nil {
+			err := ErrNoClientProvidedForIDByName{}
+			err.Argument = "ServiceClient"
+			return nil, err
+		}
+		flavorID, err := flavors.IDFromName(opts.ServiceClient, opts.FlavorName)
 		if err != nil {
-			res.Err = err
-			return res
+			return nil, err
 		}
-		reqBody["server"].(map[string]interface{})["flavorRef"] = flavorID
+		b["flavorRef"] = flavorID
 	}
-	delete(reqBody["server"].(map[string]interface{}), "flavorName")
 
-	_, res.Err = client.Post(listURL(client), reqBody, &res.Body, nil)
-	return res
+	return map[string]interface{}{"server": b}, nil
+}
+
+// Create requests a server to be provisioned to the user in the current tenant.
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	reqBody, err := opts.ToServerCreateMap()
+	if err != nil {
+		r.Err = err
+		return
+	}
+	_, r.Err = client.Post(listURL(client), reqBody, &r.Body, nil)
+	return
 }
 
 // Delete requests that a server previously provisioned be removed from your account.
-func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, id), nil)
-	return res
+func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = client.Delete(deleteURL(client, id), nil)
+	return
 }
 
-func ForceDelete(client *gophercloud.ServiceClient, id string) ActionResult {
-	var req struct {
-		ForceDelete string `json:"forceDelete"`
-	}
-
-	var res ActionResult
-	_, res.Err = client.Post(actionURL(client, id), req, nil, nil)
-	return res
-
+// ForceDelete forces the deletion of a server
+func ForceDelete(client *gophercloud.ServiceClient, id string) (r ActionResult) {
+	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil)
+	return
 }
 
 // Get requests details on a single server, by ID.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(getURL(client, id), &result.Body, &gophercloud.RequestOpts{
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 203},
 	})
-	return result
+	return
 }
 
 // UpdateOptsBuilder allows extensions to add additional attributes to the Update request.
 type UpdateOptsBuilder interface {
-	ToServerUpdateMap() map[string]interface{}
+	ToServerUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts specifies the base attributes that may be updated on an existing server.
 type UpdateOpts struct {
-	// Name [optional] changes the displayed name of the server.
+	// Name changes the displayed name of the server.
 	// The server host name will *not* change.
 	// Server names are not constrained to be unique, even within the same tenant.
-	Name string
+	Name string `json:"name,omitempty"`
 
-	// AccessIPv4 [optional] provides a new IPv4 address for the instance.
-	AccessIPv4 string
+	// AccessIPv4 provides a new IPv4 address for the instance.
+	AccessIPv4 string `json:"accessIPv4,omitempty"`
 
-	// AccessIPv6 [optional] provides a new IPv6 address for the instance.
-	AccessIPv6 string
+	// AccessIPv6 provides a new IPv6 address for the instance.
+	AccessIPv6 string `json:"accessIPv6,omitempty"`
 }
 
 // ToServerUpdateMap formats an UpdateOpts structure into a request body.
-func (opts UpdateOpts) ToServerUpdateMap() map[string]interface{} {
-	server := make(map[string]string)
-	if opts.Name != "" {
-		server["name"] = opts.Name
-	}
-	if opts.AccessIPv4 != "" {
-		server["accessIPv4"] = opts.AccessIPv4
-	}
-	if opts.AccessIPv6 != "" {
-		server["accessIPv6"] = opts.AccessIPv6
-	}
-	return map[string]interface{}{"server": server}
+func (opts UpdateOpts) ToServerUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "server")
 }
 
 // Update requests that various attributes of the indicated server be changed.
-func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var result UpdateResult
-	reqBody := opts.ToServerUpdateMap()
-	_, result.Err = client.Put(updateURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{
+func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToServerUpdateMap()
+	if err != nil {
+		r.Err = err
+		return
+	}
+	_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return result
+	return
 }
 
 // ChangeAdminPassword alters the administrator or root password for a specified server.
-func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) ActionResult {
-	var req struct {
-		ChangePassword struct {
-			AdminPass string `json:"adminPass"`
-		} `json:"changePassword"`
+func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) {
+	b := map[string]interface{}{
+		"changePassword": map[string]string{
+			"adminPass": newPassword,
+		},
 	}
-
-	req.ChangePassword.AdminPass = newPassword
-
-	var res ActionResult
-	_, res.Err = client.Post(actionURL(client, id), req, nil, nil)
-	return res
-}
-
-// ErrArgument errors occur when an argument supplied to a package function
-// fails to fall within acceptable values.  For example, the Reboot() function
-// expects the "how" parameter to be one of HardReboot or SoftReboot.  These
-// constants are (currently) strings, leading someone to wonder if they can pass
-// other string values instead, perhaps in an effort to break the API of their
-// provider.  Reboot() returns this error in this situation.
-//
-// Function identifies which function was called/which function is generating
-// the error.
-// Argument identifies which formal argument was responsible for producing the
-// error.
-// Value provides the value as it was passed into the function.
-type ErrArgument struct {
-	Function, Argument string
-	Value              interface{}
-}
-
-// Error yields a useful diagnostic for debugging purposes.
-func (e *ErrArgument) Error() string {
-	return fmt.Sprintf("Bad argument in call to %s, formal parameter %s, value %#v", e.Function, e.Argument, e.Value)
-}
-
-func (e *ErrArgument) String() string {
-	return e.Error()
+	_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
+	return
 }
 
 // RebootMethod describes the mechanisms by which a server reboot can be requested.
@@ -420,36 +349,41 @@
 	PowerCycle              = HardReboot
 )
 
+// RebootOptsBuilder is an interface that options must satisfy in order to be
+// used when rebooting a server instance
+type RebootOptsBuilder interface {
+	ToServerRebootMap() (map[string]interface{}, error)
+}
+
+// RebootOpts satisfies the RebootOptsBuilder interface
+type RebootOpts struct {
+	Type RebootMethod `json:"type" required:"true"`
+}
+
+// ToServerRebootMap allows RebootOpts to satisfiy the RebootOptsBuilder
+// interface
+func (opts *RebootOpts) ToServerRebootMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "reboot")
+}
+
 // Reboot requests that a given server reboot.
 // Two methods exist for rebooting a server:
 //
-// HardReboot (aka PowerCycle) restarts the server instance by physically cutting power to the machine, or if a VM,
+// HardReboot (aka PowerCycle) starts the server instance by physically cutting power to the machine, or if a VM,
 // terminating it at the hypervisor level.
 // It's done. Caput. Full stop.
-// Then, after a brief while, power is restored or the VM instance restarted.
+// Then, after a brief while, power is rtored or the VM instance rtarted.
 //
-// SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedures.
-// E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to restart the machine.
-func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) ActionResult {
-	var res ActionResult
-
-	if (how != SoftReboot) && (how != HardReboot) {
-		res.Err = &ErrArgument{
-			Function: "Reboot",
-			Argument: "how",
-			Value:    how,
-		}
-		return res
+// SoftReboot (aka OSReboot) simply tells the OS to rtart under its own procedur.
+// E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to rtart the machine.
+func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) {
+	b, err := opts.ToServerRebootMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-
-	reqBody := struct {
-		C map[string]string `json:"reboot"`
-	}{
-		map[string]string{"type": string(how)},
-	}
-
-	_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
-	return res
+	_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
+	return
 }
 
 // RebuildOptsBuilder is an interface that allows extensions to override the
@@ -461,87 +395,65 @@
 // RebuildOpts represents the configuration options used in a server rebuild
 // operation
 type RebuildOpts struct {
-	// Required. The ID of the image you want your server to be provisioned on
-	ImageID string
-
+	// The server's admin password
+	AdminPass string `json:"adminPass" required:"true"`
+	// The ID of the image you want your server to be provisioned on
+	ImageID   string `json:"imageRef"`
+	ImageName string `json:"-"`
+	//ImageName string `json:"-"`
 	// Name to set the server to
-	Name string
-
-	// Required. The server's admin password
-	AdminPass string
-
+	Name string `json:"name,omitempty"`
 	// AccessIPv4 [optional] provides a new IPv4 address for the instance.
-	AccessIPv4 string
-
+	AccessIPv4 string `json:"accessIPv4,omitempty"`
 	// AccessIPv6 [optional] provides a new IPv6 address for the instance.
-	AccessIPv6 string
-
+	AccessIPv6 string `json:"accessIPv6,omitempty"`
 	// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
-	Metadata map[string]string
-
+	Metadata map[string]string `json:"metadata,omitempty"`
 	// Personality [optional] includes files to inject into the server at launch.
 	// Rebuild will base64-encode file contents for you.
-	Personality Personality
+	Personality   Personality                `json:"personality,omitempty"`
+	ServiceClient *gophercloud.ServiceClient `json:"-"`
 }
 
 // ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON
 func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) {
-	var err error
-	server := make(map[string]interface{})
-
-	if opts.AdminPass == "" {
-		err = fmt.Errorf("AdminPass is required")
-	}
-
-	if opts.ImageID == "" {
-		err = fmt.Errorf("ImageID is required")
-	}
-
+	b, err := gophercloud.BuildRequestBody(opts, "")
 	if err != nil {
-		return server, err
+		return nil, err
 	}
 
-	server["name"] = opts.Name
-	server["adminPass"] = opts.AdminPass
-	server["imageRef"] = opts.ImageID
-
-	if opts.AccessIPv4 != "" {
-		server["accessIPv4"] = opts.AccessIPv4
+	// If ImageRef isn't provided, use ImageName to ascertain the image ID.
+	if opts.ImageID == "" {
+		if opts.ImageName == "" {
+			err := ErrNeitherImageIDNorImageNameProvided{}
+			err.Argument = "ImageRef/ImageName"
+			return nil, err
+		}
+		if opts.ServiceClient == nil {
+			err := ErrNoClientProvidedForIDByName{}
+			err.Argument = "ServiceClient"
+			return nil, err
+		}
+		imageID, err := images.IDFromName(opts.ServiceClient, opts.ImageName)
+		if err != nil {
+			return nil, err
+		}
+		b["imageRef"] = imageID
 	}
 
-	if opts.AccessIPv6 != "" {
-		server["accessIPv6"] = opts.AccessIPv6
-	}
-
-	if opts.Metadata != nil {
-		server["metadata"] = opts.Metadata
-	}
-
-	if len(opts.Personality) > 0 {
-		server["personality"] = opts.Personality
-	}
-
-	return map[string]interface{}{"rebuild": server}, nil
+	return map[string]interface{}{"rebuild": b}, nil
 }
 
 // Rebuild will reprovision the server according to the configuration options
 // provided in the RebuildOpts struct.
-func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) RebuildResult {
-	var result RebuildResult
-
-	if id == "" {
-		result.Err = fmt.Errorf("ID is required")
-		return result
-	}
-
-	reqBody, err := opts.ToServerRebuildMap()
+func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) (r RebuildResult) {
+	b, err := opts.ToServerRebuildMap()
 	if err != nil {
-		result.Err = err
-		return result
+		r.Err = err
+		return
 	}
-
-	_, result.Err = client.Post(actionURL(client, id), reqBody, &result.Body, nil)
-	return result
+	_, r.Err = client.Post(actionURL(client, id), b, &r.Body, nil)
+	return
 }
 
 // ResizeOptsBuilder is an interface that allows extensions to override the default structure of
@@ -553,57 +465,46 @@
 // ResizeOpts represents the configuration options used to control a Resize operation.
 type ResizeOpts struct {
 	// FlavorRef is the ID of the flavor you wish your server to become.
-	FlavorRef string
+	FlavorRef string `json:"flavorRef" required:"true"`
 }
 
 // ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON request body for the
 // Resize request.
 func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) {
-	resize := map[string]interface{}{
-		"flavorRef": opts.FlavorRef,
-	}
-
-	return map[string]interface{}{"resize": resize}, nil
+	return gophercloud.BuildRequestBody(opts, "resize")
 }
 
 // Resize instructs the provider to change the flavor of the server.
 // Note that this implies rebuilding it.
-// Unfortunately, one cannot pass rebuild parameters to the resize function.
-// When the resize completes, the server will be in RESIZE_VERIFY state.
+// Unfortunately, one cannot pass rebuild parameters to the rize function.
+// When the rize completes, the server will be in RESIZE_VERIFY state.
 // While in this state, you can explore the use of the new server's configuration.
-// If you like it, call ConfirmResize() to commit the resize permanently.
-// Otherwise, call RevertResize() to restore the old configuration.
-func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) ActionResult {
-	var res ActionResult
-	reqBody, err := opts.ToServerResizeMap()
+// If you like it, call ConfirmResize() to commit the rize permanently.
+// Otherwise, call RevertResize() to rtore the old configuration.
+func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) {
+	b, err := opts.ToServerResizeMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
-	return res
+	_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
+	return
 }
 
-// ConfirmResize confirms a previous resize operation on a server.
+// ConfirmResize confirms a previous rize operation on a server.
 // See Resize() for more details.
-func ConfirmResize(client *gophercloud.ServiceClient, id string) ActionResult {
-	var res ActionResult
-
-	reqBody := map[string]interface{}{"confirmResize": nil}
-	_, res.Err = client.Post(actionURL(client, id), reqBody, nil, &gophercloud.RequestOpts{
+func ConfirmResize(client *gophercloud.ServiceClient, id string) (r ActionResult) {
+	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{
 		OkCodes: []int{201, 202, 204},
 	})
-	return res
+	return
 }
 
-// RevertResize cancels a previous resize operation on a server.
+// RevertResize cancels a previous rize operation on a server.
 // See Resize() for more details.
-func RevertResize(client *gophercloud.ServiceClient, id string) ActionResult {
-	var res ActionResult
-	reqBody := map[string]interface{}{"revertResize": nil}
-	_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
-	return res
+func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) {
+	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil)
+	return
 }
 
 // RescueOptsBuilder is an interface that allows extensions to override the
@@ -617,38 +518,26 @@
 type RescueOpts struct {
 	// AdminPass is the desired administrative password for the instance in
 	// RESCUE mode. If it's left blank, the server will generate a password.
-	AdminPass string
+	AdminPass string `json:"adminPass,omitempty"`
 }
 
 // ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON
 // request body for the Rescue request.
 func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) {
-	server := make(map[string]interface{})
-	if opts.AdminPass != "" {
-		server["adminPass"] = opts.AdminPass
-	}
-	return map[string]interface{}{"rescue": server}, nil
+	return gophercloud.BuildRequestBody(opts, "rescue")
 }
 
 // Rescue instructs the provider to place the server into RESCUE mode.
-func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) RescueResult {
-	var result RescueResult
-
-	if id == "" {
-		result.Err = fmt.Errorf("ID is required")
-		return result
-	}
-	reqBody, err := opts.ToServerRescueMap()
+func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) {
+	b, err := opts.ToServerRescueMap()
 	if err != nil {
-		result.Err = err
-		return result
+		r.Err = err
+		return
 	}
-
-	_, result.Err = client.Post(actionURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return result
+	return
 }
 
 // ResetMetadataOptsBuilder allows extensions to add additional parameters to the
@@ -674,24 +563,22 @@
 // Note: Using this operation will erase any already-existing metadata and create
 // the new metadata provided. To keep any already-existing metadata, use the
 // UpdateMetadatas or UpdateMetadata function.
-func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) ResetMetadataResult {
-	var res ResetMetadataResult
-	metadata, err := opts.ToMetadataResetMap()
+func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) {
+	b, err := opts.ToMetadataResetMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-	_, res.Err = client.Put(metadataURL(client, id), metadata, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // Metadata requests all the metadata for the given server ID.
-func Metadata(client *gophercloud.ServiceClient, id string) GetMetadataResult {
-	var res GetMetadataResult
-	_, res.Err = client.Get(metadataURL(client, id), &res.Body, nil)
-	return res
+func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult) {
+	_, r.Err = client.Get(metadataURL(client, id), &r.Body, nil)
+	return
 }
 
 // UpdateMetadataOptsBuilder allows extensions to add additional parameters to the
@@ -703,17 +590,16 @@
 // UpdateMetadata updates (or creates) all the metadata specified by opts for the given server ID.
 // This operation does not affect already-existing metadata that is not specified
 // by opts.
-func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) UpdateMetadataResult {
-	var res UpdateMetadataResult
-	metadata, err := opts.ToMetadataUpdateMap()
+func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) {
+	b, err := opts.ToMetadataUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-	_, res.Err = client.Post(metadataURL(client, id), metadata, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // MetadatumOptsBuilder allows extensions to add additional parameters to the
@@ -728,7 +614,10 @@
 // ToMetadatumCreateMap assembles a body for a Create request based on the contents of a MetadataumOpts.
 func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string, error) {
 	if len(opts) != 1 {
-		return nil, "", errors.New("CreateMetadatum operation must have 1 and only 1 key-value pair.")
+		err := gophercloud.ErrInvalidInput{}
+		err.Argument = "servers.MetadatumOpts"
+		err.Info = "Must have 1 and only 1 key-value pair"
+		return nil, "", err
 	}
 	metadatum := map[string]interface{}{"meta": opts}
 	var key string
@@ -739,134 +628,112 @@
 }
 
 // CreateMetadatum will create or update the key-value pair with the given key for the given server ID.
-func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) CreateMetadatumResult {
-	var res CreateMetadatumResult
-	metadatum, key, err := opts.ToMetadatumCreateMap()
+func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) {
+	b, key, err := opts.ToMetadatumCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Put(metadatumURL(client, id, key), metadatum, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // Metadatum requests the key-value pair with the given key for the given server ID.
-func Metadatum(client *gophercloud.ServiceClient, id, key string) GetMetadatumResult {
-	var res GetMetadatumResult
-	_, res.Err = client.Request("GET", metadatumURL(client, id, key), gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
-	})
-	return res
+func Metadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) {
+	_, r.Err = client.Get(metadatumURL(client, id, key), &r.Body, nil)
+	return
 }
 
 // DeleteMetadatum will delete the key-value pair with the given key for the given server ID.
-func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) DeleteMetadatumResult {
-	var res DeleteMetadatumResult
-	_, res.Err = client.Delete(metadatumURL(client, id, key), nil)
-	return res
+func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) {
+	_, r.Err = client.Delete(metadatumURL(client, id, key), nil)
+	return
 }
 
 // ListAddresses makes a request against the API to list the servers IP addresses.
 func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager {
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page {
 		return AddressPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, listAddressesURL(client, id), createPageFn)
+	})
 }
 
 // ListAddressesByNetwork makes a request against the API to list the servers IP addresses
 // for the given network.
 func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager {
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page {
 		return NetworkAddressPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), createPageFn)
+	})
 }
 
-type CreateImageOpts struct {
-	// Name [required] of the image/snapshot
-	Name string
-	// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the created image.
-	Metadata map[string]string
-}
-
+// CreateImageOptsBuilder is the interface types must satisfy in order to be
+// used as CreateImage options
 type CreateImageOptsBuilder interface {
 	ToServerCreateImageMap() (map[string]interface{}, error)
 }
 
+// CreateImageOpts satisfies the CreateImageOptsBuilder
+type CreateImageOpts struct {
+	// Name of the image/snapshot
+	Name string `json:"name" required:"true"`
+	// Metadata contains key-value pairs (up to 255 bytes each) to attach to the created image.
+	Metadata map[string]string `json:"metadata,omitempty"`
+}
+
 // ToServerCreateImageMap formats a CreateImageOpts structure into a request body.
 func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) {
-	var err error
-	img := make(map[string]interface{})
-	if opts.Name == "" {
-		return nil, fmt.Errorf("Cannot create a server image without a name")
-	}
-	img["name"] = opts.Name
-	if opts.Metadata != nil {
-		img["metadata"] = opts.Metadata
-	}
-	createImage := make(map[string]interface{})
-	createImage["createImage"] = img
-	return createImage, err
+	return gophercloud.BuildRequestBody(opts, "createImage")
 }
 
 // CreateImage makes a request against the nova API to schedule an image to be created of the server
-func CreateImage(client *gophercloud.ServiceClient, serverId string, opts CreateImageOptsBuilder) CreateImageResult {
-	var res CreateImageResult
-	reqBody, err := opts.ToServerCreateImageMap()
+func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) {
+	b, err := opts.ToServerCreateImageMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-	response, err := client.Post(actionURL(client, serverId), reqBody, nil, &gophercloud.RequestOpts{
+	resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
 		OkCodes: []int{202},
 	})
-	res.Err = err
-	res.Header = response.Header
-	return res
+	r.Err = err
+	r.Header = resp.Header
+	return
 }
 
 // IDFromName is a convienience function that returns a server's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
-	serverCount := 0
-	serverID := ""
-	if name == "" {
-		return "", fmt.Errorf("A server name must be provided.")
+	count := 0
+	id := ""
+	allPages, err := List(client, nil).AllPages()
+	if err != nil {
+		return "", err
 	}
-	pager := List(client, nil)
-	pager.EachPage(func(page pagination.Page) (bool, error) {
-		serverList, err := ExtractServers(page)
-		if err != nil {
-			return false, err
-		}
 
-		for _, s := range serverList {
-			if s.Name == name {
-				serverCount++
-				serverID = s.ID
-			}
-		}
-		return true, nil
-	})
+	all, err := ExtractServers(allPages)
+	if err != nil {
+		return "", err
+	}
 
-	switch serverCount {
+	for _, f := range all {
+		if f.Name == name {
+			count++
+			id = f.ID
+		}
+	}
+
+	switch count {
 	case 0:
-		return "", fmt.Errorf("Unable to find server: %s", name)
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "server"}
 	case 1:
-		return serverID, nil
+		return id, nil
 	default:
-		return "", fmt.Errorf("Found %d servers matching %s", serverCount, name)
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "server"}
 	}
 }
 
 // GetPassword makes a request against the nova API to get the encrypted administrative password.
-func GetPassword(client *gophercloud.ServiceClient, serverId string) GetPasswordResult {
-	var res GetPasswordResult
-	_, res.Err = client.Request("GET", passwordURL(client, serverId), gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
-	})
-	return res
+func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) {
+	_, r.Err = client.Get(passwordURL(client, serverId), &r.Body, nil)
+	return
 }
diff --git a/openstack/compute/v2/servers/requests_test.go b/openstack/compute/v2/servers/requests_test.go
deleted file mode 100644
index 6e23c52..0000000
--- a/openstack/compute/v2/servers/requests_test.go
+++ /dev/null
@@ -1,391 +0,0 @@
-package servers
-
-import (
-	"encoding/base64"
-	"encoding/json"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListServers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleServerListSuccessfully(t)
-
-	pages := 0
-	err := List(client.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractServers(page)
-		if err != nil {
-			return false, err
-		}
-
-		if len(actual) != 2 {
-			t.Fatalf("Expected 2 servers, got %d", len(actual))
-		}
-		th.CheckDeepEquals(t, ServerHerp, actual[0])
-		th.CheckDeepEquals(t, ServerDerp, actual[1])
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-
-	if pages != 1 {
-		t.Errorf("Expected 1 page, saw %d", pages)
-	}
-}
-
-func TestListAllServers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleServerListSuccessfully(t)
-
-	allPages, err := List(client.ServiceClient(), ListOpts{}).AllPages()
-	th.AssertNoErr(t, err)
-	actual, err := ExtractServers(allPages)
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, ServerHerp, actual[0])
-	th.CheckDeepEquals(t, ServerDerp, actual[1])
-}
-
-func TestCreateServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleServerCreationSuccessfully(t, SingleServerBody)
-
-	actual, err := Create(client.ServiceClient(), CreateOpts{
-		Name:      "derp",
-		ImageRef:  "f90f6034-2570-4974-8351-6b49732ef2eb",
-		FlavorRef: "1",
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	th.CheckDeepEquals(t, ServerDerp, *actual)
-}
-
-func TestDeleteServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleServerDeletionSuccessfully(t)
-
-	res := Delete(client.ServiceClient(), "asdfasdfasdf")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestForceDeleteServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleServerForceDeletionSuccessfully(t)
-
-	res := ForceDelete(client.ServiceClient(), "asdfasdfasdf")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestGetServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleServerGetSuccessfully(t)
-
-	client := client.ServiceClient()
-	actual, err := Get(client, "1234asdf").Extract()
-	if err != nil {
-		t.Fatalf("Unexpected Get error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, ServerDerp, *actual)
-}
-
-func TestUpdateServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleServerUpdateSuccessfully(t)
-
-	client := client.ServiceClient()
-	actual, err := Update(client, "1234asdf", UpdateOpts{Name: "new-name"}).Extract()
-	if err != nil {
-		t.Fatalf("Unexpected Update error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, ServerDerp, *actual)
-}
-
-func TestChangeServerAdminPassword(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleAdminPasswordChangeSuccessfully(t)
-
-	res := ChangeAdminPassword(client.ServiceClient(), "1234asdf", "new-password")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestGetPassword(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePasswordGetSuccessfully(t)
-
-	res := GetPassword(client.ServiceClient(), "1234asdf")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestRebootServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleRebootSuccessfully(t)
-
-	res := Reboot(client.ServiceClient(), "1234asdf", SoftReboot)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestRebuildServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleRebuildSuccessfully(t, SingleServerBody)
-
-	opts := RebuildOpts{
-		Name:       "new-name",
-		AdminPass:  "swordfish",
-		ImageID:    "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
-		AccessIPv4: "1.2.3.4",
-	}
-
-	actual, err := Rebuild(client.ServiceClient(), "1234asdf", opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.CheckDeepEquals(t, ServerDerp, *actual)
-}
-
-func TestResizeServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{ "resize": { "flavorRef": "2" } }`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-
-	res := Resize(client.ServiceClient(), "1234asdf", ResizeOpts{FlavorRef: "2"})
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestConfirmResize(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{ "confirmResize": null }`)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := ConfirmResize(client.ServiceClient(), "1234asdf")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestRevertResize(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{ "revertResize": null }`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-
-	res := RevertResize(client.ServiceClient(), "1234asdf")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestRescue(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleServerRescueSuccessfully(t)
-
-	res := Rescue(client.ServiceClient(), "1234asdf", RescueOpts{
-		AdminPass: "1234567890",
-	})
-	th.AssertNoErr(t, res.Err)
-	adminPass, _ := res.Extract()
-	th.AssertEquals(t, "1234567890", adminPass)
-}
-
-func TestGetMetadatum(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleMetadatumGetSuccessfully(t)
-
-	expected := map[string]string{"foo": "bar"}
-	actual, err := Metadatum(client.ServiceClient(), "1234asdf", "foo").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestCreateMetadatum(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleMetadatumCreateSuccessfully(t)
-
-	expected := map[string]string{"foo": "bar"}
-	actual, err := CreateMetadatum(client.ServiceClient(), "1234asdf", MetadatumOpts{"foo": "bar"}).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestDeleteMetadatum(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleMetadatumDeleteSuccessfully(t)
-
-	err := DeleteMetadatum(client.ServiceClient(), "1234asdf", "foo").ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestGetMetadata(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleMetadataGetSuccessfully(t)
-
-	expected := map[string]string{"foo": "bar", "this": "that"}
-	actual, err := Metadata(client.ServiceClient(), "1234asdf").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestResetMetadata(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleMetadataResetSuccessfully(t)
-
-	expected := map[string]string{"foo": "bar", "this": "that"}
-	actual, err := ResetMetadata(client.ServiceClient(), "1234asdf", MetadataOpts{
-		"foo":  "bar",
-		"this": "that",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestUpdateMetadata(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleMetadataUpdateSuccessfully(t)
-
-	expected := map[string]string{"foo": "baz", "this": "those"}
-	actual, err := UpdateMetadata(client.ServiceClient(), "1234asdf", MetadataOpts{
-		"foo":  "baz",
-		"this": "those",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestListAddresses(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleAddressListSuccessfully(t)
-
-	expected := ListAddressesExpected
-	pages := 0
-	err := ListAddresses(client.ServiceClient(), "asdfasdfasdf").EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractAddresses(page)
-		th.AssertNoErr(t, err)
-
-		if len(actual) != 2 {
-			t.Fatalf("Expected 2 networks, got %d", len(actual))
-		}
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, pages)
-}
-
-func TestListAddressesByNetwork(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleNetworkAddressListSuccessfully(t)
-
-	expected := ListNetworkAddressesExpected
-	pages := 0
-	err := ListAddressesByNetwork(client.ServiceClient(), "asdfasdfasdf", "public").EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractNetworkAddresses(page)
-		th.AssertNoErr(t, err)
-
-		if len(actual) != 2 {
-			t.Fatalf("Expected 2 addresses, got %d", len(actual))
-		}
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, pages)
-}
-
-func TestCreateServerImage(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreateServerImageSuccessfully(t)
-
-	_, err := CreateImage(client.ServiceClient(), "serverimage", CreateImageOpts{Name: "test"}).ExtractImageID()
-	th.AssertNoErr(t, err)
-}
-
-func TestMarshalPersonality(t *testing.T) {
-	name := "/etc/test"
-	contents := []byte("asdfasdf")
-
-	personality := Personality{
-		&File{
-			Path:     name,
-			Contents: contents,
-		},
-	}
-
-	data, err := json.Marshal(personality)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var actual []map[string]string
-	err = json.Unmarshal(data, &actual)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if len(actual) != 1 {
-		t.Fatal("expected personality length 1")
-	}
-
-	if actual[0]["path"] != name {
-		t.Fatal("file path incorrect")
-	}
-
-	if actual[0]["contents"] != base64.StdEncoding.EncodeToString(contents) {
-		t.Fatal("file contents incorrect")
-	}
-}
diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go
index 406f689..023d0dd 100644
--- a/openstack/compute/v2/servers/results.go
+++ b/openstack/compute/v2/servers/results.go
@@ -6,11 +6,10 @@
 	"fmt"
 	"net/url"
 	"path"
-	"reflect"
 
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
 )
 
 type serverResult struct {
@@ -19,29 +18,11 @@
 
 // Extract interprets any serverResult as a Server, if possible.
 func (r serverResult) Extract() (*Server, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Server *Server `json:"server"`
 	}
-
-	var response struct {
-		Server Server `mapstructure:"server"`
-	}
-
-	config := &mapstructure.DecoderConfig{
-		DecodeHook: toMapFromString,
-		Result:     &response,
-	}
-	decoder, err := mapstructure.NewDecoder(config)
-	if err != nil {
-		return nil, err
-	}
-
-	err = decoder.Decode(r.Body)
-	if err != nil {
-		return nil, err
-	}
-
-	return &response.Server, nil
+	err := r.ExtractInto(&s)
+	return s.Server, err
 }
 
 // CreateResult temporarily contains the response from a Create call.
@@ -133,42 +114,37 @@
 	// Get the image id from the header
 	u, err := url.ParseRequestURI(res.Header.Get("Location"))
 	if err != nil {
-		return "", fmt.Errorf("Failed to parse the image id: %s", err.Error())
+		return "", err
 	}
-	imageId := path.Base(u.Path)
-	if imageId == "." || imageId == "/" {
+	imageID := path.Base(u.Path)
+	if imageID == "." || imageID == "/" {
 		return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u)
 	}
-	return imageId, nil
+	return imageID, nil
 }
 
 // Extract interprets any RescueResult as an AdminPass, if possible.
 func (r RescueResult) Extract() (string, error) {
-	if r.Err != nil {
-		return "", r.Err
+	var s struct {
+		AdminPass string `json:"adminPass"`
 	}
-
-	var response struct {
-		AdminPass string `mapstructure:"adminPass"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-	return response.AdminPass, err
+	err := r.ExtractInto(&s)
+	return s.AdminPass, err
 }
 
 // Server exposes only the standard OpenStack fields corresponding to a given server on the user's account.
 type Server struct {
 	// ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.
-	ID string
+	ID string `json:"id"`
 
 	// TenantID identifies the tenant owning this server resource.
-	TenantID string `mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// UserID uniquely identifies the user account owning the tenant.
-	UserID string `mapstructure:"user_id"`
+	UserID string `json:"user_id"`
 
 	// Name contains the human-readable name for the server.
-	Name string
+	Name string `json:"name"`
 
 	// Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
 	Updated string
@@ -202,14 +178,14 @@
 	Links []interface{}
 
 	// KeyName indicates which public key was injected into the server on launch.
-	KeyName string `json:"key_name" mapstructure:"key_name"`
+	KeyName string `json:"key_name"`
 
 	// AdminPass will generally be empty ("").  However, it will contain the administrative password chosen when provisioning a new server without a set AdminPass setting in the first place.
 	// Note that this is the ONLY time this field will be valid.
-	AdminPass string `json:"adminPass" mapstructure:"adminPass"`
+	AdminPass string `json:"adminPass"`
 
 	// SecurityGroups includes the security groups that this instance has applied to it
-	SecurityGroups []map[string]interface{} `json:"security_groups" mapstructure:"security_groups"`
+	SecurityGroups []map[string]interface{} `json:"security_groups"`
 }
 
 // ServerPage abstracts the raw results of making a List() request against the API.
@@ -222,47 +198,28 @@
 // IsEmpty returns true if a page contains no Server results.
 func (page ServerPage) IsEmpty() (bool, error) {
 	servers, err := ExtractServers(page)
-	if err != nil {
-		return true, err
-	}
-	return len(servers) == 0, nil
+	return len(servers) == 0, err
 }
 
 // NextPageURL uses the response's embedded link reference to navigate to the next page of results.
 func (page ServerPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"servers_links"`
+	var s struct {
+		Links []gophercloud.Link `json:"servers_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(page.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities.
-func ExtractServers(page pagination.Page) ([]Server, error) {
-	casted := page.(ServerPage).Body
-
-	var response struct {
-		Servers []Server `mapstructure:"servers"`
+func ExtractServers(r pagination.Page) ([]Server, error) {
+	var s struct {
+		Servers []Server `json:"servers"`
 	}
-
-	config := &mapstructure.DecoderConfig{
-		DecodeHook: toMapFromString,
-		Result:     &response,
-	}
-	decoder, err := mapstructure.NewDecoder(config)
-	if err != nil {
-		return nil, err
-	}
-
-	err = decoder.Decode(casted)
-
-	return response.Servers, err
+	err := (r.(ServerPage)).ExtractInto(&s)
+	return s.Servers, err
 }
 
 // MetadataResult contains the result of a call for (potentially) multiple key-value pairs.
@@ -307,43 +264,26 @@
 
 // Extract interprets any MetadataResult as a Metadata, if possible.
 func (r MetadataResult) Extract() (map[string]string, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Metadata map[string]string `json:"metadata"`
 	}
-
-	var response struct {
-		Metadata map[string]string `mapstructure:"metadata"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-	return response.Metadata, err
+	err := r.ExtractInto(&s)
+	return s.Metadata, err
 }
 
 // Extract interprets any MetadatumResult as a Metadatum, if possible.
 func (r MetadatumResult) Extract() (map[string]string, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Metadatum map[string]string `json:"meta"`
 	}
-
-	var response struct {
-		Metadatum map[string]string `mapstructure:"meta"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-	return response.Metadatum, err
-}
-
-func toMapFromString(from reflect.Kind, to reflect.Kind, data interface{}) (interface{}, error) {
-	if (from == reflect.String) && (to == reflect.Map) {
-		return map[string]interface{}{}, nil
-	}
-	return data, nil
+	err := r.ExtractInto(&s)
+	return s.Metadatum, err
 }
 
 // Address represents an IP address.
 type Address struct {
-	Version int    `mapstructure:"version"`
-	Address string `mapstructure:"addr"`
+	Version int    `json:"version"`
+	Address string `json:"addr"`
 }
 
 // AddressPage abstracts the raw results of making a ListAddresses() request against the API.
@@ -356,27 +296,17 @@
 // IsEmpty returns true if an AddressPage contains no networks.
 func (r AddressPage) IsEmpty() (bool, error) {
 	addresses, err := ExtractAddresses(r)
-	if err != nil {
-		return true, err
-	}
-	return len(addresses) == 0, nil
+	return len(addresses) == 0, err
 }
 
 // ExtractAddresses interprets the results of a single page from a ListAddresses() call,
 // producing a map of addresses.
-func ExtractAddresses(page pagination.Page) (map[string][]Address, error) {
-	casted := page.(AddressPage).Body
-
-	var response struct {
-		Addresses map[string][]Address `mapstructure:"addresses"`
+func ExtractAddresses(r pagination.Page) (map[string][]Address, error) {
+	var s struct {
+		Addresses map[string][]Address `json:"addresses"`
 	}
-
-	err := mapstructure.Decode(casted, &response)
-	if err != nil {
-		return nil, err
-	}
-
-	return response.Addresses, err
+	err := (r.(AddressPage)).ExtractInto(&s)
+	return s.Addresses, err
 }
 
 // NetworkAddressPage abstracts the raw results of making a ListAddressesByNetwork() request against the API.
@@ -389,27 +319,22 @@
 // IsEmpty returns true if a NetworkAddressPage contains no addresses.
 func (r NetworkAddressPage) IsEmpty() (bool, error) {
 	addresses, err := ExtractNetworkAddresses(r)
-	if err != nil {
-		return true, err
-	}
-	return len(addresses) == 0, nil
+	return len(addresses) == 0, err
 }
 
 // ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call,
 // producing a slice of addresses.
-func ExtractNetworkAddresses(page pagination.Page) ([]Address, error) {
-	casted := page.(NetworkAddressPage).Body
-
-	var response map[string][]Address
-	err := mapstructure.Decode(casted, &response)
+func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) {
+	var s map[string][]Address
+	err := (r.(NetworkAddressPage)).ExtractInto(&s)
 	if err != nil {
 		return nil, err
 	}
 
 	var key string
-	for k := range response {
+	for k := range s {
 		key = k
 	}
 
-	return response[key], err
+	return s[key], err
 }
diff --git a/openstack/compute/v2/servers/results_test.go b/openstack/compute/v2/servers/results_test.go
deleted file mode 100644
index 9ee5579..0000000
--- a/openstack/compute/v2/servers/results_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// +build fixtures
-
-package servers
-
-import (
-	"crypto/rsa"
-	"encoding/json"
-	"fmt"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-	"golang.org/x/crypto/ssh"
-)
-
-// Fail - No password in JSON.
-func TestExtractPassword_no_pwd_data(t *testing.T) {
-
-	var dejson interface{}
-	err := json.Unmarshal([]byte(`{ "Crappy data": ".-.-." }`), &dejson)
-	if err != nil {
-		t.Fatalf("%s", err)
-	}
-	resp := GetPasswordResult{gophercloud.Result{Body: dejson}}
-
-	pwd, err := resp.ExtractPassword(nil)
-	th.AssertEquals(t, pwd, "")
-}
-
-// Ok - return encrypted password when no private key is given.
-func TestExtractPassword_encrypted_pwd(t *testing.T) {
-
-	var dejson interface{}
-	sejson := []byte(`{"password":"PP8EnwPO9DhEc8+O/6CKAkPF379mKsUsfFY6yyw0734XXvKsSdV9KbiHQ2hrBvzeZxtGMrlFaikVunCRizyLLWLMuOi4hoH+qy9F9sQid61gQIGkxwDAt85d/7Eau2/KzorFnZhgxArl7IiqJ67X6xjKkR3zur+Yp3V/mtVIehpPYIaAvPbcp2t4mQXl1I9J8yrQfEZOctLL1L4heDEVXnxvNihVLK6pivlVggp6SZCtjj9cduZGrYGsxsOCso1dqJQr7GCojfwvuLOoG0OYwEGuWVTZppxWxi/q1QgeHFhGKA5QUXlz7pS71oqpjYsTeViuHnfvlqb5TVYZpQ1haw=="}`)
-
-	err := json.Unmarshal(sejson, &dejson)
-	fmt.Printf("%v\n", dejson)
-	if err != nil {
-		t.Fatalf("%s", err)
-	}
-	resp := GetPasswordResult{gophercloud.Result{Body: dejson}}
-
-	pwd, err := resp.ExtractPassword(nil)
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, "PP8EnwPO9DhEc8+O/6CKAkPF379mKsUsfFY6yyw0734XXvKsSdV9KbiHQ2hrBvzeZxtGMrlFaikVunCRizyLLWLMuOi4hoH+qy9F9sQid61gQIGkxwDAt85d/7Eau2/KzorFnZhgxArl7IiqJ67X6xjKkR3zur+Yp3V/mtVIehpPYIaAvPbcp2t4mQXl1I9J8yrQfEZOctLL1L4heDEVXnxvNihVLK6pivlVggp6SZCtjj9cduZGrYGsxsOCso1dqJQr7GCojfwvuLOoG0OYwEGuWVTZppxWxi/q1QgeHFhGKA5QUXlz7pS71oqpjYsTeViuHnfvlqb5TVYZpQ1haw==", pwd)
-}
-
-// Ok - return decrypted password when private key is given.
-// Decrytion can be verified by:
-//   echo "<enc_pwd>" | base64 -D | openssl rsautl -decrypt -inkey <privateKey.pem>
-func TestExtractPassword_decrypted_pwd(t *testing.T) {
-
-	privateKey, err := ssh.ParseRawPrivateKey([]byte(`
------BEGIN RSA PRIVATE KEY-----
-MIIEpQIBAAKCAQEAo1ODZgwMVdTJYim9UYuYhowoPMhGEuV5IRZjcJ315r7RBSC+
-yEiBb1V+jhf+P8fzAyU35lkBzZGDr7E3jxSesbOuYT8cItQS4ErUnI1LGuqvMxwv
-X3GMyE/HmOcaiODF1XZN3Ur5pMJdVknnmczgUsW0hT98Udrh3MQn9WSuh/6LRy6+
-x1QsKHOCLFPnkhWa3LKyxmpQq/Gvhz+6NLe+gt8MFullA5mKQxBJ/K6laVHeaMlw
-JG3GCX0EZhRlvzoV8koIBKZtbKFolFr8ZtxBm3R5LvnyrtOvp22sa+xeItUT5kG1
-ZnbGNdK87oYW+VigEUfzT/+8R1i6E2QIXoeZiQIDAQABAoIBAQCVZ70IqbbTAW8j
-RAlyQh/J3Qal65LmkFJJKUDX8TfT1/Q/G6BKeMEmxm+Zrmsfj1pHI1HKftt+YEG1
-g4jOc09kQXkgbmnfll6aHPn3J+1vdwXD3GGdjrL5PrnYrngAhJWU2r8J0x8hT8ew
-OrUJZXhDX6XuSpAAFRmOKUZgXbSmo4X+LZX76ACnarselJt5FL724ECvpWJ7xxC4
-FMzvp4RqMmNFvv/Uq9lE/EmoSk4dviYyIZZ16DbDNyc9k/sGqCAMktCEwZ3EQm//
-S5bkNhgP6oUXjluWy53aPRgykEylgDWo5SSdSEyKnw/fciU0xdprA9JrBGIcTyHS
-/k2kgD4xAoGBANTkJ88Q0YrxX3fZNZVqcn00XKTxPGmxN5LRs7eV743q30AxK5Db
-QU8iwaAA1IKUWV5DLhgUTNsDCOPUPue4aOSBD3/sj+WEmvIhj7afDL5didkYHsqf
-fDnhFHq7y/3i57d428C7BwwR79pGWVyi7vH3pfu9A1iwl1aNOae+zvbVAoGBAMRm
-AmwQ9fJ3Qc44jysFK/yliLRGdShjkMMah5G3JlrelwfPtwPwEL2EHHhJB/C1acMs
-n6Q6RaoF6WNSZUY65ksQg7aPOYf2X0FTFwQJvwDJ4qlWjmq7w+tQ0AoGJG+dVUmQ
-zHZ/Y+HokSXzz9c4oevk4v/rMgAQ00WHrTdtIhnlAoGBALIJJ72D7CkNGHCq5qPQ
-xHQukPejgolFGhufYXM7YX3GmPMe67cVlTVv9Isxhoa5N0+cUPT0LR3PGOUm/4Bb
-eOT3hZXOqLwhvE6XgI8Rzd95bClwgXekDoh80dqeKMdmta961BQGlKskaPiacmsF
-G1yhZV70P9Mwwy8vpbLB4GUNAoGAbTwbjsWkNfa0qCF3J8NZoszjCvnBQfSW2J1R
-1+8ZKyNwt0yFi3Ajr3TibNiZzPzp1T9lj29FvfpJxA9Y+sXZvthxmcFxizix5GB1
-ha5yCNtA8VSOI7lJkAFDpL+j1lyYyjD6N9JE2KqEyKoh6J+8F7sXsqW7CqRRDfQX
-mKNfey0CgYEAxcEoNoADN2hRl7qY9rbQfVvQb3RkoQkdHhl9gpLFCcV32IP8R4xg
-09NbQK5OmgcIuZhLVNzTmUHJbabEGeXqIFIV0DsqECAt3WzbDyKQO23VJysFD46c
-KSde3I0ybDz7iS2EtceKB7m4C0slYd+oBkm4efuF00rCOKDwpFq45m0=
------END RSA PRIVATE KEY-----
-`))
-	if err != nil {
-		t.Fatalf("Error parsing private key: %s\n", err)
-	}
-
-	var dejson interface{}
-	sejson := []byte(`{"password":"PP8EnwPO9DhEc8+O/6CKAkPF379mKsUsfFY6yyw0734XXvKsSdV9KbiHQ2hrBvzeZxtGMrlFaikVunCRizyLLWLMuOi4hoH+qy9F9sQid61gQIGkxwDAt85d/7Eau2/KzorFnZhgxArl7IiqJ67X6xjKkR3zur+Yp3V/mtVIehpPYIaAvPbcp2t4mQXl1I9J8yrQfEZOctLL1L4heDEVXnxvNihVLK6pivlVggp6SZCtjj9cduZGrYGsxsOCso1dqJQr7GCojfwvuLOoG0OYwEGuWVTZppxWxi/q1QgeHFhGKA5QUXlz7pS71oqpjYsTeViuHnfvlqb5TVYZpQ1haw=="}`)
-
-	err = json.Unmarshal(sejson, &dejson)
-	fmt.Printf("%v\n", dejson)
-	if err != nil {
-		t.Fatalf("%s", err)
-	}
-	resp := GetPasswordResult{gophercloud.Result{Body: dejson}}
-
-	pwd, err := resp.ExtractPassword(privateKey.(*rsa.PrivateKey))
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, "ruZKK0tqxRfYm5t7lSJq", pwd)
-}
-
-func TestListAddressesAllPages(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleAddressListSuccessfully(t)
-
-	allPages, err := ListAddresses(client.ServiceClient(), "asdfasdfasdf").AllPages()
-	th.AssertNoErr(t, err)
-	_, err = ExtractAddresses(allPages)
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/compute/v2/servers/testing/doc.go b/openstack/compute/v2/servers/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/compute/v2/servers/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go
new file mode 100644
index 0000000..b4fb7ff
--- /dev/null
+++ b/openstack/compute/v2/servers/testing/fixtures.go
@@ -0,0 +1,722 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ServerListBody contains the canned body of a servers.List response.
+const ServerListBody = `
+{
+	"servers": [
+		{
+			"status": "ACTIVE",
+			"updated": "2014-09-25T13:10:10Z",
+			"hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+			"OS-EXT-SRV-ATTR:host": "devstack",
+			"addresses": {
+				"private": [
+					{
+						"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b",
+						"version": 4,
+						"addr": "10.0.0.32",
+						"OS-EXT-IPS:type": "fixed"
+					}
+				]
+			},
+			"links": [
+				{
+					"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+					"rel": "self"
+				},
+				{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+					"rel": "bookmark"
+				}
+			],
+			"key_name": null,
+			"image": {
+				"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
+				"links": [
+					{
+						"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+						"rel": "bookmark"
+					}
+				]
+			},
+			"OS-EXT-STS:task_state": null,
+			"OS-EXT-STS:vm_state": "active",
+			"OS-EXT-SRV-ATTR:instance_name": "instance-0000001e",
+			"OS-SRV-USG:launched_at": "2014-09-25T13:10:10.000000",
+			"OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
+			"flavor": {
+				"id": "1",
+				"links": [
+					{
+						"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
+						"rel": "bookmark"
+					}
+				]
+			},
+			"id": "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+			"security_groups": [
+				{
+					"name": "default"
+				}
+			],
+			"OS-SRV-USG:terminated_at": null,
+			"OS-EXT-AZ:availability_zone": "nova",
+			"user_id": "9349aff8be7545ac9d2f1d00999a23cd",
+			"name": "herp",
+			"created": "2014-09-25T13:10:02Z",
+			"tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
+			"OS-DCF:diskConfig": "MANUAL",
+			"os-extended-volumes:volumes_attached": [],
+			"accessIPv4": "",
+			"accessIPv6": "",
+			"progress": 0,
+			"OS-EXT-STS:power_state": 1,
+			"config_drive": "",
+			"metadata": {}
+		},
+		{
+			"status": "ACTIVE",
+			"updated": "2014-09-25T13:04:49Z",
+			"hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+			"OS-EXT-SRV-ATTR:host": "devstack",
+			"addresses": {
+				"private": [
+					{
+						"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
+						"version": 4,
+						"addr": "10.0.0.31",
+						"OS-EXT-IPS:type": "fixed"
+					}
+				]
+			},
+			"links": [
+				{
+					"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+					"rel": "self"
+				},
+				{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+					"rel": "bookmark"
+				}
+			],
+			"key_name": null,
+			"image": {
+				"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
+				"links": [
+					{
+						"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+						"rel": "bookmark"
+					}
+				]
+			},
+			"OS-EXT-STS:task_state": null,
+			"OS-EXT-STS:vm_state": "active",
+			"OS-EXT-SRV-ATTR:instance_name": "instance-0000001d",
+			"OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000",
+			"OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
+			"flavor": {
+				"id": "1",
+				"links": [
+					{
+						"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
+						"rel": "bookmark"
+					}
+				]
+			},
+			"id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+			"security_groups": [
+				{
+					"name": "default"
+				}
+			],
+			"OS-SRV-USG:terminated_at": null,
+			"OS-EXT-AZ:availability_zone": "nova",
+			"user_id": "9349aff8be7545ac9d2f1d00999a23cd",
+			"name": "derp",
+			"created": "2014-09-25T13:04:41Z",
+			"tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
+			"OS-DCF:diskConfig": "MANUAL",
+			"os-extended-volumes:volumes_attached": [],
+			"accessIPv4": "",
+			"accessIPv6": "",
+			"progress": 0,
+			"OS-EXT-STS:power_state": 1,
+			"config_drive": "",
+			"metadata": {}
+		}
+	]
+}
+`
+
+// SingleServerBody is the canned body of a Get request on an existing server.
+const SingleServerBody = `
+{
+	"server": {
+		"status": "ACTIVE",
+		"updated": "2014-09-25T13:04:49Z",
+		"hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+		"OS-EXT-SRV-ATTR:host": "devstack",
+		"addresses": {
+			"private": [
+				{
+					"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
+					"version": 4,
+					"addr": "10.0.0.31",
+					"OS-EXT-IPS:type": "fixed"
+				}
+			]
+		},
+		"links": [
+			{
+				"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+				"rel": "self"
+			},
+			{
+				"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+				"rel": "bookmark"
+			}
+		],
+		"key_name": null,
+		"image": {
+			"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
+			"links": [
+				{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+					"rel": "bookmark"
+				}
+			]
+		},
+		"OS-EXT-STS:task_state": null,
+		"OS-EXT-STS:vm_state": "active",
+		"OS-EXT-SRV-ATTR:instance_name": "instance-0000001d",
+		"OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000",
+		"OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
+		"flavor": {
+			"id": "1",
+			"links": [
+				{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
+					"rel": "bookmark"
+				}
+			]
+		},
+		"id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+		"security_groups": [
+			{
+				"name": "default"
+			}
+		],
+		"OS-SRV-USG:terminated_at": null,
+		"OS-EXT-AZ:availability_zone": "nova",
+		"user_id": "9349aff8be7545ac9d2f1d00999a23cd",
+		"name": "derp",
+		"created": "2014-09-25T13:04:41Z",
+		"tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
+		"OS-DCF:diskConfig": "MANUAL",
+		"os-extended-volumes:volumes_attached": [],
+		"accessIPv4": "",
+		"accessIPv6": "",
+		"progress": 0,
+		"OS-EXT-STS:power_state": 1,
+		"config_drive": "",
+		"metadata": {}
+	}
+}
+`
+
+const ServerPasswordBody = `
+{
+    "password": "xlozO3wLCBRWAa2yDjCCVx8vwNPypxnypmRYDa/zErlQ+EzPe1S/Gz6nfmC52mOlOSCRuUOmG7kqqgejPof6M7bOezS387zjq4LSvvwp28zUknzy4YzfFGhnHAdai3TxUJ26pfQCYrq8UTzmKF2Bq8ioSEtVVzM0A96pDh8W2i7BOz6MdoiVyiev/I1K2LsuipfxSJR7Wdke4zNXJjHHP2RfYsVbZ/k9ANu+Nz4iIH8/7Cacud/pphH7EjrY6a4RZNrjQskrhKYed0YERpotyjYk1eDtRe72GrSiXteqCM4biaQ5w3ruS+AcX//PXk3uJ5kC7d67fPXaVz4WaQRYMg=="
+}
+`
+
+var (
+	// ServerHerp is a Server struct that should correspond to the first result in ServerListBody.
+	ServerHerp = servers.Server{
+		Status:  "ACTIVE",
+		Updated: "2014-09-25T13:10:10Z",
+		HostID:  "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+		Addresses: map[string]interface{}{
+			"private": []interface{}{
+				map[string]interface{}{
+					"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b",
+					"version":                 float64(4),
+					"addr":                    "10.0.0.32",
+					"OS-EXT-IPS:type":         "fixed",
+				},
+			},
+		},
+		Links: []interface{}{
+			map[string]interface{}{
+				"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+				"rel":  "self",
+			},
+			map[string]interface{}{
+				"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+				"rel":  "bookmark",
+			},
+		},
+		Image: map[string]interface{}{
+			"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
+			"links": []interface{}{
+				map[string]interface{}{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+					"rel":  "bookmark",
+				},
+			},
+		},
+		Flavor: map[string]interface{}{
+			"id": "1",
+			"links": []interface{}{
+				map[string]interface{}{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
+					"rel":  "bookmark",
+				},
+			},
+		},
+		ID:       "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+		UserID:   "9349aff8be7545ac9d2f1d00999a23cd",
+		Name:     "herp",
+		Created:  "2014-09-25T13:10:02Z",
+		TenantID: "fcad67a6189847c4aecfa3c81a05783b",
+		Metadata: map[string]interface{}{},
+		SecurityGroups: []map[string]interface{}{
+			map[string]interface{}{
+				"name": "default",
+			},
+		},
+	}
+
+	// ServerDerp is a Server struct that should correspond to the second server in ServerListBody.
+	ServerDerp = servers.Server{
+		Status:  "ACTIVE",
+		Updated: "2014-09-25T13:04:49Z",
+		HostID:  "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+		Addresses: map[string]interface{}{
+			"private": []interface{}{
+				map[string]interface{}{
+					"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
+					"version":                 float64(4),
+					"addr":                    "10.0.0.31",
+					"OS-EXT-IPS:type":         "fixed",
+				},
+			},
+		},
+		Links: []interface{}{
+			map[string]interface{}{
+				"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+				"rel":  "self",
+			},
+			map[string]interface{}{
+				"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+				"rel":  "bookmark",
+			},
+		},
+		Image: map[string]interface{}{
+			"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
+			"links": []interface{}{
+				map[string]interface{}{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+					"rel":  "bookmark",
+				},
+			},
+		},
+		Flavor: map[string]interface{}{
+			"id": "1",
+			"links": []interface{}{
+				map[string]interface{}{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
+					"rel":  "bookmark",
+				},
+			},
+		},
+		ID:       "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+		UserID:   "9349aff8be7545ac9d2f1d00999a23cd",
+		Name:     "derp",
+		Created:  "2014-09-25T13:04:41Z",
+		TenantID: "fcad67a6189847c4aecfa3c81a05783b",
+		Metadata: map[string]interface{}{},
+		SecurityGroups: []map[string]interface{}{
+			map[string]interface{}{
+				"name": "default",
+			},
+		},
+	}
+)
+
+type CreateOptsWithCustomField struct {
+	servers.CreateOpts
+	Foo string `json:"foo,omitempty"`
+}
+
+func (opts CreateOptsWithCustomField) ToServerCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "server")
+}
+
+// HandleServerCreationSuccessfully sets up the test server to respond to a server creation request
+// with a given response.
+func HandleServerCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"server": {
+				"name": "derp",
+				"imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb",
+				"flavorRef": "1"
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandleServerCreationWithCustomFieldSuccessfully sets up the test server to respond to a server creation request
+// with a given response.
+func HandleServerCreationWithCustomFieldSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"server": {
+				"name": "derp",
+				"imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb",
+				"flavorRef": "1",
+				"foo": "bar"
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandleServerListSuccessfully sets up the test server to respond to a server List request.
+func HandleServerListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, ServerListBody)
+		case "9e5476bd-a4ec-4653-93d6-72c93aa682ba":
+			fmt.Fprintf(w, `{ "servers": [] }`)
+		default:
+			t.Fatalf("/servers/detail invoked with unexpected marker=[%s]", marker)
+		}
+	})
+}
+
+// HandleServerDeletionSuccessfully sets up the test server to respond to a server deletion request.
+func HandleServerDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/asdfasdfasdf", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleServerForceDeletionSuccessfully sets up the test server to respond to a server force deletion
+// request.
+func HandleServerForceDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/asdfasdfasdf/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "forceDelete": "" }`)
+
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+// HandleServerGetSuccessfully sets up the test server to respond to a server Get request.
+func HandleServerGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleServerBody)
+	})
+}
+
+// HandleServerUpdateSuccessfully sets up the test server to respond to a server Update request.
+func HandleServerUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `{ "server": { "name": "new-name" } }`)
+
+		fmt.Fprintf(w, SingleServerBody)
+	})
+}
+
+// HandleAdminPasswordChangeSuccessfully sets up the test server to respond to a server password
+// change request.
+func HandleAdminPasswordChangeSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "changePassword": { "adminPass": "new-password" } }`)
+
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+// HandleRebootSuccessfully sets up the test server to respond to a reboot request with success.
+func HandleRebootSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "reboot": { "type": "SOFT" } }`)
+
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+// HandleRebuildSuccessfully sets up the test server to respond to a rebuild request with success.
+func HandleRebuildSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `
+			{
+				"rebuild": {
+					"name": "new-name",
+					"adminPass": "swordfish",
+					"imageRef": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+					"accessIPv4": "1.2.3.4"
+				}
+			}
+		`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandleServerRescueSuccessfully sets up the test server to respond to a server Rescue request.
+func HandleServerRescueSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "rescue": { "adminPass": "1234567890" } }`)
+
+		w.WriteHeader(http.StatusOK)
+		w.Write([]byte(`{ "adminPass": "1234567890" }`))
+	})
+}
+
+// HandleMetadatumGetSuccessfully sets up the test server to respond to a metadatum Get request.
+func HandleMetadatumGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.WriteHeader(http.StatusOK)
+		w.Header().Add("Content-Type", "application/json")
+		w.Write([]byte(`{ "meta": {"foo":"bar"}}`))
+	})
+}
+
+// HandleMetadatumCreateSuccessfully sets up the test server to respond to a metadatum Create request.
+func HandleMetadatumCreateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"meta": {
+				"foo": "bar"
+			}
+		}`)
+
+		w.WriteHeader(http.StatusOK)
+		w.Header().Add("Content-Type", "application/json")
+		w.Write([]byte(`{ "meta": {"foo":"bar"}}`))
+	})
+}
+
+// HandleMetadatumDeleteSuccessfully sets up the test server to respond to a metadatum Delete request.
+func HandleMetadatumDeleteSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleMetadataGetSuccessfully sets up the test server to respond to a metadata Get request.
+func HandleMetadataGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.WriteHeader(http.StatusOK)
+		w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`))
+	})
+}
+
+// HandleMetadataResetSuccessfully sets up the test server to respond to a metadata Create request.
+func HandleMetadataResetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+				"metadata": {
+					"foo": "bar",
+					"this": "that"
+				}
+			}`)
+
+		w.WriteHeader(http.StatusOK)
+		w.Header().Add("Content-Type", "application/json")
+		w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`))
+	})
+}
+
+// HandleMetadataUpdateSuccessfully sets up the test server to respond to a metadata Update request.
+func HandleMetadataUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+				"metadata": {
+					"foo": "baz",
+					"this": "those"
+				}
+			}`)
+
+		w.WriteHeader(http.StatusOK)
+		w.Header().Add("Content-Type", "application/json")
+		w.Write([]byte(`{ "metadata": {"foo":"baz", "this":"those"}}`))
+	})
+}
+
+// ListAddressesExpected represents an expected repsonse from a ListAddresses request.
+var ListAddressesExpected = map[string][]servers.Address{
+	"public": []servers.Address{
+		{
+			Version: 4,
+			Address: "80.56.136.39",
+		},
+		{
+			Version: 6,
+			Address: "2001:4800:790e:510:be76:4eff:fe04:82a8",
+		},
+	},
+	"private": []servers.Address{
+		{
+			Version: 4,
+			Address: "10.880.3.154",
+		},
+	},
+}
+
+// HandleAddressListSuccessfully sets up the test server to respond to a ListAddresses request.
+func HandleAddressListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/asdfasdfasdf/ips", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `{
+			"addresses": {
+				"public": [
+				{
+					"version": 4,
+					"addr": "50.56.176.35"
+				},
+				{
+					"version": 6,
+					"addr": "2001:4800:780e:510:be76:4eff:fe04:84a8"
+				}
+				],
+				"private": [
+				{
+					"version": 4,
+					"addr": "10.180.3.155"
+				}
+				]
+			}
+		}`)
+	})
+}
+
+// ListNetworkAddressesExpected represents an expected repsonse from a ListAddressesByNetwork request.
+var ListNetworkAddressesExpected = []servers.Address{
+	{
+		Version: 4,
+		Address: "50.56.176.35",
+	},
+	{
+		Version: 6,
+		Address: "2001:4800:780e:510:be76:4eff:fe04:84a8",
+	},
+}
+
+// HandleNetworkAddressListSuccessfully sets up the test server to respond to a ListAddressesByNetwork request.
+func HandleNetworkAddressListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/asdfasdfasdf/ips/public", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `{
+			"public": [
+			{
+				"version": 4,
+				"addr": "50.56.176.35"
+				},
+				{
+					"version": 6,
+					"addr": "2001:4800:780e:510:be76:4eff:fe04:84a8"
+				}
+			]
+			}`)
+	})
+}
+
+// HandleCreateServerImageSuccessfully sets up the test server to respond to a TestCreateServerImage request.
+func HandleCreateServerImageSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/serverimage/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		w.Header().Add("Location", "https://0.0.0.0/images/xxxx-xxxxx-xxxxx-xxxx")
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+// HandlePasswordGetSuccessfully sets up the test server to respond to a password Get request.
+func HandlePasswordGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/servers/1234asdf/os-server-password", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, ServerPasswordBody)
+	})
+}
diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go
new file mode 100644
index 0000000..7db6b93
--- /dev/null
+++ b/openstack/compute/v2/servers/testing/requests_test.go
@@ -0,0 +1,412 @@
+package testing
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestListServers(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleServerListSuccessfully(t)
+
+	pages := 0
+	err := servers.List(client.ServiceClient(), servers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := servers.ExtractServers(page)
+		if err != nil {
+			return false, err
+		}
+
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 servers, got %d", len(actual))
+		}
+		th.CheckDeepEquals(t, ServerHerp, actual[0])
+		th.CheckDeepEquals(t, ServerDerp, actual[1])
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
+	}
+}
+
+func TestListAllServers(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleServerListSuccessfully(t)
+
+	allPages, err := servers.List(client.ServiceClient(), servers.ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := servers.ExtractServers(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, ServerHerp, actual[0])
+	th.CheckDeepEquals(t, ServerDerp, actual[1])
+}
+
+func TestCreateServer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleServerCreationSuccessfully(t, SingleServerBody)
+
+	actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{
+		Name:      "derp",
+		ImageRef:  "f90f6034-2570-4974-8351-6b49732ef2eb",
+		FlavorRef: "1",
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, ServerDerp, *actual)
+}
+
+func TestCreateServerWithCustomField(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleServerCreationWithCustomFieldSuccessfully(t, SingleServerBody)
+
+	actual, err := servers.Create(client.ServiceClient(), CreateOptsWithCustomField{
+		CreateOpts: servers.CreateOpts{
+			Name:      "derp",
+			ImageRef:  "f90f6034-2570-4974-8351-6b49732ef2eb",
+			FlavorRef: "1",
+		},
+		Foo: "bar",
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, ServerDerp, *actual)
+}
+
+func TestDeleteServer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleServerDeletionSuccessfully(t)
+
+	res := servers.Delete(client.ServiceClient(), "asdfasdfasdf")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestForceDeleteServer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleServerForceDeletionSuccessfully(t)
+
+	res := servers.ForceDelete(client.ServiceClient(), "asdfasdfasdf")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestGetServer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleServerGetSuccessfully(t)
+
+	client := client.ServiceClient()
+	actual, err := servers.Get(client, "1234asdf").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, ServerDerp, *actual)
+}
+
+func TestUpdateServer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleServerUpdateSuccessfully(t)
+
+	client := client.ServiceClient()
+	actual, err := servers.Update(client, "1234asdf", servers.UpdateOpts{Name: "new-name"}).Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Update error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, ServerDerp, *actual)
+}
+
+func TestChangeServerAdminPassword(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleAdminPasswordChangeSuccessfully(t)
+
+	res := servers.ChangeAdminPassword(client.ServiceClient(), "1234asdf", "new-password")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestGetPassword(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePasswordGetSuccessfully(t)
+
+	res := servers.GetPassword(client.ServiceClient(), "1234asdf")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestRebootServer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleRebootSuccessfully(t)
+
+	res := servers.Reboot(client.ServiceClient(), "1234asdf", &servers.RebootOpts{
+		Type: servers.SoftReboot,
+	})
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestRebuildServer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleRebuildSuccessfully(t, SingleServerBody)
+
+	opts := servers.RebuildOpts{
+		Name:       "new-name",
+		AdminPass:  "swordfish",
+		ImageID:    "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+		AccessIPv4: "1.2.3.4",
+	}
+
+	actual, err := servers.Rebuild(client.ServiceClient(), "1234asdf", opts).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, ServerDerp, *actual)
+}
+
+func TestResizeServer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "resize": { "flavorRef": "2" } }`)
+
+		w.WriteHeader(http.StatusAccepted)
+	})
+
+	res := servers.Resize(client.ServiceClient(), "1234asdf", servers.ResizeOpts{FlavorRef: "2"})
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestConfirmResize(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "confirmResize": null }`)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := servers.ConfirmResize(client.ServiceClient(), "1234asdf")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestRevertResize(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "revertResize": null }`)
+
+		w.WriteHeader(http.StatusAccepted)
+	})
+
+	res := servers.RevertResize(client.ServiceClient(), "1234asdf")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestRescue(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleServerRescueSuccessfully(t)
+
+	res := servers.Rescue(client.ServiceClient(), "1234asdf", servers.RescueOpts{
+		AdminPass: "1234567890",
+	})
+	th.AssertNoErr(t, res.Err)
+	adminPass, _ := res.Extract()
+	th.AssertEquals(t, "1234567890", adminPass)
+}
+
+func TestGetMetadatum(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleMetadatumGetSuccessfully(t)
+
+	expected := map[string]string{"foo": "bar"}
+	actual, err := servers.Metadatum(client.ServiceClient(), "1234asdf", "foo").Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestCreateMetadatum(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleMetadatumCreateSuccessfully(t)
+
+	expected := map[string]string{"foo": "bar"}
+	actual, err := servers.CreateMetadatum(client.ServiceClient(), "1234asdf", servers.MetadatumOpts{"foo": "bar"}).Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestDeleteMetadatum(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleMetadatumDeleteSuccessfully(t)
+
+	err := servers.DeleteMetadatum(client.ServiceClient(), "1234asdf", "foo").ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestGetMetadata(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleMetadataGetSuccessfully(t)
+
+	expected := map[string]string{"foo": "bar", "this": "that"}
+	actual, err := servers.Metadata(client.ServiceClient(), "1234asdf").Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestResetMetadata(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleMetadataResetSuccessfully(t)
+
+	expected := map[string]string{"foo": "bar", "this": "that"}
+	actual, err := servers.ResetMetadata(client.ServiceClient(), "1234asdf", servers.MetadataOpts{
+		"foo":  "bar",
+		"this": "that",
+	}).Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestUpdateMetadata(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleMetadataUpdateSuccessfully(t)
+
+	expected := map[string]string{"foo": "baz", "this": "those"}
+	actual, err := servers.UpdateMetadata(client.ServiceClient(), "1234asdf", servers.MetadataOpts{
+		"foo":  "baz",
+		"this": "those",
+	}).Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestListAddresses(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleAddressListSuccessfully(t)
+
+	expected := ListAddressesExpected
+	pages := 0
+	err := servers.ListAddresses(client.ServiceClient(), "asdfasdfasdf").EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := servers.ExtractAddresses(page)
+		th.AssertNoErr(t, err)
+
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 networks, got %d", len(actual))
+		}
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, 1, pages)
+}
+
+func TestListAddressesByNetwork(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleNetworkAddressListSuccessfully(t)
+
+	expected := ListNetworkAddressesExpected
+	pages := 0
+	err := servers.ListAddressesByNetwork(client.ServiceClient(), "asdfasdfasdf", "public").EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := servers.ExtractNetworkAddresses(page)
+		th.AssertNoErr(t, err)
+
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 addresses, got %d", len(actual))
+		}
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, 1, pages)
+}
+
+func TestCreateServerImage(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleCreateServerImageSuccessfully(t)
+
+	_, err := servers.CreateImage(client.ServiceClient(), "serverimage", servers.CreateImageOpts{Name: "test"}).ExtractImageID()
+	th.AssertNoErr(t, err)
+}
+
+func TestMarshalPersonality(t *testing.T) {
+	name := "/etc/test"
+	contents := []byte("asdfasdf")
+
+	personality := servers.Personality{
+		&servers.File{
+			Path:     name,
+			Contents: contents,
+		},
+	}
+
+	data, err := json.Marshal(personality)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var actual []map[string]string
+	err = json.Unmarshal(data, &actual)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(actual) != 1 {
+		t.Fatal("expected personality length 1")
+	}
+
+	if actual[0]["path"] != name {
+		t.Fatal("file path incorrect")
+	}
+
+	if actual[0]["contents"] != base64.StdEncoding.EncodeToString(contents) {
+		t.Fatal("file contents incorrect")
+	}
+}
diff --git a/openstack/compute/v2/servers/testing/results_test.go b/openstack/compute/v2/servers/testing/results_test.go
new file mode 100644
index 0000000..5866957
--- /dev/null
+++ b/openstack/compute/v2/servers/testing/results_test.go
@@ -0,0 +1,110 @@
+package testing
+
+import (
+	"crypto/rsa"
+	"encoding/json"
+	"fmt"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+	"golang.org/x/crypto/ssh"
+)
+
+// Fail - No password in JSON.
+func TestExtractPassword_no_pwd_data(t *testing.T) {
+
+	var dejson interface{}
+	err := json.Unmarshal([]byte(`{ "Crappy data": ".-.-." }`), &dejson)
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+	resp := servers.GetPasswordResult{gophercloud.Result{Body: dejson}}
+
+	pwd, err := resp.ExtractPassword(nil)
+	th.AssertEquals(t, pwd, "")
+}
+
+// Ok - return encrypted password when no private key is given.
+func TestExtractPassword_encrypted_pwd(t *testing.T) {
+
+	var dejson interface{}
+	sejson := []byte(`{"password":"PP8EnwPO9DhEc8+O/6CKAkPF379mKsUsfFY6yyw0734XXvKsSdV9KbiHQ2hrBvzeZxtGMrlFaikVunCRizyLLWLMuOi4hoH+qy9F9sQid61gQIGkxwDAt85d/7Eau2/KzorFnZhgxArl7IiqJ67X6xjKkR3zur+Yp3V/mtVIehpPYIaAvPbcp2t4mQXl1I9J8yrQfEZOctLL1L4heDEVXnxvNihVLK6pivlVggp6SZCtjj9cduZGrYGsxsOCso1dqJQr7GCojfwvuLOoG0OYwEGuWVTZppxWxi/q1QgeHFhGKA5QUXlz7pS71oqpjYsTeViuHnfvlqb5TVYZpQ1haw=="}`)
+
+	err := json.Unmarshal(sejson, &dejson)
+	fmt.Printf("%v\n", dejson)
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+	resp := servers.GetPasswordResult{gophercloud.Result{Body: dejson}}
+
+	pwd, err := resp.ExtractPassword(nil)
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, "PP8EnwPO9DhEc8+O/6CKAkPF379mKsUsfFY6yyw0734XXvKsSdV9KbiHQ2hrBvzeZxtGMrlFaikVunCRizyLLWLMuOi4hoH+qy9F9sQid61gQIGkxwDAt85d/7Eau2/KzorFnZhgxArl7IiqJ67X6xjKkR3zur+Yp3V/mtVIehpPYIaAvPbcp2t4mQXl1I9J8yrQfEZOctLL1L4heDEVXnxvNihVLK6pivlVggp6SZCtjj9cduZGrYGsxsOCso1dqJQr7GCojfwvuLOoG0OYwEGuWVTZppxWxi/q1QgeHFhGKA5QUXlz7pS71oqpjYsTeViuHnfvlqb5TVYZpQ1haw==", pwd)
+}
+
+// Ok - return decrypted password when private key is given.
+// Decrytion can be verified by:
+//   echo "<enc_pwd>" | base64 -D | openssl rsautl -decrypt -inkey <privateKey.pem>
+func TestExtractPassword_decrypted_pwd(t *testing.T) {
+
+	privateKey, err := ssh.ParseRawPrivateKey([]byte(`
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAo1ODZgwMVdTJYim9UYuYhowoPMhGEuV5IRZjcJ315r7RBSC+
+yEiBb1V+jhf+P8fzAyU35lkBzZGDr7E3jxSesbOuYT8cItQS4ErUnI1LGuqvMxwv
+X3GMyE/HmOcaiODF1XZN3Ur5pMJdVknnmczgUsW0hT98Udrh3MQn9WSuh/6LRy6+
+x1QsKHOCLFPnkhWa3LKyxmpQq/Gvhz+6NLe+gt8MFullA5mKQxBJ/K6laVHeaMlw
+JG3GCX0EZhRlvzoV8koIBKZtbKFolFr8ZtxBm3R5LvnyrtOvp22sa+xeItUT5kG1
+ZnbGNdK87oYW+VigEUfzT/+8R1i6E2QIXoeZiQIDAQABAoIBAQCVZ70IqbbTAW8j
+RAlyQh/J3Qal65LmkFJJKUDX8TfT1/Q/G6BKeMEmxm+Zrmsfj1pHI1HKftt+YEG1
+g4jOc09kQXkgbmnfll6aHPn3J+1vdwXD3GGdjrL5PrnYrngAhJWU2r8J0x8hT8ew
+OrUJZXhDX6XuSpAAFRmOKUZgXbSmo4X+LZX76ACnarselJt5FL724ECvpWJ7xxC4
+FMzvp4RqMmNFvv/Uq9lE/EmoSk4dviYyIZZ16DbDNyc9k/sGqCAMktCEwZ3EQm//
+S5bkNhgP6oUXjluWy53aPRgykEylgDWo5SSdSEyKnw/fciU0xdprA9JrBGIcTyHS
+/k2kgD4xAoGBANTkJ88Q0YrxX3fZNZVqcn00XKTxPGmxN5LRs7eV743q30AxK5Db
+QU8iwaAA1IKUWV5DLhgUTNsDCOPUPue4aOSBD3/sj+WEmvIhj7afDL5didkYHsqf
+fDnhFHq7y/3i57d428C7BwwR79pGWVyi7vH3pfu9A1iwl1aNOae+zvbVAoGBAMRm
+AmwQ9fJ3Qc44jysFK/yliLRGdShjkMMah5G3JlrelwfPtwPwEL2EHHhJB/C1acMs
+n6Q6RaoF6WNSZUY65ksQg7aPOYf2X0FTFwQJvwDJ4qlWjmq7w+tQ0AoGJG+dVUmQ
+zHZ/Y+HokSXzz9c4oevk4v/rMgAQ00WHrTdtIhnlAoGBALIJJ72D7CkNGHCq5qPQ
+xHQukPejgolFGhufYXM7YX3GmPMe67cVlTVv9Isxhoa5N0+cUPT0LR3PGOUm/4Bb
+eOT3hZXOqLwhvE6XgI8Rzd95bClwgXekDoh80dqeKMdmta961BQGlKskaPiacmsF
+G1yhZV70P9Mwwy8vpbLB4GUNAoGAbTwbjsWkNfa0qCF3J8NZoszjCvnBQfSW2J1R
+1+8ZKyNwt0yFi3Ajr3TibNiZzPzp1T9lj29FvfpJxA9Y+sXZvthxmcFxizix5GB1
+ha5yCNtA8VSOI7lJkAFDpL+j1lyYyjD6N9JE2KqEyKoh6J+8F7sXsqW7CqRRDfQX
+mKNfey0CgYEAxcEoNoADN2hRl7qY9rbQfVvQb3RkoQkdHhl9gpLFCcV32IP8R4xg
+09NbQK5OmgcIuZhLVNzTmUHJbabEGeXqIFIV0DsqECAt3WzbDyKQO23VJysFD46c
+KSde3I0ybDz7iS2EtceKB7m4C0slYd+oBkm4efuF00rCOKDwpFq45m0=
+-----END RSA PRIVATE KEY-----
+`))
+	if err != nil {
+		t.Fatalf("Error parsing private key: %s\n", err)
+	}
+
+	var dejson interface{}
+	sejson := []byte(`{"password":"PP8EnwPO9DhEc8+O/6CKAkPF379mKsUsfFY6yyw0734XXvKsSdV9KbiHQ2hrBvzeZxtGMrlFaikVunCRizyLLWLMuOi4hoH+qy9F9sQid61gQIGkxwDAt85d/7Eau2/KzorFnZhgxArl7IiqJ67X6xjKkR3zur+Yp3V/mtVIehpPYIaAvPbcp2t4mQXl1I9J8yrQfEZOctLL1L4heDEVXnxvNihVLK6pivlVggp6SZCtjj9cduZGrYGsxsOCso1dqJQr7GCojfwvuLOoG0OYwEGuWVTZppxWxi/q1QgeHFhGKA5QUXlz7pS71oqpjYsTeViuHnfvlqb5TVYZpQ1haw=="}`)
+
+	err = json.Unmarshal(sejson, &dejson)
+	fmt.Printf("%v\n", dejson)
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+	resp := servers.GetPasswordResult{gophercloud.Result{Body: dejson}}
+
+	pwd, err := resp.ExtractPassword(privateKey.(*rsa.PrivateKey))
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, "ruZKK0tqxRfYm5t7lSJq", pwd)
+}
+
+func TestListAddressesAllPages(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleAddressListSuccessfully(t)
+
+	allPages, err := servers.ListAddresses(client.ServiceClient(), "asdfasdfasdf").AllPages()
+	th.AssertNoErr(t, err)
+	_, err = servers.ExtractAddresses(allPages)
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/compute/v2/servers/urls.go b/openstack/compute/v2/servers/urls.go
index d51fcbe..e892e8d 100644
--- a/openstack/compute/v2/servers/urls.go
+++ b/openstack/compute/v2/servers/urls.go
@@ -1,6 +1,6 @@
 package servers
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func createURL(client *gophercloud.ServiceClient) string {
 	return client.ServiceURL("servers")
diff --git a/openstack/compute/v2/servers/urls_test.go b/openstack/compute/v2/servers/urls_test.go
deleted file mode 100644
index 9015d06..0000000
--- a/openstack/compute/v2/servers/urls_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package servers
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "servers"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient())
-	expected := endpoint + "servers"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestListDetailURL(t *testing.T) {
-	actual := listDetailURL(endpointClient())
-	expected := endpoint + "servers/detail"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "foo")
-	expected := endpoint + "servers/foo"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "servers/foo"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient(), "foo")
-	expected := endpoint + "servers/foo"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestActionURL(t *testing.T) {
-	actual := actionURL(endpointClient(), "foo")
-	expected := endpoint + "servers/foo/action"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestMetadatumURL(t *testing.T) {
-	actual := metadatumURL(endpointClient(), "foo", "bar")
-	expected := endpoint + "servers/foo/metadata/bar"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestMetadataURL(t *testing.T) {
-	actual := metadataURL(endpointClient(), "foo")
-	expected := endpoint + "servers/foo/metadata"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestPasswordURL(t *testing.T) {
-	actual := passwordURL(endpointClient(), "foo")
-	expected := endpoint + "servers/foo/os-server-password"
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/openstack/compute/v2/servers/util.go b/openstack/compute/v2/servers/util.go
index e6baf74..494a0e4 100644
--- a/openstack/compute/v2/servers/util.go
+++ b/openstack/compute/v2/servers/util.go
@@ -1,6 +1,6 @@
 package servers
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 // WaitForStatus will continually poll a server until it successfully transitions to a specified
 // status. It will do this for at most the number of seconds specified.
diff --git a/openstack/db/v1/configurations/fixtures.go b/openstack/db/v1/configurations/fixtures.go
deleted file mode 100644
index 9064c6c..0000000
--- a/openstack/db/v1/configurations/fixtures.go
+++ /dev/null
@@ -1,159 +0,0 @@
-// +build fixtures
-
-package configurations
-
-import (
-	"fmt"
-	"time"
-)
-
-var (
-	timestamp  = "2015-11-12T14:22:42Z"
-	timeVal, _ = time.Parse(time.RFC3339, timestamp)
-)
-
-var singleConfigJSON = `
-{
-  "created": "` + timestamp + `",
-  "datastore_name": "mysql",
-  "datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb",
-  "datastore_version_name": "5.6",
-  "description": "example_description",
-  "id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
-  "name": "example-configuration-name",
-  "updated": "` + timestamp + `"
-}
-`
-
-var singleConfigWithValuesJSON = `
-{
-  "created": "` + timestamp + `",
-  "datastore_name": "mysql",
-  "datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb",
-  "datastore_version_name": "5.6",
-  "description": "example description",
-  "id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
-  "instance_count": 0,
-  "name": "example-configuration-name",
-  "updated": "` + timestamp + `",
-  "values": {
-    "collation_server": "latin1_swedish_ci",
-    "connect_timeout": 120
-  }
-}
-`
-
-var (
-	ListConfigsJSON  = fmt.Sprintf(`{"configurations": [%s]}`, singleConfigJSON)
-	GetConfigJSON    = fmt.Sprintf(`{"configuration": %s}`, singleConfigJSON)
-	CreateConfigJSON = fmt.Sprintf(`{"configuration": %s}`, singleConfigWithValuesJSON)
-)
-
-var CreateReq = `
-{
-  "configuration": {
-    "datastore": {
-      "type": "a00000a0-00a0-0a00-00a0-000a000000aa",
-      "version": "b00000b0-00b0-0b00-00b0-000b000000bb"
-    },
-    "description": "example description",
-    "name": "example-configuration-name",
-    "values": {
-      "collation_server": "latin1_swedish_ci",
-      "connect_timeout": 120
-    }
-  }
-}
-`
-
-var UpdateReq = `
-{
-  "configuration": {
-    "values": {
-      "connect_timeout": 300
-    }
-  }
-}
-`
-
-var ListInstancesJSON = `
-{
-  "instances": [
-    {
-      "id": "d4603f69-ec7e-4e9b-803f-600b9205576f",
-      "name": "json_rack_instance"
-    }
-  ]
-}
-`
-
-var ListParamsJSON = `
-{
-  "configuration-parameters": [
-    {
-      "max": 1,
-      "min": 0,
-      "name": "innodb_file_per_table",
-      "restart_required": true,
-      "type": "integer"
-    },
-    {
-      "max": 4294967296,
-      "min": 0,
-      "name": "key_buffer_size",
-      "restart_required": false,
-      "type": "integer"
-    },
-    {
-      "max": 65535,
-      "min": 2,
-      "name": "connect_timeout",
-      "restart_required": false,
-      "type": "integer"
-    },
-    {
-      "max": 4294967296,
-      "min": 0,
-      "name": "join_buffer_size",
-      "restart_required": false,
-      "type": "integer"
-    }
-  ]
-}
-`
-
-var GetParamJSON = `
-{
-  "max": 1,
-  "min": 0,
-  "name": "innodb_file_per_table",
-  "restart_required": true,
-  "type": "integer"
-}
-`
-
-var ExampleConfig = Config{
-	Created:              timeVal,
-	DatastoreName:        "mysql",
-	DatastoreVersionID:   "b00000b0-00b0-0b00-00b0-000b000000bb",
-	DatastoreVersionName: "5.6",
-	Description:          "example_description",
-	ID:                   "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
-	Name:                 "example-configuration-name",
-	Updated:              timeVal,
-}
-
-var ExampleConfigWithValues = Config{
-	Created:              timeVal,
-	DatastoreName:        "mysql",
-	DatastoreVersionID:   "b00000b0-00b0-0b00-00b0-000b000000bb",
-	DatastoreVersionName: "5.6",
-	Description:          "example description",
-	ID:                   "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
-	Name:                 "example-configuration-name",
-	Updated:              timeVal,
-	Values: map[string]interface{}{
-		"collation_server": "latin1_swedish_ci",
-		"connect_timeout":  120,
-	},
-}
diff --git a/openstack/db/v1/configurations/requests.go b/openstack/db/v1/configurations/requests.go
index 83c7102..8fc8295 100644
--- a/openstack/db/v1/configurations/requests.go
+++ b/openstack/db/v1/configurations/requests.go
@@ -1,20 +1,16 @@
 package configurations
 
 import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/db/v1/instances"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/instances"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // List will list all of the available configurations.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	pageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, baseURL(client), func(r pagination.PageResult) pagination.Page {
 		return ConfigPage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, baseURL(client), pageFn)
+	})
 }
 
 // CreateOptsBuilder is a top-level interface which renders a JSON map.
@@ -25,103 +21,46 @@
 // DatastoreOpts is the primary options struct for creating and modifying
 // how configuration resources are associated with datastores.
 type DatastoreOpts struct {
-	// [OPTIONAL] The type of datastore. Defaults to "MySQL".
-	Type string
-
-	// [OPTIONAL] The specific version of a datastore. Defaults to "5.6".
-	Version string
-}
-
-// ToMap renders a JSON map for a datastore setting.
-func (opts DatastoreOpts) ToMap() (map[string]string, error) {
-	datastore := map[string]string{}
-
-	if opts.Type != "" {
-		datastore["type"] = opts.Type
-	}
-
-	if opts.Version != "" {
-		datastore["version"] = opts.Version
-	}
-
-	return datastore, nil
+	// The type of datastore. Defaults to "MySQL".
+	Type string `json:"type,omitempty"`
+	// The specific version of a datastore. Defaults to "5.6".
+	Version string `json:"version,omitempty"`
 }
 
 // CreateOpts is the struct responsible for configuring new configurations.
 type CreateOpts struct {
-	// [REQUIRED] The configuration group name
-	Name string
-
-	// [REQUIRED] A map of user-defined configuration settings that will define
+	// The configuration group name
+	Name string `json:"name" required:"true"`
+	// A map of user-defined configuration settings that will define
 	// how each associated datastore works. Each key/value pair is specific to a
 	// datastore type.
-	Values map[string]interface{}
-
-	// [OPTIONAL] Associates the configuration group with a particular datastore.
-	Datastore *DatastoreOpts
-
-	// [OPTIONAL] A human-readable explanation for the group.
-	Description string
+	Values map[string]interface{} `json:"values" required:"true"`
+	// Associates the configuration group with a particular datastore.
+	Datastore *DatastoreOpts `json:"datastore,omitempty"`
+	// A human-readable explanation for the group.
+	Description string `json:"description,omitempty"`
 }
 
 // ToConfigCreateMap casts a CreateOpts struct into a JSON map.
 func (opts CreateOpts) ToConfigCreateMap() (map[string]interface{}, error) {
-	if opts.Name == "" {
-		return nil, errors.New("Name is a required field")
-	}
-	if len(opts.Values) == 0 {
-		return nil, errors.New("Values must be a populated map")
-	}
-
-	config := map[string]interface{}{
-		"name":   opts.Name,
-		"values": opts.Values,
-	}
-
-	if opts.Datastore != nil {
-		ds, err := opts.Datastore.ToMap()
-		if err != nil {
-			return config, err
-		}
-		config["datastore"] = ds
-	}
-
-	if opts.Description != "" {
-		config["description"] = opts.Description
-	}
-
-	return map[string]interface{}{"configuration": config}, nil
+	return gophercloud.BuildRequestBody(opts, "configuration")
 }
 
 // Create will create a new configuration group.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToConfigCreateMap()
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToConfigCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Request("POST", baseURL(client), gophercloud.RequestOpts{
-		OkCodes:      []int{200},
-		JSONBody:     &reqBody,
-		JSONResponse: &res.Body,
-	})
-
-	return res
+	_, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}})
+	return
 }
 
 // Get will retrieve the details for a specified configuration group.
-func Get(client *gophercloud.ServiceClient, configID string) GetResult {
-	var res GetResult
-
-	_, res.Err = client.Request("GET", resourceURL(client, configID), gophercloud.RequestOpts{
-		OkCodes:      []int{200},
-		JSONResponse: &res.Body,
-	})
-
-	return res
+func Get(client *gophercloud.ServiceClient, configID string) (r GetResult) {
+	_, r.Err = client.Get(resourceURL(client, configID), &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder is the top-level interface for casting update options into
@@ -132,108 +71,63 @@
 
 // UpdateOpts is the struct responsible for modifying existing configurations.
 type UpdateOpts struct {
-	// [OPTIONAL] The configuration group name
-	Name string
-
-	// [OPTIONAL] A map of user-defined configuration settings that will define
+	// The configuration group name
+	Name string `json:"name,omitempty"`
+	// A map of user-defined configuration settings that will define
 	// how each associated datastore works. Each key/value pair is specific to a
 	// datastore type.
-	Values map[string]interface{}
-
-	// [OPTIONAL] Associates the configuration group with a particular datastore.
-	Datastore *DatastoreOpts
-
-	// [OPTIONAL] A human-readable explanation for the group.
-	Description string
+	Values map[string]interface{} `json:"values,omitempty"`
+	// Associates the configuration group with a particular datastore.
+	Datastore *DatastoreOpts `json:"datastore,omitempty"`
+	// A human-readable explanation for the group.
+	Description string `json:"description,omitempty"`
 }
 
 // ToConfigUpdateMap will cast an UpdateOpts struct into a JSON map.
 func (opts UpdateOpts) ToConfigUpdateMap() (map[string]interface{}, error) {
-	config := map[string]interface{}{}
-
-	if opts.Name != "" {
-		config["name"] = opts.Name
-	}
-
-	if opts.Description != "" {
-		config["description"] = opts.Description
-	}
-
-	if opts.Datastore != nil {
-		ds, err := opts.Datastore.ToMap()
-		if err != nil {
-			return config, err
-		}
-		config["datastore"] = ds
-	}
-
-	if len(opts.Values) > 0 {
-		config["values"] = opts.Values
-	}
-
-	return map[string]interface{}{"configuration": config}, nil
+	return gophercloud.BuildRequestBody(opts, "configuration")
 }
 
 // Update will modify an existing configuration group by performing a merge
 // between new and existing values. If the key already exists, the new value
 // will overwrite. All other keys will remain unaffected.
-func Update(client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToConfigUpdateMap()
+func Update(client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToConfigUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Request("PATCH", resourceURL(client, configID), gophercloud.RequestOpts{
-		OkCodes:  []int{200},
-		JSONBody: &reqBody,
-	})
-
-	return res
+	_, r.Err = client.Patch(resourceURL(client, configID), &b, nil, nil)
+	return
 }
 
 // Replace will modify an existing configuration group by overwriting the
 // entire parameter group with the new values provided. Any existing keys not
 // included in UpdateOptsBuilder will be deleted.
-func Replace(client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) ReplaceResult {
-	var res ReplaceResult
-
-	reqBody, err := opts.ToConfigUpdateMap()
+func Replace(client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) (r ReplaceResult) {
+	b, err := opts.ToConfigUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Request("PUT", resourceURL(client, configID), gophercloud.RequestOpts{
-		OkCodes:  []int{202},
-		JSONBody: &reqBody,
-	})
-
-	return res
+	_, r.Err = client.Put(resourceURL(client, configID), &b, nil, nil)
+	return
 }
 
 // Delete will permanently delete a configuration group. Please note that
 // config groups cannot be deleted whilst still attached to running instances -
 // you must detach and then delete them.
-func Delete(client *gophercloud.ServiceClient, configID string) DeleteResult {
-	var res DeleteResult
-
-	_, res.Err = client.Request("DELETE", resourceURL(client, configID), gophercloud.RequestOpts{
-		OkCodes: []int{202},
-	})
-
-	return res
+func Delete(client *gophercloud.ServiceClient, configID string) (r DeleteResult) {
+	_, r.Err = client.Delete(resourceURL(client, configID), nil)
+	return
 }
 
 // ListInstances will list all the instances associated with a particular
 // configuration group.
 func ListInstances(client *gophercloud.ServiceClient, configID string) pagination.Pager {
-	pageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, instancesURL(client, configID), func(r pagination.PageResult) pagination.Page {
 		return instances.InstancePage{pagination.LinkedPageBase{PageResult: r}}
-	}
-	return pagination.NewPager(client, instancesURL(client, configID), pageFn)
+	})
 }
 
 // ListDatastoreParams will list all the available and supported parameters
@@ -242,10 +136,9 @@
 // you can use this operation (you will need to retrieve the MySQL datastore ID
 // by using the datastores API).
 func ListDatastoreParams(client *gophercloud.ServiceClient, datastoreID, versionID string) pagination.Pager {
-	pageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listDSParamsURL(client, datastoreID, versionID), func(r pagination.PageResult) pagination.Page {
 		return ParamPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, listDSParamsURL(client, datastoreID, versionID), pageFn)
+	})
 }
 
 // GetDatastoreParam will retrieve information about a specific configuration
@@ -253,35 +146,22 @@
 // "innodb_file_per_table" configuration param for MySQL datastores. You will
 // need the param's ID first, which can be attained by using the ListDatastoreParams
 // operation.
-func GetDatastoreParam(client *gophercloud.ServiceClient, datastoreID, versionID, paramID string) ParamResult {
-	var res ParamResult
-
-	_, res.Err = client.Request("GET", getDSParamURL(client, datastoreID, versionID, paramID), gophercloud.RequestOpts{
-		OkCodes:      []int{200},
-		JSONResponse: &res.Body,
-	})
-
-	return res
+func GetDatastoreParam(client *gophercloud.ServiceClient, datastoreID, versionID, paramID string) (r ParamResult) {
+	_, r.Err = client.Get(getDSParamURL(client, datastoreID, versionID, paramID), &r.Body, nil)
+	return
 }
 
 // ListGlobalParams is similar to ListDatastoreParams but does not require a
 // DatastoreID.
 func ListGlobalParams(client *gophercloud.ServiceClient, versionID string) pagination.Pager {
-	pageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listGlobalParamsURL(client, versionID), func(r pagination.PageResult) pagination.Page {
 		return ParamPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, listGlobalParamsURL(client, versionID), pageFn)
+	})
 }
 
 // GetGlobalParam is similar to GetDatastoreParam but does not require a
 // DatastoreID.
-func GetGlobalParam(client *gophercloud.ServiceClient, versionID, paramID string) ParamResult {
-	var res ParamResult
-
-	_, res.Err = client.Request("GET", getGlobalParamURL(client, versionID, paramID), gophercloud.RequestOpts{
-		OkCodes:      []int{200},
-		JSONResponse: &res.Body,
-	})
-
-	return res
+func GetGlobalParam(client *gophercloud.ServiceClient, versionID, paramID string) (r ParamResult) {
+	_, r.Err = client.Get(getGlobalParamURL(client, versionID, paramID), &r.Body, nil)
+	return
 }
diff --git a/openstack/db/v1/configurations/requests_test.go b/openstack/db/v1/configurations/requests_test.go
deleted file mode 100644
index db66f29..0000000
--- a/openstack/db/v1/configurations/requests_test.go
+++ /dev/null
@@ -1,236 +0,0 @@
-package configurations
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/db/v1/instances"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-var (
-	configID = "{configID}"
-	_baseURL = "/configurations"
-	resURL   = _baseURL + "/" + configID
-
-	dsID               = "{datastoreID}"
-	versionID          = "{versionID}"
-	paramID            = "{paramID}"
-	dsParamListURL     = "/datastores/" + dsID + "/versions/" + versionID + "/parameters"
-	dsParamGetURL      = "/datastores/" + dsID + "/versions/" + versionID + "/parameters/" + paramID
-	globalParamListURL = "/datastores/versions/" + versionID + "/parameters"
-	globalParamGetURL  = "/datastores/versions/" + versionID + "/parameters/" + paramID
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _baseURL, "GET", "", ListConfigsJSON, 200)
-
-	count := 0
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractConfigs(page)
-		th.AssertNoErr(t, err)
-
-		expected := []Config{ExampleConfig}
-		th.AssertDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertEquals(t, 1, count)
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "GET", "", GetConfigJSON, 200)
-
-	config, err := Get(fake.ServiceClient(), configID).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, &ExampleConfig, config)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _baseURL, "POST", CreateReq, CreateConfigJSON, 200)
-
-	opts := CreateOpts{
-		Datastore: &DatastoreOpts{
-			Type:    "a00000a0-00a0-0a00-00a0-000a000000aa",
-			Version: "b00000b0-00b0-0b00-00b0-000b000000bb",
-		},
-		Description: "example description",
-		Name:        "example-configuration-name",
-		Values: map[string]interface{}{
-			"collation_server": "latin1_swedish_ci",
-			"connect_timeout":  120,
-		},
-	}
-
-	config, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, &ExampleConfigWithValues, config)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "PATCH", UpdateReq, "", 200)
-
-	opts := UpdateOpts{
-		Values: map[string]interface{}{
-			"connect_timeout": 300,
-		},
-	}
-
-	err := Update(fake.ServiceClient(), configID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestReplace(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "PUT", UpdateReq, "", 202)
-
-	opts := UpdateOpts{
-		Values: map[string]interface{}{
-			"connect_timeout": 300,
-		},
-	}
-
-	err := Replace(fake.ServiceClient(), configID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "DELETE", "", "", 202)
-
-	err := Delete(fake.ServiceClient(), configID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestListInstances(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL+"/instances", "GET", "", ListInstancesJSON, 200)
-
-	expectedInstance := instances.Instance{
-		ID:   "d4603f69-ec7e-4e9b-803f-600b9205576f",
-		Name: "json_rack_instance",
-	}
-
-	pages := 0
-	err := ListInstances(fake.ServiceClient(), configID).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := instances.ExtractInstances(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.AssertDeepEquals(t, actual, []instances.Instance{expectedInstance})
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestListDSParams(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, dsParamListURL, "GET", "", ListParamsJSON, 200)
-
-	pages := 0
-	err := ListDatastoreParams(fake.ServiceClient(), dsID, versionID).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractParams(page)
-		if err != nil {
-			return false, err
-		}
-
-		expected := []Param{
-			Param{Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer"},
-			Param{Max: 4294967296, Min: 0, Name: "key_buffer_size", RestartRequired: false, Type: "integer"},
-			Param{Max: 65535, Min: 2, Name: "connect_timeout", RestartRequired: false, Type: "integer"},
-			Param{Max: 4294967296, Min: 0, Name: "join_buffer_size", RestartRequired: false, Type: "integer"},
-		}
-
-		th.AssertDeepEquals(t, actual, expected)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGetDSParam(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, dsParamGetURL, "GET", "", GetParamJSON, 200)
-
-	param, err := GetDatastoreParam(fake.ServiceClient(), dsID, versionID, paramID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Param{
-		Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer",
-	}
-
-	th.AssertDeepEquals(t, expected, param)
-}
-
-func TestListGlobalParams(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, globalParamListURL, "GET", "", ListParamsJSON, 200)
-
-	pages := 0
-	err := ListGlobalParams(fake.ServiceClient(), versionID).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractParams(page)
-		if err != nil {
-			return false, err
-		}
-
-		expected := []Param{
-			Param{Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer"},
-			Param{Max: 4294967296, Min: 0, Name: "key_buffer_size", RestartRequired: false, Type: "integer"},
-			Param{Max: 65535, Min: 2, Name: "connect_timeout", RestartRequired: false, Type: "integer"},
-			Param{Max: 4294967296, Min: 0, Name: "join_buffer_size", RestartRequired: false, Type: "integer"},
-		}
-
-		th.AssertDeepEquals(t, actual, expected)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGetGlobalParam(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, globalParamGetURL, "GET", "", GetParamJSON, 200)
-
-	param, err := GetGlobalParam(fake.ServiceClient(), versionID, paramID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Param{
-		Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer",
-	}
-
-	th.AssertDeepEquals(t, expected, param)
-}
diff --git a/openstack/db/v1/configurations/results.go b/openstack/db/v1/configurations/results.go
index d0d1d6e..c52a654 100644
--- a/openstack/db/v1/configurations/results.go
+++ b/openstack/db/v1/configurations/results.go
@@ -1,22 +1,19 @@
 package configurations
 
 import (
-	"fmt"
-	"reflect"
 	"time"
 
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Config represents a configuration group API resource.
 type Config struct {
-	Created              time.Time `mapstructure:"-"`
-	Updated              time.Time `mapstructure:"-"`
-	DatastoreName        string    `mapstructure:"datastore_name"`
-	DatastoreVersionID   string    `mapstructure:"datastore_version_id"`
-	DatastoreVersionName string    `mapstructure:"datastore_version_name"`
+	Created              time.Time `json:"created"`
+	Updated              time.Time `json:"updated"`
+	DatastoreName        string    `json:"datastore_name"`
+	DatastoreVersionID   string    `json:"datastore_version_id"`
+	DatastoreVersionName string    `json:"datastore_version_name"`
 	Description          string
 	ID                   string
 	Name                 string
@@ -31,55 +28,16 @@
 // IsEmpty indicates whether a ConfigPage is empty.
 func (r ConfigPage) IsEmpty() (bool, error) {
 	is, err := ExtractConfigs(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractConfigs will retrieve a slice of Config structs from a page.
-func ExtractConfigs(page pagination.Page) ([]Config, error) {
-	casted := page.(ConfigPage).Body
-
-	var resp struct {
-		Configs []Config `mapstructure:"configurations" json:"configurations"`
+func ExtractConfigs(r pagination.Page) ([]Config, error) {
+	var s struct {
+		Configs []Config `json:"configurations"`
 	}
-
-	if err := mapstructure.Decode(casted, &resp); err != nil {
-		return nil, err
-	}
-
-	var vals []interface{}
-	switch casted.(type) {
-	case map[string]interface{}:
-		vals = casted.(map[string]interface{})["configurations"].([]interface{})
-	case map[string][]interface{}:
-		vals = casted.(map[string][]interface{})["configurations"]
-	default:
-		return resp.Configs, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i, v := range vals {
-		val := v.(map[string]interface{})
-
-		if t, ok := val["created"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return resp.Configs, err
-			}
-			resp.Configs[i].Created = creationTime
-		}
-
-		if t, ok := val["updated"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return resp.Configs, err
-			}
-			resp.Configs[i].Updated = updatedTime
-		}
-	}
-
-	return resp.Configs, nil
+	err := (r.(ConfigPage)).ExtractInto(&s)
+	return s.Configs, err
 }
 
 type commonResult struct {
@@ -88,34 +46,11 @@
 
 // Extract will retrieve a Config resource from an operation result.
 func (r commonResult) Extract() (*Config, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Config *Config `json:"configuration"`
 	}
-
-	var response struct {
-		Config Config `mapstructure:"configuration"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-	val := r.Body.(map[string]interface{})["configuration"].(map[string]interface{})
-
-	if t, ok := val["created"].(string); ok && t != "" {
-		creationTime, err := time.Parse(time.RFC3339, t)
-		if err != nil {
-			return &response.Config, err
-		}
-		response.Config.Created = creationTime
-	}
-
-	if t, ok := val["updated"].(string); ok && t != "" {
-		updatedTime, err := time.Parse(time.RFC3339, t)
-		if err != nil {
-			return &response.Config, err
-		}
-		response.Config.Updated = updatedTime
-	}
-
-	return &response.Config, err
+	err := r.ExtractInto(&s)
+	return s.Config, err
 }
 
 // GetResult represents the result of a Get operation.
@@ -145,10 +80,10 @@
 
 // Param represents a configuration parameter API resource.
 type Param struct {
-	Max             int
-	Min             int
+	Max             float64
+	Min             float64
 	Name            string
-	RestartRequired bool `mapstructure:"restart_required" json:"restart_required"`
+	RestartRequired bool `json:"restart_required"`
 	Type            string
 }
 
@@ -160,22 +95,16 @@
 // IsEmpty indicates whether a ParamPage is empty.
 func (r ParamPage) IsEmpty() (bool, error) {
 	is, err := ExtractParams(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractParams will retrieve a slice of Param structs from a page.
-func ExtractParams(page pagination.Page) ([]Param, error) {
-	casted := page.(ParamPage).Body
-
-	var resp struct {
-		Params []Param `mapstructure:"configuration-parameters" json:"configuration-parameters"`
+func ExtractParams(r pagination.Page) ([]Param, error) {
+	var s struct {
+		Params []Param `json:"configuration-parameters"`
 	}
-
-	err := mapstructure.Decode(casted, &resp)
-	return resp.Params, err
+	err := (r.(ParamPage)).ExtractInto(&s)
+	return s.Params, err
 }
 
 // ParamResult represents the result of an operation which retrieves details
@@ -186,12 +115,7 @@
 
 // Extract will retrieve a param from an operation result.
 func (r ParamResult) Extract() (*Param, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var param Param
-
-	err := mapstructure.Decode(r.Body, &param)
-	return &param, err
+	var s *Param
+	err := r.ExtractInto(&s)
+	return s, err
 }
diff --git a/openstack/db/v1/configurations/testing/doc.go b/openstack/db/v1/configurations/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/db/v1/configurations/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/db/v1/configurations/testing/fixtures.go b/openstack/db/v1/configurations/testing/fixtures.go
new file mode 100644
index 0000000..56e10f4
--- /dev/null
+++ b/openstack/db/v1/configurations/testing/fixtures.go
@@ -0,0 +1,159 @@
+package testing
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/gophercloud/gophercloud/openstack/db/v1/configurations"
+)
+
+var (
+	timestamp  = "2015-11-12T14:22:42Z"
+	timeVal, _ = time.Parse(time.RFC3339, timestamp)
+)
+
+var singleConfigJSON = `
+{
+  "created": "` + timestamp + `",
+  "datastore_name": "mysql",
+  "datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb",
+  "datastore_version_name": "5.6",
+  "description": "example_description",
+  "id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
+  "name": "example-configuration-name",
+  "updated": "` + timestamp + `"
+}
+`
+
+var singleConfigWithValuesJSON = `
+{
+  "created": "` + timestamp + `",
+  "datastore_name": "mysql",
+  "datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb",
+  "datastore_version_name": "5.6",
+  "description": "example description",
+  "id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
+  "instance_count": 0,
+  "name": "example-configuration-name",
+  "updated": "` + timestamp + `",
+  "values": {
+    "collation_server": "latin1_swedish_ci",
+    "connect_timeout": 120
+  }
+}
+`
+
+var (
+	ListConfigsJSON  = fmt.Sprintf(`{"configurations": [%s]}`, singleConfigJSON)
+	GetConfigJSON    = fmt.Sprintf(`{"configuration": %s}`, singleConfigJSON)
+	CreateConfigJSON = fmt.Sprintf(`{"configuration": %s}`, singleConfigWithValuesJSON)
+)
+
+var CreateReq = `
+{
+  "configuration": {
+    "datastore": {
+      "type": "a00000a0-00a0-0a00-00a0-000a000000aa",
+      "version": "b00000b0-00b0-0b00-00b0-000b000000bb"
+    },
+    "description": "example description",
+    "name": "example-configuration-name",
+    "values": {
+      "collation_server": "latin1_swedish_ci",
+      "connect_timeout": 120
+    }
+  }
+}
+`
+
+var UpdateReq = `
+{
+  "configuration": {
+    "values": {
+      "connect_timeout": 300
+    }
+  }
+}
+`
+
+var ListInstancesJSON = `
+{
+  "instances": [
+    {
+      "id": "d4603f69-ec7e-4e9b-803f-600b9205576f",
+      "name": "json_rack_instance"
+    }
+  ]
+}
+`
+
+var ListParamsJSON = `
+{
+  "configuration-parameters": [
+    {
+      "max": 1,
+      "min": 0,
+      "name": "innodb_file_per_table",
+      "restart_required": true,
+      "type": "integer"
+    },
+    {
+      "max": 4294967296,
+      "min": 0,
+      "name": "key_buffer_size",
+      "restart_required": false,
+      "type": "integer"
+    },
+    {
+      "max": 65535,
+      "min": 2,
+      "name": "connect_timeout",
+      "restart_required": false,
+      "type": "integer"
+    },
+    {
+      "max": 4294967296,
+      "min": 0,
+      "name": "join_buffer_size",
+      "restart_required": false,
+      "type": "integer"
+    }
+  ]
+}
+`
+
+var GetParamJSON = `
+{
+  "max": 1,
+  "min": 0,
+  "name": "innodb_file_per_table",
+  "restart_required": true,
+  "type": "integer"
+}
+`
+
+var ExampleConfig = configurations.Config{
+	Created:              timeVal,
+	DatastoreName:        "mysql",
+	DatastoreVersionID:   "b00000b0-00b0-0b00-00b0-000b000000bb",
+	DatastoreVersionName: "5.6",
+	Description:          "example_description",
+	ID:                   "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
+	Name:                 "example-configuration-name",
+	Updated:              timeVal,
+}
+
+var ExampleConfigWithValues = configurations.Config{
+	Created:              timeVal,
+	DatastoreName:        "mysql",
+	DatastoreVersionID:   "b00000b0-00b0-0b00-00b0-000b000000bb",
+	DatastoreVersionName: "5.6",
+	Description:          "example description",
+	ID:                   "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
+	Name:                 "example-configuration-name",
+	Updated:              timeVal,
+	Values: map[string]interface{}{
+		"collation_server": "latin1_swedish_ci",
+		"connect_timeout":  120,
+	},
+}
diff --git a/openstack/db/v1/configurations/testing/requests_test.go b/openstack/db/v1/configurations/testing/requests_test.go
new file mode 100644
index 0000000..643f363
--- /dev/null
+++ b/openstack/db/v1/configurations/testing/requests_test.go
@@ -0,0 +1,237 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/db/v1/configurations"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/instances"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+	"github.com/gophercloud/gophercloud/testhelper/fixture"
+)
+
+var (
+	configID = "{configID}"
+	_baseURL = "/configurations"
+	resURL   = _baseURL + "/" + configID
+
+	dsID               = "{datastoreID}"
+	versionID          = "{versionID}"
+	paramID            = "{paramID}"
+	dsParamListURL     = "/datastores/" + dsID + "/versions/" + versionID + "/parameters"
+	dsParamGetURL      = "/datastores/" + dsID + "/versions/" + versionID + "/parameters/" + paramID
+	globalParamListURL = "/datastores/versions/" + versionID + "/parameters"
+	globalParamGetURL  = "/datastores/versions/" + versionID + "/parameters/" + paramID
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, _baseURL, "GET", "", ListConfigsJSON, 200)
+
+	count := 0
+	err := configurations.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := configurations.ExtractConfigs(page)
+		th.AssertNoErr(t, err)
+
+		expected := []configurations.Config{ExampleConfig}
+		th.AssertDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	th.AssertEquals(t, 1, count)
+	th.AssertNoErr(t, err)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, resURL, "GET", "", GetConfigJSON, 200)
+
+	config, err := configurations.Get(fake.ServiceClient(), configID).Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, &ExampleConfig, config)
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, _baseURL, "POST", CreateReq, CreateConfigJSON, 200)
+
+	opts := configurations.CreateOpts{
+		Datastore: &configurations.DatastoreOpts{
+			Type:    "a00000a0-00a0-0a00-00a0-000a000000aa",
+			Version: "b00000b0-00b0-0b00-00b0-000b000000bb",
+		},
+		Description: "example description",
+		Name:        "example-configuration-name",
+		Values: map[string]interface{}{
+			"collation_server": "latin1_swedish_ci",
+			"connect_timeout":  120,
+		},
+	}
+
+	config, err := configurations.Create(fake.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, &ExampleConfigWithValues, config)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, resURL, "PATCH", UpdateReq, "", 200)
+
+	opts := configurations.UpdateOpts{
+		Values: map[string]interface{}{
+			"connect_timeout": 300,
+		},
+	}
+
+	err := configurations.Update(fake.ServiceClient(), configID, opts).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestReplace(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, resURL, "PUT", UpdateReq, "", 202)
+
+	opts := configurations.UpdateOpts{
+		Values: map[string]interface{}{
+			"connect_timeout": 300,
+		},
+	}
+
+	err := configurations.Replace(fake.ServiceClient(), configID, opts).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, resURL, "DELETE", "", "", 202)
+
+	err := configurations.Delete(fake.ServiceClient(), configID).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestListInstances(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, resURL+"/instances", "GET", "", ListInstancesJSON, 200)
+
+	expectedInstance := instances.Instance{
+		ID:   "d4603f69-ec7e-4e9b-803f-600b9205576f",
+		Name: "json_rack_instance",
+	}
+
+	pages := 0
+	err := configurations.ListInstances(fake.ServiceClient(), configID).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := instances.ExtractInstances(page)
+		if err != nil {
+			return false, err
+		}
+
+		th.AssertDeepEquals(t, actual, []instances.Instance{expectedInstance})
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, pages)
+}
+
+func TestListDSParams(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, dsParamListURL, "GET", "", ListParamsJSON, 200)
+
+	pages := 0
+	err := configurations.ListDatastoreParams(fake.ServiceClient(), dsID, versionID).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := configurations.ExtractParams(page)
+		if err != nil {
+			return false, err
+		}
+
+		expected := []configurations.Param{
+			{Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer"},
+			{Max: 4294967296, Min: 0, Name: "key_buffer_size", RestartRequired: false, Type: "integer"},
+			{Max: 65535, Min: 2, Name: "connect_timeout", RestartRequired: false, Type: "integer"},
+			{Max: 4294967296, Min: 0, Name: "join_buffer_size", RestartRequired: false, Type: "integer"},
+		}
+
+		th.AssertDeepEquals(t, actual, expected)
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, pages)
+}
+
+func TestGetDSParam(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, dsParamGetURL, "GET", "", GetParamJSON, 200)
+
+	param, err := configurations.GetDatastoreParam(fake.ServiceClient(), dsID, versionID, paramID).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &configurations.Param{
+		Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer",
+	}
+
+	th.AssertDeepEquals(t, expected, param)
+}
+
+func TestListGlobalParams(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, globalParamListURL, "GET", "", ListParamsJSON, 200)
+
+	pages := 0
+	err := configurations.ListGlobalParams(fake.ServiceClient(), versionID).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := configurations.ExtractParams(page)
+		if err != nil {
+			return false, err
+		}
+
+		expected := []configurations.Param{
+			{Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer"},
+			{Max: 4294967296, Min: 0, Name: "key_buffer_size", RestartRequired: false, Type: "integer"},
+			{Max: 65535, Min: 2, Name: "connect_timeout", RestartRequired: false, Type: "integer"},
+			{Max: 4294967296, Min: 0, Name: "join_buffer_size", RestartRequired: false, Type: "integer"},
+		}
+
+		th.AssertDeepEquals(t, actual, expected)
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, pages)
+}
+
+func TestGetGlobalParam(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, globalParamGetURL, "GET", "", GetParamJSON, 200)
+
+	param, err := configurations.GetGlobalParam(fake.ServiceClient(), versionID, paramID).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &configurations.Param{
+		Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer",
+	}
+
+	th.AssertDeepEquals(t, expected, param)
+}
diff --git a/openstack/db/v1/configurations/urls.go b/openstack/db/v1/configurations/urls.go
index abea961..0a69253 100644
--- a/openstack/db/v1/configurations/urls.go
+++ b/openstack/db/v1/configurations/urls.go
@@ -1,6 +1,6 @@
 package configurations
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func baseURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("configurations")
diff --git a/openstack/db/v1/databases/fixtures.go b/openstack/db/v1/databases/fixtures.go
deleted file mode 100644
index 8d21e90..0000000
--- a/openstack/db/v1/databases/fixtures.go
+++ /dev/null
@@ -1,63 +0,0 @@
-// +build fixtures
-
-package databases
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-var (
-	instanceID = "{instanceID}"
-	resURL     = "/instances/" + instanceID + "/databases"
-)
-
-var createDBsReq = `
-{
-	"databases": [
-		{
-			"character_set": "utf8",
-			"collate": "utf8_general_ci",
-			"name": "testingdb"
-		},
-		{
-			"name": "sampledb"
-		}
-	]
-}
-`
-
-var listDBsResp = `
-{
-	"databases": [
-		{
-			"name": "anotherexampledb"
-		},
-		{
-			"name": "exampledb"
-		},
-		{
-			"name": "nextround"
-		},
-		{
-			"name": "sampledb"
-		},
-		{
-			"name": "testingdb"
-		}
-	]
-}
-`
-
-func HandleCreate(t *testing.T) {
-	fixture.SetupHandler(t, resURL, "POST", createDBsReq, "", 202)
-}
-
-func HandleList(t *testing.T) {
-	fixture.SetupHandler(t, resURL, "GET", "", listDBsResp, 200)
-}
-
-func HandleDelete(t *testing.T) {
-	fixture.SetupHandler(t, resURL+"/{dbName}", "DELETE", "", "", 202)
-}
diff --git a/openstack/db/v1/databases/requests.go b/openstack/db/v1/databases/requests.go
index f1eb5d9..ef5394f 100644
--- a/openstack/db/v1/databases/requests.go
+++ b/openstack/db/v1/databases/requests.go
@@ -1,10 +1,8 @@
 package databases
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // CreateOptsBuilder builds create options
@@ -12,48 +10,38 @@
 	ToDBCreateMap() (map[string]interface{}, error)
 }
 
-// DatabaseOpts is the struct responsible for configuring a database; often in
+// CreateOpts is the struct responsible for configuring a database; often in
 // the context of an instance.
 type CreateOpts struct {
-	// [REQUIRED] Specifies the name of the database. Valid names can be composed
+	// Specifies the name of the database. Valid names can be composed
 	// of the following characters: letters (either case); numbers; these
 	// characters '@', '?', '#', ' ' but NEVER beginning a name string; '_' is
 	// permitted anywhere. Prohibited characters that are forbidden include:
 	// single quotes, double quotes, back quotes, semicolons, commas, backslashes,
 	// and forward slashes.
-	Name string
-
-	// [OPTIONAL] Set of symbols and encodings. The default character set is
+	Name string `json:"name" required:"true"`
+	// Set of symbols and encodings. The default character set is
 	// "utf8". See http://dev.mysql.com/doc/refman/5.1/en/charset-mysql.html for
 	// supported character sets.
-	CharSet string
-
-	// [OPTIONAL] Set of rules for comparing characters in a character set. The
+	CharSet string `json:"character_set,omitempty"`
+	// Set of rules for comparing characters in a character set. The
 	// default value for collate is "utf8_general_ci". See
 	// http://dev.mysql.com/doc/refman/5.1/en/charset-mysql.html for supported
 	// collations.
-	Collate string
+	Collate string `json:"collate,omitempty"`
 }
 
 // ToMap is a helper function to convert individual DB create opt structures
 // into sub-maps.
-func (opts CreateOpts) ToMap() (map[string]string, error) {
-	if opts.Name == "" {
-		return nil, fmt.Errorf("Name is a required field")
-	}
+func (opts CreateOpts) ToMap() (map[string]interface{}, error) {
 	if len(opts.Name) > 64 {
-		return nil, fmt.Errorf("Name must be less than 64 chars long")
+		err := gophercloud.ErrInvalidInput{}
+		err.Argument = "databases.CreateOpts.Name"
+		err.Value = opts.Name
+		err.Info = "Must be less than 64 chars long"
+		return nil, err
 	}
-
-	db := map[string]string{"name": opts.Name}
-
-	if opts.CharSet != "" {
-		db["character_set"] = opts.CharSet
-	}
-	if opts.Collate != "" {
-		db["collate"] = opts.Collate
-	}
-	return db, nil
+	return gophercloud.BuildRequestBody(opts, "")
 }
 
 // BatchCreateOpts allows for multiple databases to created and modified.
@@ -61,7 +49,7 @@
 
 // ToDBCreateMap renders a JSON map for creating DBs.
 func (opts BatchCreateOpts) ToDBCreateMap() (map[string]interface{}, error) {
-	dbs := make([]map[string]string, len(opts))
+	dbs := make([]map[string]interface{}, len(opts))
 	for i, db := range opts {
 		dbMap, err := db.ToMap()
 		if err != nil {
@@ -74,42 +62,28 @@
 
 // Create will create a new database within the specified instance. If the
 // specified instance does not exist, a 404 error will be returned.
-func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToDBCreateMap()
+func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToDBCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Request("POST", baseURL(client, instanceID), gophercloud.RequestOpts{
-		JSONBody: &reqBody,
-		OkCodes:  []int{202},
-	})
-
-	return res
+	_, r.Err = client.Post(baseURL(client, instanceID), &b, nil, nil)
+	return
 }
 
 // List will list all of the databases for a specified instance. Note: this
 // operation will only return user-defined databases; it will exclude system
 // databases like "mysql", "information_schema", "lost+found" etc.
 func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager {
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, baseURL(client, instanceID), func(r pagination.PageResult) pagination.Page {
 		return DBPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, baseURL(client, instanceID), createPageFn)
+	})
 }
 
 // Delete will permanently delete the database within a specified instance.
 // All contained data inside the database will also be permanently deleted.
-func Delete(client *gophercloud.ServiceClient, instanceID, dbName string) DeleteResult {
-	var res DeleteResult
-
-	_, res.Err = client.Request("DELETE", dbURL(client, instanceID, dbName), gophercloud.RequestOpts{
-		OkCodes: []int{202},
-	})
-
-	return res
+func Delete(client *gophercloud.ServiceClient, instanceID, dbName string) (r DeleteResult) {
+	_, r.Err = client.Delete(dbURL(client, instanceID, dbName), nil)
+	return
 }
diff --git a/openstack/db/v1/databases/requests_test.go b/openstack/db/v1/databases/requests_test.go
deleted file mode 100644
index 8a1b297..0000000
--- a/openstack/db/v1/databases/requests_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package databases
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreate(t)
-
-	opts := BatchCreateOpts{
-		CreateOpts{Name: "testingdb", CharSet: "utf8", Collate: "utf8_general_ci"},
-		CreateOpts{Name: "sampledb"},
-	}
-
-	res := Create(fake.ServiceClient(), instanceID, opts)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleList(t)
-
-	expectedDBs := []Database{
-		Database{Name: "anotherexampledb"},
-		Database{Name: "exampledb"},
-		Database{Name: "nextround"},
-		Database{Name: "sampledb"},
-		Database{Name: "testingdb"},
-	}
-
-	pages := 0
-	err := List(fake.ServiceClient(), instanceID).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractDBs(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, expectedDBs, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-
-	if pages != 1 {
-		t.Errorf("Expected 1 page, saw %d", pages)
-	}
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDelete(t)
-
-	err := Delete(fake.ServiceClient(), instanceID, "{dbName}").ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/db/v1/databases/results.go b/openstack/db/v1/databases/results.go
index 7d4b6ae..0479d0e 100644
--- a/openstack/db/v1/databases/results.go
+++ b/openstack/db/v1/databases/results.go
@@ -1,9 +1,8 @@
 package databases
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Database represents a Database API resource.
@@ -37,36 +36,28 @@
 // IsEmpty checks to see whether the collection is empty.
 func (page DBPage) IsEmpty() (bool, error) {
 	dbs, err := ExtractDBs(page)
-	if err != nil {
-		return true, err
-	}
-	return len(dbs) == 0, nil
+	return len(dbs) == 0, err
 }
 
 // NextPageURL will retrieve the next page URL.
 func (page DBPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"databases_links"`
+	var s struct {
+		Links []gophercloud.Link `json:"databases_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(page.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // ExtractDBs will convert a generic pagination struct into a more
 // relevant slice of DB structs.
 func ExtractDBs(page pagination.Page) ([]Database, error) {
-	casted := page.(DBPage).Body
-
-	var response struct {
-		Databases []Database `mapstructure:"databases"`
+	r := page.(DBPage)
+	var s struct {
+		Databases []Database `json:"databases"`
 	}
-
-	err := mapstructure.Decode(casted, &response)
-	return response.Databases, err
+	err := r.ExtractInto(&s)
+	return s.Databases, err
 }
diff --git a/openstack/db/v1/databases/testing/doc.go b/openstack/db/v1/databases/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/db/v1/databases/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/db/v1/databases/testing/fixtures.go b/openstack/db/v1/databases/testing/fixtures.go
new file mode 100644
index 0000000..02b9ecc
--- /dev/null
+++ b/openstack/db/v1/databases/testing/fixtures.go
@@ -0,0 +1,61 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/testhelper/fixture"
+)
+
+var (
+	instanceID = "{instanceID}"
+	resURL     = "/instances/" + instanceID + "/databases"
+)
+
+var createDBsReq = `
+{
+	"databases": [
+		{
+			"character_set": "utf8",
+			"collate": "utf8_general_ci",
+			"name": "testingdb"
+		},
+		{
+			"name": "sampledb"
+		}
+	]
+}
+`
+
+var listDBsResp = `
+{
+	"databases": [
+		{
+			"name": "anotherexampledb"
+		},
+		{
+			"name": "exampledb"
+		},
+		{
+			"name": "nextround"
+		},
+		{
+			"name": "sampledb"
+		},
+		{
+			"name": "testingdb"
+		}
+	]
+}
+`
+
+func HandleCreate(t *testing.T) {
+	fixture.SetupHandler(t, resURL, "POST", createDBsReq, "", 202)
+}
+
+func HandleList(t *testing.T) {
+	fixture.SetupHandler(t, resURL, "GET", "", listDBsResp, 200)
+}
+
+func HandleDelete(t *testing.T) {
+	fixture.SetupHandler(t, resURL+"/{dbName}", "DELETE", "", "", 202)
+}
diff --git a/openstack/db/v1/databases/testing/requests_test.go b/openstack/db/v1/databases/testing/requests_test.go
new file mode 100644
index 0000000..a470ffa
--- /dev/null
+++ b/openstack/db/v1/databases/testing/requests_test.go
@@ -0,0 +1,67 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/db/v1/databases"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleCreate(t)
+
+	opts := databases.BatchCreateOpts{
+		databases.CreateOpts{Name: "testingdb", CharSet: "utf8", Collate: "utf8_general_ci"},
+		databases.CreateOpts{Name: "sampledb"},
+	}
+
+	res := databases.Create(fake.ServiceClient(), instanceID, opts)
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleList(t)
+
+	expectedDBs := []databases.Database{
+		{Name: "anotherexampledb"},
+		{Name: "exampledb"},
+		{Name: "nextround"},
+		{Name: "sampledb"},
+		{Name: "testingdb"},
+	}
+
+	pages := 0
+	err := databases.List(fake.ServiceClient(), instanceID).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := databases.ExtractDBs(page)
+		if err != nil {
+			return false, err
+		}
+
+		th.CheckDeepEquals(t, expectedDBs, actual)
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
+	}
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDelete(t)
+
+	err := databases.Delete(fake.ServiceClient(), instanceID, "{dbName}").ExtractErr()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/db/v1/databases/urls.go b/openstack/db/v1/databases/urls.go
index 027ca58..aba42c9 100644
--- a/openstack/db/v1/databases/urls.go
+++ b/openstack/db/v1/databases/urls.go
@@ -1,6 +1,6 @@
 package databases
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func baseURL(c *gophercloud.ServiceClient, instanceID string) string {
 	return c.ServiceURL("instances", instanceID, "databases")
diff --git a/openstack/db/v1/datastores/fixtures.go b/openstack/db/v1/datastores/fixtures.go
deleted file mode 100644
index eb796eb..0000000
--- a/openstack/db/v1/datastores/fixtures.go
+++ /dev/null
@@ -1,102 +0,0 @@
-// +build fixtures
-
-package datastores
-
-import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-)
-
-const version1JSON = `
-{
-	"id": "b00000b0-00b0-0b00-00b0-000b000000bb",
-	"links": [
-		{
-			"href": "https://10.240.28.38:8779/v1.0/1234/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb",
-			"rel": "self"
-		},
-		{
-			"href": "https://10.240.28.38:8779/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb",
-			"rel": "bookmark"
-		}
-	],
-	"name": "5.1"
-}
-`
-
-const version2JSON = `
-{
-	"id": "c00000b0-00c0-0c00-00c0-000b000000cc",
-	"links": [
-		{
-			"href": "https://10.240.28.38:8779/v1.0/1234/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc",
-			"rel": "self"
-		},
-		{
-			"href": "https://10.240.28.38:8779/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc",
-			"rel": "bookmark"
-		}
-	],
-	"name": "5.2"
-}
-`
-
-var versionsJSON = fmt.Sprintf(`"versions": [%s, %s]`, version1JSON, version2JSON)
-
-var singleDSJSON = fmt.Sprintf(`
-{
-  "default_version": "c00000b0-00c0-0c00-00c0-000b000000cc",
-  "id": "10000000-0000-0000-0000-000000000001",
-  "links": [
-    {
-      "href": "https://10.240.28.38:8779/v1.0/1234/datastores/10000000-0000-0000-0000-000000000001",
-      "rel": "self"
-    },
-    {
-      "href": "https://10.240.28.38:8779/datastores/10000000-0000-0000-0000-000000000001",
-      "rel": "bookmark"
-    }
-  ],
-  "name": "mysql",
-  %s
-}
-`, versionsJSON)
-
-var (
-	ListDSResp       = fmt.Sprintf(`{"datastores":[%s]}`, singleDSJSON)
-	GetDSResp        = fmt.Sprintf(`{"datastore":%s}`, singleDSJSON)
-	ListVersionsResp = fmt.Sprintf(`{%s}`, versionsJSON)
-	GetVersionResp   = fmt.Sprintf(`{"version":%s}`, version1JSON)
-)
-
-var ExampleVersion1 = Version{
-	ID: "b00000b0-00b0-0b00-00b0-000b000000bb",
-	Links: []gophercloud.Link{
-		gophercloud.Link{Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb"},
-		gophercloud.Link{Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb"},
-	},
-	Name: "5.1",
-}
-
-var exampleVersion2 = Version{
-	ID: "c00000b0-00c0-0c00-00c0-000b000000cc",
-	Links: []gophercloud.Link{
-		gophercloud.Link{Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc"},
-		gophercloud.Link{Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc"},
-	},
-	Name: "5.2",
-}
-
-var ExampleVersions = []Version{ExampleVersion1, exampleVersion2}
-
-var ExampleDatastore = Datastore{
-	DefaultVersion: "c00000b0-00c0-0c00-00c0-000b000000cc",
-	ID:             "10000000-0000-0000-0000-000000000001",
-	Links: []gophercloud.Link{
-		gophercloud.Link{Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/10000000-0000-0000-0000-000000000001"},
-		gophercloud.Link{Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/10000000-0000-0000-0000-000000000001"},
-	},
-	Name:     "mysql",
-	Versions: ExampleVersions,
-}
diff --git a/openstack/db/v1/datastores/requests.go b/openstack/db/v1/datastores/requests.go
index 9e147ab..134e309 100644
--- a/openstack/db/v1/datastores/requests.go
+++ b/openstack/db/v1/datastores/requests.go
@@ -1,47 +1,33 @@
 package datastores
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // List will list all available datastore types that instances can use.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	pageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, baseURL(client), func(r pagination.PageResult) pagination.Page {
 		return DatastorePage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, baseURL(client), pageFn)
+	})
 }
 
 // Get will retrieve the details of a specified datastore type.
-func Get(client *gophercloud.ServiceClient, datastoreID string) GetResult {
-	var res GetResult
-
-	_, res.Err = client.Request("GET", resourceURL(client, datastoreID), gophercloud.RequestOpts{
-		OkCodes:      []int{200},
-		JSONResponse: &res.Body,
-	})
-
-	return res
+func Get(client *gophercloud.ServiceClient, datastoreID string) (r GetResult) {
+	_, r.Err = client.Get(resourceURL(client, datastoreID), &r.Body, nil)
+	return
 }
 
 // ListVersions will list all of the available versions for a specified
 // datastore type.
 func ListVersions(client *gophercloud.ServiceClient, datastoreID string) pagination.Pager {
-	pageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, versionsURL(client, datastoreID), func(r pagination.PageResult) pagination.Page {
 		return VersionPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, versionsURL(client, datastoreID), pageFn)
+	})
 }
 
 // GetVersion will retrieve the details of a specified datastore version.
-func GetVersion(client *gophercloud.ServiceClient, datastoreID, versionID string) GetVersionResult {
-	var res GetVersionResult
-
-	_, res.Err = client.Request("GET", versionURL(client, datastoreID, versionID), gophercloud.RequestOpts{
-		OkCodes:      []int{200},
-		JSONResponse: &res.Body,
-	})
-
-	return res
+func GetVersion(client *gophercloud.ServiceClient, datastoreID, versionID string) (r GetVersionResult) {
+	_, r.Err = client.Get(versionURL(client, datastoreID, versionID), &r.Body, nil)
+	return
 }
diff --git a/openstack/db/v1/datastores/requests_test.go b/openstack/db/v1/datastores/requests_test.go
deleted file mode 100644
index b4ce871..0000000
--- a/openstack/db/v1/datastores/requests_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package datastores
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, "/datastores", "GET", "", ListDSResp, 200)
-
-	pages := 0
-
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractDatastores(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, []Datastore{ExampleDatastore}, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, "/datastores/{dsID}", "GET", "", GetDSResp, 200)
-
-	ds, err := Get(fake.ServiceClient(), "{dsID}").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, &ExampleDatastore, ds)
-}
-
-func TestListVersions(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, "/datastores/{dsID}/versions", "GET", "", ListVersionsResp, 200)
-
-	pages := 0
-
-	err := ListVersions(fake.ServiceClient(), "{dsID}").EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractVersions(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, ExampleVersions, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGetVersion(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, "/datastores/{dsID}/versions/{versionID}", "GET", "", GetVersionResp, 200)
-
-	ds, err := GetVersion(fake.ServiceClient(), "{dsID}", "{versionID}").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, &ExampleVersion1, ds)
-}
diff --git a/openstack/db/v1/datastores/results.go b/openstack/db/v1/datastores/results.go
index a86a3cc..a6e27d2 100644
--- a/openstack/db/v1/datastores/results.go
+++ b/openstack/db/v1/datastores/results.go
@@ -1,9 +1,8 @@
 package datastores
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Version represents a version API resource. Multiple versions belong to a Datastore.
@@ -15,7 +14,7 @@
 
 // Datastore represents a Datastore API resource.
 type Datastore struct {
-	DefaultVersion string `json:"default_version" mapstructure:"default_version"`
+	DefaultVersion string `json:"default_version"`
 	ID             string
 	Links          []gophercloud.Link
 	Name           string
@@ -28,7 +27,7 @@
 type DatastorePartial struct {
 	Version   string
 	Type      string
-	VersionID string `json:"version_id" mapstructure:"version_id"`
+	VersionID string `json:"version_id"`
 }
 
 // GetResult represents the result of a Get operation.
@@ -49,40 +48,29 @@
 // IsEmpty indicates whether a Datastore collection is empty.
 func (r DatastorePage) IsEmpty() (bool, error) {
 	is, err := ExtractDatastores(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractDatastores retrieves a slice of datastore structs from a paginated
 // collection.
-func ExtractDatastores(page pagination.Page) ([]Datastore, error) {
-	casted := page.(DatastorePage).Body
-
-	var resp struct {
-		Datastores []Datastore `mapstructure:"datastores" json:"datastores"`
+func ExtractDatastores(r pagination.Page) ([]Datastore, error) {
+	var s struct {
+		Datastores []Datastore `json:"datastores"`
 	}
-
-	err := mapstructure.Decode(casted, &resp)
-	return resp.Datastores, err
+	err := (r.(DatastorePage)).ExtractInto(&s)
+	return s.Datastores, err
 }
 
 // Extract retrieves a single Datastore struct from an operation result.
 func (r GetResult) Extract() (*Datastore, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Datastore *Datastore `json:"datastore"`
 	}
-
-	var response struct {
-		Datastore Datastore `mapstructure:"datastore"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-	return &response.Datastore, err
+	err := r.ExtractInto(&s)
+	return s.Datastore, err
 }
 
-// DatastorePage represents a page of version resources.
+// VersionPage represents a page of version resources.
 type VersionPage struct {
 	pagination.SinglePageBase
 }
@@ -90,34 +78,23 @@
 // IsEmpty indicates whether a collection of version resources is empty.
 func (r VersionPage) IsEmpty() (bool, error) {
 	is, err := ExtractVersions(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractVersions retrieves a slice of versions from a paginated collection.
-func ExtractVersions(page pagination.Page) ([]Version, error) {
-	casted := page.(VersionPage).Body
-
-	var resp struct {
-		Versions []Version `mapstructure:"versions" json:"versions"`
+func ExtractVersions(r pagination.Page) ([]Version, error) {
+	var s struct {
+		Versions []Version `json:"versions"`
 	}
-
-	err := mapstructure.Decode(casted, &resp)
-	return resp.Versions, err
+	err := (r.(VersionPage)).ExtractInto(&s)
+	return s.Versions, err
 }
 
 // Extract retrieves a single Version struct from an operation result.
 func (r GetVersionResult) Extract() (*Version, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Version *Version `json:"version"`
 	}
-
-	var response struct {
-		Version Version `mapstructure:"version"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-	return &response.Version, err
+	err := r.ExtractInto(&s)
+	return s.Version, err
 }
diff --git a/openstack/db/v1/datastores/testing/doc.go b/openstack/db/v1/datastores/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/db/v1/datastores/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/db/v1/datastores/testing/fixtures.go b/openstack/db/v1/datastores/testing/fixtures.go
new file mode 100644
index 0000000..3b82646
--- /dev/null
+++ b/openstack/db/v1/datastores/testing/fixtures.go
@@ -0,0 +1,101 @@
+package testing
+
+import (
+	"fmt"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/datastores"
+)
+
+const version1JSON = `
+{
+	"id": "b00000b0-00b0-0b00-00b0-000b000000bb",
+	"links": [
+		{
+			"href": "https://10.240.28.38:8779/v1.0/1234/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb",
+			"rel": "self"
+		},
+		{
+			"href": "https://10.240.28.38:8779/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb",
+			"rel": "bookmark"
+		}
+	],
+	"name": "5.1"
+}
+`
+
+const version2JSON = `
+{
+	"id": "c00000b0-00c0-0c00-00c0-000b000000cc",
+	"links": [
+		{
+			"href": "https://10.240.28.38:8779/v1.0/1234/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc",
+			"rel": "self"
+		},
+		{
+			"href": "https://10.240.28.38:8779/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc",
+			"rel": "bookmark"
+		}
+	],
+	"name": "5.2"
+}
+`
+
+var versionsJSON = fmt.Sprintf(`"versions": [%s, %s]`, version1JSON, version2JSON)
+
+var singleDSJSON = fmt.Sprintf(`
+{
+  "default_version": "c00000b0-00c0-0c00-00c0-000b000000cc",
+  "id": "10000000-0000-0000-0000-000000000001",
+  "links": [
+    {
+      "href": "https://10.240.28.38:8779/v1.0/1234/datastores/10000000-0000-0000-0000-000000000001",
+      "rel": "self"
+    },
+    {
+      "href": "https://10.240.28.38:8779/datastores/10000000-0000-0000-0000-000000000001",
+      "rel": "bookmark"
+    }
+  ],
+  "name": "mysql",
+  %s
+}
+`, versionsJSON)
+
+var (
+	ListDSResp       = fmt.Sprintf(`{"datastores":[%s]}`, singleDSJSON)
+	GetDSResp        = fmt.Sprintf(`{"datastore":%s}`, singleDSJSON)
+	ListVersionsResp = fmt.Sprintf(`{%s}`, versionsJSON)
+	GetVersionResp   = fmt.Sprintf(`{"version":%s}`, version1JSON)
+)
+
+var ExampleVersion1 = datastores.Version{
+	ID: "b00000b0-00b0-0b00-00b0-000b000000bb",
+	Links: []gophercloud.Link{
+		{Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb"},
+		{Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb"},
+	},
+	Name: "5.1",
+}
+
+var exampleVersion2 = datastores.Version{
+	ID: "c00000b0-00c0-0c00-00c0-000b000000cc",
+	Links: []gophercloud.Link{
+		{Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc"},
+		{Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc"},
+	},
+	Name: "5.2",
+}
+
+var ExampleVersions = []datastores.Version{ExampleVersion1, exampleVersion2}
+
+var ExampleDatastore = datastores.Datastore{
+	DefaultVersion: "c00000b0-00c0-0c00-00c0-000b000000cc",
+	ID:             "10000000-0000-0000-0000-000000000001",
+	Links: []gophercloud.Link{
+		{Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/10000000-0000-0000-0000-000000000001"},
+		{Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/10000000-0000-0000-0000-000000000001"},
+	},
+	Name:     "mysql",
+	Versions: ExampleVersions,
+}
diff --git a/openstack/db/v1/datastores/testing/requests_test.go b/openstack/db/v1/datastores/testing/requests_test.go
new file mode 100644
index 0000000..b505726
--- /dev/null
+++ b/openstack/db/v1/datastores/testing/requests_test.go
@@ -0,0 +1,79 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/db/v1/datastores"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+	"github.com/gophercloud/gophercloud/testhelper/fixture"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, "/datastores", "GET", "", ListDSResp, 200)
+
+	pages := 0
+
+	err := datastores.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := datastores.ExtractDatastores(page)
+		if err != nil {
+			return false, err
+		}
+
+		th.CheckDeepEquals(t, []datastores.Datastore{ExampleDatastore}, actual)
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, pages)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, "/datastores/{dsID}", "GET", "", GetDSResp, 200)
+
+	ds, err := datastores.Get(fake.ServiceClient(), "{dsID}").Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, &ExampleDatastore, ds)
+}
+
+func TestListVersions(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, "/datastores/{dsID}/versions", "GET", "", ListVersionsResp, 200)
+
+	pages := 0
+
+	err := datastores.ListVersions(fake.ServiceClient(), "{dsID}").EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := datastores.ExtractVersions(page)
+		if err != nil {
+			return false, err
+		}
+
+		th.CheckDeepEquals(t, ExampleVersions, actual)
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, pages)
+}
+
+func TestGetVersion(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	fixture.SetupHandler(t, "/datastores/{dsID}/versions/{versionID}", "GET", "", GetVersionResp, 200)
+
+	ds, err := datastores.GetVersion(fake.ServiceClient(), "{dsID}", "{versionID}").Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, &ExampleVersion1, ds)
+}
diff --git a/openstack/db/v1/datastores/urls.go b/openstack/db/v1/datastores/urls.go
index c4d5248..06d1b3d 100644
--- a/openstack/db/v1/datastores/urls.go
+++ b/openstack/db/v1/datastores/urls.go
@@ -1,6 +1,6 @@
 package datastores
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func baseURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("datastores")
diff --git a/openstack/db/v1/flavors/fixtures.go b/openstack/db/v1/flavors/fixtures.go
deleted file mode 100644
index 9839324..0000000
--- a/openstack/db/v1/flavors/fixtures.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// +build fixtures
-
-package flavors
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-const flavor = `
-{
-	"id": %d,
-	"links": [
-		{
-			"href": "https://openstack.example.com/v1.0/1234/flavors/%d",
-			"rel": "self"
-		},
-		{
-			"href": "https://openstack.example.com/flavors/%d",
-			"rel": "bookmark"
-		}
-	],
-	"name": "%s",
-	"ram": %d
-}
-`
-
-var (
-	flavorID = "{flavorID}"
-	_baseURL = "/flavors"
-	resURL   = "/flavors/" + flavorID
-)
-
-var (
-	flavor1 = fmt.Sprintf(flavor, 1, 1, 1, "m1.tiny", 512)
-	flavor2 = fmt.Sprintf(flavor, 2, 2, 2, "m1.small", 1024)
-	flavor3 = fmt.Sprintf(flavor, 3, 3, 3, "m1.medium", 2048)
-	flavor4 = fmt.Sprintf(flavor, 4, 4, 4, "m1.large", 4096)
-
-	listFlavorsResp = fmt.Sprintf(`{"flavors":[%s, %s, %s, %s]}`, flavor1, flavor2, flavor3, flavor4)
-	getFlavorResp   = fmt.Sprintf(`{"flavor": %s}`, flavor1)
-)
-
-func HandleList(t *testing.T) {
-	fixture.SetupHandler(t, _baseURL, "GET", "", listFlavorsResp, 200)
-}
-
-func HandleGet(t *testing.T) {
-	fixture.SetupHandler(t, resURL, "GET", "", getFlavorResp, 200)
-}
diff --git a/openstack/db/v1/flavors/requests.go b/openstack/db/v1/flavors/requests.go
index fa34446..7fac56a 100644
--- a/openstack/db/v1/flavors/requests.go
+++ b/openstack/db/v1/flavors/requests.go
@@ -1,29 +1,21 @@
 package flavors
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // List will list all available hardware flavors that an instance can use. The
 // operation is identical to the one supported by the Nova API, but without the
 // "disk" property.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
 		return FlavorPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, listURL(client), createPage)
+	})
 }
 
 // Get will retrieve information for a specified hardware flavor.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var gr GetResult
-
-	_, gr.Err = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{
-		JSONResponse: &gr.Body,
-		OkCodes:      []int{200},
-	})
-
-	return gr
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return
 }
diff --git a/openstack/db/v1/flavors/requests_test.go b/openstack/db/v1/flavors/requests_test.go
deleted file mode 100644
index 88b5871..0000000
--- a/openstack/db/v1/flavors/requests_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package flavors
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListFlavors(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleList(t)
-
-	pages := 0
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractFlavors(page)
-		if err != nil {
-			return false, err
-		}
-
-		expected := []Flavor{
-			Flavor{
-				ID:   "1",
-				Name: "m1.tiny",
-				RAM:  512,
-				Links: []gophercloud.Link{
-					gophercloud.Link{Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"},
-					gophercloud.Link{Href: "https://openstack.example.com/flavors/1", Rel: "bookmark"},
-				},
-			},
-			Flavor{
-				ID:   "2",
-				Name: "m1.small",
-				RAM:  1024,
-				Links: []gophercloud.Link{
-					gophercloud.Link{Href: "https://openstack.example.com/v1.0/1234/flavors/2", Rel: "self"},
-					gophercloud.Link{Href: "https://openstack.example.com/flavors/2", Rel: "bookmark"},
-				},
-			},
-			Flavor{
-				ID:   "3",
-				Name: "m1.medium",
-				RAM:  2048,
-				Links: []gophercloud.Link{
-					gophercloud.Link{Href: "https://openstack.example.com/v1.0/1234/flavors/3", Rel: "self"},
-					gophercloud.Link{Href: "https://openstack.example.com/flavors/3", Rel: "bookmark"},
-				},
-			},
-			Flavor{
-				ID:   "4",
-				Name: "m1.large",
-				RAM:  4096,
-				Links: []gophercloud.Link{
-					gophercloud.Link{Href: "https://openstack.example.com/v1.0/1234/flavors/4", Rel: "self"},
-					gophercloud.Link{Href: "https://openstack.example.com/flavors/4", Rel: "bookmark"},
-				},
-			},
-		}
-
-		th.AssertDeepEquals(t, expected, actual)
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGetFlavor(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGet(t)
-
-	actual, err := Get(fake.ServiceClient(), flavorID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Flavor{
-		ID:   "1",
-		Name: "m1.tiny",
-		RAM:  512,
-		Links: []gophercloud.Link{
-			gophercloud.Link{Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"},
-		},
-	}
-
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/openstack/db/v1/flavors/results.go b/openstack/db/v1/flavors/results.go
index 2cee010..f74f20c 100644
--- a/openstack/db/v1/flavors/results.go
+++ b/openstack/db/v1/flavors/results.go
@@ -1,9 +1,8 @@
 package flavors
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // GetResult temporarily holds the response from a Get call.
@@ -12,34 +11,24 @@
 }
 
 // Extract provides access to the individual Flavor returned by the Get function.
-func (gr GetResult) Extract() (*Flavor, error) {
-	if gr.Err != nil {
-		return nil, gr.Err
+func (r GetResult) Extract() (*Flavor, error) {
+	var s struct {
+		Flavor *Flavor `json:"flavor"`
 	}
-
-	var result struct {
-		Flavor Flavor `mapstructure:"flavor"`
-	}
-
-	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
-		WeaklyTypedInput: true,
-		Result:           &result,
-	})
-
-	err = decoder.Decode(gr.Body)
-	return &result.Flavor, err
+	err := r.ExtractInto(&s)
+	return s.Flavor, err
 }
 
 // Flavor records represent (virtual) hardware configurations for server resources in a region.
 type Flavor struct {
 	// The flavor's unique identifier.
-	ID string `mapstructure:"id"`
+	ID int `json:"id"`
 
 	// The RAM capacity for the flavor.
-	RAM int `mapstructure:"ram"`
+	RAM int `json:"ram"`
 
 	// The Name field provides a human-readable moniker for the flavor.
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 
 	// Links to access the flavor.
 	Links []gophercloud.Link
@@ -51,42 +40,28 @@
 }
 
 // IsEmpty determines if a page contains any results.
-func (p FlavorPage) IsEmpty() (bool, error) {
-	flavors, err := ExtractFlavors(p)
-	if err != nil {
-		return true, err
-	}
-	return len(flavors) == 0, nil
+func (page FlavorPage) IsEmpty() (bool, error) {
+	flavors, err := ExtractFlavors(page)
+	return len(flavors) == 0, err
 }
 
 // NextPageURL uses the response's embedded link reference to navigate to the next page of results.
-func (p FlavorPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"flavors_links"`
+func (page FlavorPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"flavors_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // ExtractFlavors provides access to the list of flavors in a page acquired from the List operation.
-func ExtractFlavors(page pagination.Page) ([]Flavor, error) {
-	casted := page.(FlavorPage).Body
-	var container struct {
-		Flavors []Flavor `mapstructure:"flavors"`
+func ExtractFlavors(r pagination.Page) ([]Flavor, error) {
+	var s struct {
+		Flavors []Flavor `json:"flavors"`
 	}
-
-	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
-		WeaklyTypedInput: true,
-		Result:           &container,
-	})
-
-	err = decoder.Decode(casted)
-
-	return container.Flavors, err
+	err := (r.(FlavorPage)).ExtractInto(&s)
+	return s.Flavors, err
 }
diff --git a/openstack/db/v1/flavors/testing/doc.go b/openstack/db/v1/flavors/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/db/v1/flavors/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/db/v1/flavors/testing/fixtures.go b/openstack/db/v1/flavors/testing/fixtures.go
new file mode 100644
index 0000000..b2b2d5f
--- /dev/null
+++ b/openstack/db/v1/flavors/testing/fixtures.go
@@ -0,0 +1,50 @@
+package testing
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/testhelper/fixture"
+)
+
+const flavor = `
+{
+	"id": %d,
+	"links": [
+		{
+			"href": "https://openstack.example.com/v1.0/1234/flavors/%d",
+			"rel": "self"
+		},
+		{
+			"href": "https://openstack.example.com/flavors/%d",
+			"rel": "bookmark"
+		}
+	],
+	"name": "%s",
+	"ram": %d
+}
+`
+
+var (
+	flavorID = "{flavorID}"
+	_baseURL = "/flavors"
+	resURL   = "/flavors/" + flavorID
+)
+
+var (
+	flavor1 = fmt.Sprintf(flavor, 1, 1, 1, "m1.tiny", 512)
+	flavor2 = fmt.Sprintf(flavor, 2, 2, 2, "m1.small", 1024)
+	flavor3 = fmt.Sprintf(flavor, 3, 3, 3, "m1.medium", 2048)
+	flavor4 = fmt.Sprintf(flavor, 4, 4, 4, "m1.large", 4096)
+
+	listFlavorsResp = fmt.Sprintf(`{"flavors":[%s, %s, %s, %s]}`, flavor1, flavor2, flavor3, flavor4)
+	getFlavorResp   = fmt.Sprintf(`{"flavor": %s}`, flavor1)
+)
+
+func HandleList(t *testing.T) {
+	fixture.SetupHandler(t, _baseURL, "GET", "", listFlavorsResp, 200)
+}
+
+func HandleGet(t *testing.T) {
+	fixture.SetupHandler(t, resURL, "GET", "", getFlavorResp, 200)
+}
diff --git a/openstack/db/v1/flavors/testing/requests_test.go b/openstack/db/v1/flavors/testing/requests_test.go
new file mode 100644
index 0000000..cbc0edd
--- /dev/null
+++ b/openstack/db/v1/flavors/testing/requests_test.go
@@ -0,0 +1,92 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/flavors"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestListFlavors(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleList(t)
+
+	pages := 0
+	err := flavors.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := flavors.ExtractFlavors(page)
+		if err != nil {
+			return false, err
+		}
+
+		expected := []flavors.Flavor{
+			{
+				ID:   1,
+				Name: "m1.tiny",
+				RAM:  512,
+				Links: []gophercloud.Link{
+					{Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"},
+					{Href: "https://openstack.example.com/flavors/1", Rel: "bookmark"},
+				},
+			},
+			{
+				ID:   2,
+				Name: "m1.small",
+				RAM:  1024,
+				Links: []gophercloud.Link{
+					{Href: "https://openstack.example.com/v1.0/1234/flavors/2", Rel: "self"},
+					{Href: "https://openstack.example.com/flavors/2", Rel: "bookmark"},
+				},
+			},
+			{
+				ID:   3,
+				Name: "m1.medium",
+				RAM:  2048,
+				Links: []gophercloud.Link{
+					{Href: "https://openstack.example.com/v1.0/1234/flavors/3", Rel: "self"},
+					{Href: "https://openstack.example.com/flavors/3", Rel: "bookmark"},
+				},
+			},
+			{
+				ID:   4,
+				Name: "m1.large",
+				RAM:  4096,
+				Links: []gophercloud.Link{
+					{Href: "https://openstack.example.com/v1.0/1234/flavors/4", Rel: "self"},
+					{Href: "https://openstack.example.com/flavors/4", Rel: "bookmark"},
+				},
+			},
+		}
+
+		th.AssertDeepEquals(t, expected, actual)
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, pages)
+}
+
+func TestGetFlavor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGet(t)
+
+	actual, err := flavors.Get(fake.ServiceClient(), flavorID).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &flavors.Flavor{
+		ID:   1,
+		Name: "m1.tiny",
+		RAM:  512,
+		Links: []gophercloud.Link{
+			{Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"},
+		},
+	}
+
+	th.AssertDeepEquals(t, expected, actual)
+}
diff --git a/openstack/db/v1/flavors/urls.go b/openstack/db/v1/flavors/urls.go
index 80da11f..a24301b 100644
--- a/openstack/db/v1/flavors/urls.go
+++ b/openstack/db/v1/flavors/urls.go
@@ -1,6 +1,6 @@
 package flavors
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func getURL(client *gophercloud.ServiceClient, id string) string {
 	return client.ServiceURL("flavors", id)
diff --git a/openstack/db/v1/instances/fixtures.go b/openstack/db/v1/instances/fixtures.go
deleted file mode 100644
index eb1da6a..0000000
--- a/openstack/db/v1/instances/fixtures.go
+++ /dev/null
@@ -1,171 +0,0 @@
-// +build fixtures
-
-package instances
-
-import (
-	"fmt"
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/db/v1/datastores"
-	"github.com/rackspace/gophercloud/openstack/db/v1/flavors"
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-var (
-	timestamp  = "2015-11-12T14:22:42Z"
-	timeVal, _ = time.Parse(time.RFC3339, timestamp)
-)
-
-var instance = `
-{
-  "created": "` + timestamp + `",
-  "datastore": {
-    "type": "mysql",
-    "version": "5.6"
-  },
-  "flavor": {
-    "id": "1",
-    "links": [
-      {
-        "href": "https://my-openstack.com/v1.0/1234/flavors/1",
-        "rel": "self"
-      },
-      {
-        "href": "https://my-openstack.com/v1.0/1234/flavors/1",
-        "rel": "bookmark"
-      }
-    ]
-  },
-  "links": [
-    {
-      "href": "https://my-openstack.com/v1.0/1234/instances/1",
-      "rel": "self"
-    }
-  ],
-  "hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.my-openstack.com",
-  "id": "{instanceID}",
-  "name": "json_rack_instance",
-  "status": "BUILD",
-  "updated": "` + timestamp + `",
-  "volume": {
-    "size": 2
-  }
-}
-`
-
-var createReq = `
-{
-	"instance": {
-		"databases": [
-			{
-				"character_set": "utf8",
-				"collate": "utf8_general_ci",
-				"name": "sampledb"
-			},
-			{
-				"name": "nextround"
-			}
-		],
-		"flavorRef": "1",
-		"name": "json_rack_instance",
-		"users": [
-			{
-				"databases": [
-					{
-						"name": "sampledb"
-					}
-				],
-				"name": "demouser",
-				"password": "demopassword"
-			}
-		],
-		"volume": {
-			"size": 2
-		}
-	}
-}
-`
-
-var (
-	instanceID = "{instanceID}"
-	rootURL    = "/instances"
-	resURL     = rootURL + "/" + instanceID
-	uRootURL   = resURL + "/root"
-	aURL       = resURL + "/action"
-)
-
-var (
-	restartReq   = `{"restart": {}}`
-	resizeReq    = `{"resize": {"flavorRef": "2"}}`
-	resizeVolReq = `{"resize": {"volume": {"size": 4}}}`
-)
-
-var (
-	createResp        = fmt.Sprintf(`{"instance": %s}`, instance)
-	listInstancesResp = fmt.Sprintf(`{"instances":[%s]}`, instance)
-	getInstanceResp   = createResp
-	enableUserResp    = `{"user":{"name":"root","password":"secretsecret"}}`
-	isUserEnabledResp = `{"rootEnabled":true}`
-)
-
-var expectedInstance = Instance{
-	Created: timeVal,
-	Updated: timeVal,
-	Flavor: flavors.Flavor{
-		ID: "1",
-		Links: []gophercloud.Link{
-			gophercloud.Link{Href: "https://my-openstack.com/v1.0/1234/flavors/1", Rel: "self"},
-			gophercloud.Link{Href: "https://my-openstack.com/v1.0/1234/flavors/1", Rel: "bookmark"},
-		},
-	},
-	Hostname: "e09ad9a3f73309469cf1f43d11e79549caf9acf2.my-openstack.com",
-	ID:       instanceID,
-	Links: []gophercloud.Link{
-		gophercloud.Link{Href: "https://my-openstack.com/v1.0/1234/instances/1", Rel: "self"},
-	},
-	Name:   "json_rack_instance",
-	Status: "BUILD",
-	Volume: Volume{Size: 2},
-	Datastore: datastores.DatastorePartial{
-		Type:    "mysql",
-		Version: "5.6",
-	},
-}
-
-func HandleCreate(t *testing.T) {
-	fixture.SetupHandler(t, rootURL, "POST", createReq, createResp, 200)
-}
-
-func HandleList(t *testing.T) {
-	fixture.SetupHandler(t, rootURL, "GET", "", listInstancesResp, 200)
-}
-
-func HandleGet(t *testing.T) {
-	fixture.SetupHandler(t, resURL, "GET", "", getInstanceResp, 200)
-}
-
-func HandleDelete(t *testing.T) {
-	fixture.SetupHandler(t, resURL, "DELETE", "", "", 202)
-}
-
-func HandleEnableRoot(t *testing.T) {
-	fixture.SetupHandler(t, uRootURL, "POST", "", enableUserResp, 200)
-}
-
-func HandleIsRootEnabled(t *testing.T) {
-	fixture.SetupHandler(t, uRootURL, "GET", "", isUserEnabledResp, 200)
-}
-
-func HandleRestart(t *testing.T) {
-	fixture.SetupHandler(t, aURL, "POST", restartReq, "", 202)
-}
-
-func HandleResize(t *testing.T) {
-	fixture.SetupHandler(t, aURL, "POST", resizeReq, "", 202)
-}
-
-func HandleResizeVol(t *testing.T) {
-	fixture.SetupHandler(t, aURL, "POST", resizeVolReq, "", 202)
-}
diff --git a/openstack/db/v1/instances/requests.go b/openstack/db/v1/instances/requests.go
index f4a63b8..4f06649 100644
--- a/openstack/db/v1/instances/requests.go
+++ b/openstack/db/v1/instances/requests.go
@@ -1,12 +1,10 @@
 package instances
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	"github.com/rackspace/gophercloud/openstack/db/v1/users"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	db "github.com/gophercloud/gophercloud/openstack/db/v1/databases"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/users"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // CreateOptsBuilder is the top-level interface for create options.
@@ -16,15 +14,13 @@
 
 // DatastoreOpts represents the configuration for how an instance stores data.
 type DatastoreOpts struct {
-	Version string
-	Type    string
+	Version string `json:"version"`
+	Type    string `json:"type"`
 }
 
-func (opts DatastoreOpts) ToMap() (map[string]string, error) {
-	return map[string]string{
-		"version": opts.Version,
-		"type":    opts.Type,
-	}, nil
+// ToMap converts a DatastoreOpts to a map[string]string (for a request body)
+func (opts DatastoreOpts) ToMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "")
 }
 
 // CreateOpts is the struct responsible for configuring a new database instance.
@@ -32,21 +28,16 @@
 	// Either the integer UUID (in string form) of the flavor, or its URI
 	// reference as specified in the response from the List() call. Required.
 	FlavorRef string
-
 	// Specifies the volume size in gigabytes (GB). The value must be between 1
 	// and 300. Required.
 	Size int
-
 	// Name of the instance to create. The length of the name is limited to
 	// 255 characters and any characters are permitted. Optional.
 	Name string
-
 	// A slice of database information options.
 	Databases db.CreateOptsBuilder
-
 	// A slice of user information options.
 	Users users.CreateOptsBuilder
-
 	// Options to configure the type of datastore the instance will use. This is
 	// optional, and if excluded will default to MySQL.
 	Datastore *DatastoreOpts
@@ -55,10 +46,15 @@
 // ToInstanceCreateMap will render a JSON map.
 func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) {
 	if opts.Size > 300 || opts.Size < 1 {
-		return nil, fmt.Errorf("Size (GB) must be between 1-300")
+		err := gophercloud.ErrInvalidInput{}
+		err.Argument = "instances.CreateOpts.Size"
+		err.Value = opts.Size
+		err.Info = "Size (GB) must be between 1-300"
+		return nil, err
 	}
+
 	if opts.FlavorRef == "" {
-		return nil, fmt.Errorf("FlavorRef is a required field")
+		return nil, gophercloud.ErrMissingInput{Argument: "instances.CreateOpts.FlavorRef"}
 	}
 
 	instance := map[string]interface{}{
@@ -83,6 +79,13 @@
 		}
 		instance["users"] = users["users"]
 	}
+	if opts.Datastore != nil {
+		datastore, err := opts.Datastore.ToMap()
+		if err != nil {
+			return nil, err
+		}
+		instance["datastore"] = datastore
+	}
 
 	return map[string]interface{}{"instance": instance}, nil
 }
@@ -95,144 +98,72 @@
 // Although this call only allows the creation of 1 instance per request, you
 // can create an instance with multiple databases and users. The default
 // binding for a MySQL instance is port 3306.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToInstanceCreateMap()
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToInstanceCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Request("POST", baseURL(client), gophercloud.RequestOpts{
-		JSONBody:     &reqBody,
-		JSONResponse: &res.Body,
-		OkCodes:      []int{200},
-	})
-
-	return res
+	_, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}})
+	return
 }
 
 // List retrieves the status and information for all database instances.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, baseURL(client), func(r pagination.PageResult) pagination.Page {
 		return InstancePage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, baseURL(client), createPageFn)
+	})
 }
 
 // Get retrieves the status and information for a specified database instance.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-
-	_, res.Err = client.Request("GET", resourceURL(client, id), gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
-		OkCodes:      []int{200},
-	})
-
-	return res
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(resourceURL(client, id), &r.Body, nil)
+	return
 }
 
 // Delete permanently destroys the database instance.
-func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-
-	_, res.Err = client.Request("DELETE", resourceURL(client, id), gophercloud.RequestOpts{
-		OkCodes: []int{202},
-	})
-
-	return res
+func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = client.Delete(resourceURL(client, id), nil)
+	return
 }
 
 // EnableRootUser enables the login from any host for the root user and
 // provides the user with a generated root password.
-func EnableRootUser(client *gophercloud.ServiceClient, id string) UserRootResult {
-	var res UserRootResult
-
-	_, res.Err = client.Request("POST", userRootURL(client, id), gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
-		OkCodes:      []int{200},
-	})
-
-	return res
+func EnableRootUser(client *gophercloud.ServiceClient, id string) (r EnableRootUserResult) {
+	_, r.Err = client.Post(userRootURL(client, id), nil, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}})
+	return
 }
 
 // IsRootEnabled checks an instance to see if root access is enabled. It returns
 // True if root user is enabled for the specified database instance or False
 // otherwise.
-func IsRootEnabled(client *gophercloud.ServiceClient, id string) (bool, error) {
-	var res gophercloud.Result
-
-	_, err := client.Request("GET", userRootURL(client, id), gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
-		OkCodes:      []int{200},
-	})
-
-	return res.Body.(map[string]interface{})["rootEnabled"] == true, err
+func IsRootEnabled(client *gophercloud.ServiceClient, id string) (r IsRootEnabledResult) {
+	_, r.Err = client.Get(userRootURL(client, id), &r.Body, nil)
+	return
 }
 
 // Restart will restart only the MySQL Instance. Restarting MySQL will
 // erase any dynamic configuration settings that you have made within MySQL.
 // The MySQL service will be unavailable until the instance restarts.
-func Restart(client *gophercloud.ServiceClient, id string) ActionResult {
-	var res ActionResult
-
-	_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
-		JSONBody: map[string]interface{}{"restart": struct{}{}},
-		OkCodes:  []int{202},
-	})
-
-	return res
+func Restart(client *gophercloud.ServiceClient, id string) (r ActionResult) {
+	b := map[string]interface{}{"restart": struct{}{}}
+	_, r.Err = client.Post(actionURL(client, id), &b, nil, nil)
+	return
 }
 
 // Resize changes the memory size of the instance, assuming a valid
 // flavorRef is provided. It will also restart the MySQL service.
-func Resize(client *gophercloud.ServiceClient, id, flavorRef string) ActionResult {
-	var res ActionResult
-
-	type resize struct {
-		FlavorRef string `json:"flavorRef"`
-	}
-
-	type req struct {
-		Resize resize `json:"resize"`
-	}
-
-	reqBody := req{Resize: resize{FlavorRef: flavorRef}}
-
-	_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
-		JSONBody: reqBody,
-		OkCodes:  []int{202},
-	})
-
-	return res
+func Resize(client *gophercloud.ServiceClient, id, flavorRef string) (r ActionResult) {
+	b := map[string]interface{}{"resize": map[string]string{"flavorRef": flavorRef}}
+	_, r.Err = client.Post(actionURL(client, id), &b, nil, nil)
+	return
 }
 
 // ResizeVolume will resize the attached volume for an instance. It supports
 // only increasing the volume size and does not support decreasing the size.
 // The volume size is in gigabytes (GB) and must be an integer.
-func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) ActionResult {
-	var res ActionResult
-
-	type volume struct {
-		Size int `json:"size"`
-	}
-
-	type resize struct {
-		Volume volume `json:"volume"`
-	}
-
-	type req struct {
-		Resize resize `json:"resize"`
-	}
-
-	reqBody := req{Resize: resize{Volume: volume{Size: size}}}
-
-	_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
-		JSONBody: reqBody,
-		OkCodes:  []int{202},
-	})
-
-	return res
+func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) (r ActionResult) {
+	b := map[string]interface{}{"resize": map[string]interface{}{"volume": map[string]int{"size": size}}}
+	_, r.Err = client.Post(actionURL(client, id), &b, nil, nil)
+	return
 }
diff --git a/openstack/db/v1/instances/requests_test.go b/openstack/db/v1/instances/requests_test.go
deleted file mode 100644
index 3cc2b70..0000000
--- a/openstack/db/v1/instances/requests_test.go
+++ /dev/null
@@ -1,133 +0,0 @@
-package instances
-
-import (
-	"testing"
-
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	"github.com/rackspace/gophercloud/openstack/db/v1/users"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreate(t)
-
-	opts := CreateOpts{
-		Name:      "json_rack_instance",
-		FlavorRef: "1",
-		Databases: db.BatchCreateOpts{
-			db.CreateOpts{CharSet: "utf8", Collate: "utf8_general_ci", Name: "sampledb"},
-			db.CreateOpts{Name: "nextround"},
-		},
-		Users: users.BatchCreateOpts{
-			users.CreateOpts{
-				Name:     "demouser",
-				Password: "demopassword",
-				Databases: db.BatchCreateOpts{
-					db.CreateOpts{Name: "sampledb"},
-				},
-			},
-		},
-		Size: 2,
-	}
-
-	instance, err := Create(fake.ServiceClient(), opts).Extract()
-
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, &expectedInstance, instance)
-}
-
-func TestInstanceList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleList(t)
-
-	pages := 0
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractInstances(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, []Instance{expectedInstance}, actual)
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGetInstance(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGet(t)
-
-	instance, err := Get(fake.ServiceClient(), instanceID).Extract()
-
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, &expectedInstance, instance)
-}
-
-func TestDeleteInstance(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDelete(t)
-
-	res := Delete(fake.ServiceClient(), instanceID)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestEnableRootUser(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleEnableRoot(t)
-
-	expected := &users.User{Name: "root", Password: "secretsecret"}
-	user, err := EnableRootUser(fake.ServiceClient(), instanceID).Extract()
-
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, user)
-}
-
-func TestIsRootEnabled(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleIsRootEnabled(t)
-
-	isEnabled, err := IsRootEnabled(fake.ServiceClient(), instanceID)
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, true, isEnabled)
-}
-
-func TestRestart(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleRestart(t)
-
-	res := Restart(fake.ServiceClient(), instanceID)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestResize(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleResize(t)
-
-	res := Resize(fake.ServiceClient(), instanceID, "2")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestResizeVolume(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleResizeVol(t)
-
-	res := ResizeVolume(fake.ServiceClient(), instanceID, 4)
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/db/v1/instances/results.go b/openstack/db/v1/instances/results.go
index 95aed16..21900d7 100644
--- a/openstack/db/v1/instances/results.go
+++ b/openstack/db/v1/instances/results.go
@@ -1,16 +1,13 @@
 package instances
 
 import (
-	"fmt"
-	"reflect"
 	"time"
 
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/db/v1/datastores"
-	"github.com/rackspace/gophercloud/openstack/db/v1/flavors"
-	"github.com/rackspace/gophercloud/openstack/db/v1/users"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/datastores"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/flavors"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/users"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Volume represents information about an attached volume for a database instance.
@@ -24,10 +21,10 @@
 // Instance represents a remote MySQL instance.
 type Instance struct {
 	// Indicates the datetime that the instance was created
-	Created time.Time `mapstructure:"-"`
+	Created time.Time `json:"created"`
 
 	// Indicates the most recent datetime that the instance was updated.
-	Updated time.Time `mapstructure:"-"`
+	Updated time.Time `json:"updated"`
 
 	// Indicates the hardware flavor the instance uses.
 	Flavor flavors.Flavor
@@ -80,34 +77,11 @@
 
 // Extract will extract an Instance from various result structs.
 func (r commonResult) Extract() (*Instance, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Instance *Instance `json:"instance"`
 	}
-
-	var response struct {
-		Instance Instance `mapstructure:"instance"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-	val := r.Body.(map[string]interface{})["instance"].(map[string]interface{})
-
-	if t, ok := val["created"].(string); ok && t != "" {
-		creationTime, err := time.Parse(time.RFC3339, t)
-		if err != nil {
-			return &response.Instance, err
-		}
-		response.Instance.Created = creationTime
-	}
-
-	if t, ok := val["updated"].(string); ok && t != "" {
-		updatedTime, err := time.Parse(time.RFC3339, t)
-		if err != nil {
-			return &response.Instance, err
-		}
-		response.Instance.Updated = updatedTime
-	}
-
-	return &response.Instance, err
+	err := r.ExtractInto(&s)
+	return s.Instance, err
 }
 
 // InstancePage represents a single page of a paginated instance collection.
@@ -118,91 +92,43 @@
 // IsEmpty checks to see whether the collection is empty.
 func (page InstancePage) IsEmpty() (bool, error) {
 	instances, err := ExtractInstances(page)
-	if err != nil {
-		return true, err
-	}
-	return len(instances) == 0, nil
+	return len(instances) == 0, err
 }
 
 // NextPageURL will retrieve the next page URL.
 func (page InstancePage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"instances_links"`
+	var s struct {
+		Links []gophercloud.Link `json:"instances_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(page.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // ExtractInstances will convert a generic pagination struct into a more
 // relevant slice of Instance structs.
-func ExtractInstances(page pagination.Page) ([]Instance, error) {
-	casted := page.(InstancePage).Body
-
-	var resp struct {
-		Instances []Instance `mapstructure:"instances"`
+func ExtractInstances(r pagination.Page) ([]Instance, error) {
+	var s struct {
+		Instances []Instance `json:"instances"`
 	}
-
-	if err := mapstructure.Decode(casted, &resp); err != nil {
-		return nil, err
-	}
-
-	var vals []interface{}
-	switch casted.(type) {
-	case map[string]interface{}:
-		vals = casted.(map[string]interface{})["instances"].([]interface{})
-	case map[string][]interface{}:
-		vals = casted.(map[string][]interface{})["instances"]
-	default:
-		return resp.Instances, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i, v := range vals {
-		val := v.(map[string]interface{})
-
-		if t, ok := val["created"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return resp.Instances, err
-			}
-			resp.Instances[i].Created = creationTime
-		}
-
-		if t, ok := val["updated"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return resp.Instances, err
-			}
-			resp.Instances[i].Updated = updatedTime
-		}
-	}
-
-	return resp.Instances, nil
+	err := (r.(InstancePage)).ExtractInto(&s)
+	return s.Instances, err
 }
 
-// UserRootResult represents the result of an operation to enable the root user.
-type UserRootResult struct {
+// EnableRootUserResult represents the result of an operation to enable the root user.
+type EnableRootUserResult struct {
 	gophercloud.Result
 }
 
 // Extract will extract root user information from a UserRootResult.
-func (r UserRootResult) Extract() (*users.User, error) {
-	if r.Err != nil {
-		return nil, r.Err
+func (r EnableRootUserResult) Extract() (*users.User, error) {
+	var s struct {
+		User *users.User `json:"user"`
 	}
-
-	var response struct {
-		User users.User `mapstructure:"user"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.User, err
+	err := r.ExtractInto(&s)
+	return s.User, err
 }
 
 // ActionResult represents the result of action requests, such as: restarting
@@ -211,3 +137,14 @@
 type ActionResult struct {
 	gophercloud.ErrResult
 }
+
+// IsRootEnabledResult is the result of a call to IsRootEnabled. To see if
+// root is enabled, call the type's Extract method.
+type IsRootEnabledResult struct {
+	gophercloud.Result
+}
+
+// Extract is used to extract the data from a IsRootEnabledResult.
+func (r IsRootEnabledResult) Extract() (bool, error) {
+	return r.Body.(map[string]interface{})["rootEnabled"] == true, r.Err
+}
diff --git a/openstack/db/v1/instances/testing/doc.go b/openstack/db/v1/instances/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/db/v1/instances/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/db/v1/instances/testing/fixtures.go b/openstack/db/v1/instances/testing/fixtures.go
new file mode 100644
index 0000000..e0a0d28
--- /dev/null
+++ b/openstack/db/v1/instances/testing/fixtures.go
@@ -0,0 +1,170 @@
+package testing
+
+import (
+	"fmt"
+	"testing"
+	"time"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/datastores"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/flavors"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/instances"
+	"github.com/gophercloud/gophercloud/testhelper/fixture"
+)
+
+var (
+	timestamp  = "2015-11-12T14:22:42Z"
+	timeVal, _ = time.Parse(time.RFC3339, timestamp)
+)
+
+var instance = `
+{
+  "created": "` + timestamp + `",
+  "datastore": {
+    "type": "mysql",
+    "version": "5.6"
+  },
+  "flavor": {
+    "id": 1,
+    "links": [
+      {
+        "href": "https://my-openstack.com/v1.0/1234/flavors/1",
+        "rel": "self"
+      },
+      {
+        "href": "https://my-openstack.com/v1.0/1234/flavors/1",
+        "rel": "bookmark"
+      }
+    ]
+  },
+  "links": [
+    {
+      "href": "https://my-openstack.com/v1.0/1234/instances/1",
+      "rel": "self"
+    }
+  ],
+  "hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.my-openstack.com",
+  "id": "{instanceID}",
+  "name": "json_rack_instance",
+  "status": "BUILD",
+  "updated": "` + timestamp + `",
+  "volume": {
+    "size": 2
+  }
+}
+`
+
+var createReq = `
+{
+	"instance": {
+		"databases": [
+			{
+				"character_set": "utf8",
+				"collate": "utf8_general_ci",
+				"name": "sampledb"
+			},
+			{
+				"name": "nextround"
+			}
+		],
+		"flavorRef": "1",
+		"name": "json_rack_instance",
+		"users": [
+			{
+				"databases": [
+					{
+						"name": "sampledb"
+					}
+				],
+				"name": "demouser",
+				"password": "demopassword"
+			}
+		],
+		"volume": {
+			"size": 2
+		}
+	}
+}
+`
+
+var (
+	instanceID = "{instanceID}"
+	rootURL    = "/instances"
+	resURL     = rootURL + "/" + instanceID
+	uRootURL   = resURL + "/root"
+	aURL       = resURL + "/action"
+)
+
+var (
+	restartReq   = `{"restart": {}}`
+	resizeReq    = `{"resize": {"flavorRef": "2"}}`
+	resizeVolReq = `{"resize": {"volume": {"size": 4}}}`
+)
+
+var (
+	createResp        = fmt.Sprintf(`{"instance": %s}`, instance)
+	listInstancesResp = fmt.Sprintf(`{"instances":[%s]}`, instance)
+	getInstanceResp   = createResp
+	enableUserResp    = `{"user":{"name":"root","password":"secretsecret"}}`
+	isUserEnabledResp = `{"rootEnabled":true}`
+)
+
+var expectedInstance = instances.Instance{
+	Created: timeVal,
+	Updated: timeVal,
+	Flavor: flavors.Flavor{
+		ID: 1,
+		Links: []gophercloud.Link{
+			{Href: "https://my-openstack.com/v1.0/1234/flavors/1", Rel: "self"},
+			{Href: "https://my-openstack.com/v1.0/1234/flavors/1", Rel: "bookmark"},
+		},
+	},
+	Hostname: "e09ad9a3f73309469cf1f43d11e79549caf9acf2.my-openstack.com",
+	ID:       instanceID,
+	Links: []gophercloud.Link{
+		{Href: "https://my-openstack.com/v1.0/1234/instances/1", Rel: "self"},
+	},
+	Name:   "json_rack_instance",
+	Status: "BUILD",
+	Volume: instances.Volume{Size: 2},
+	Datastore: datastores.DatastorePartial{
+		Type:    "mysql",
+		Version: "5.6",
+	},
+}
+
+func HandleCreate(t *testing.T) {
+	fixture.SetupHandler(t, rootURL, "POST", createReq, createResp, 200)
+}
+
+func HandleList(t *testing.T) {
+	fixture.SetupHandler(t, rootURL, "GET", "", listInstancesResp, 200)
+}
+
+func HandleGet(t *testing.T) {
+	fixture.SetupHandler(t, resURL, "GET", "", getInstanceResp, 200)
+}
+
+func HandleDelete(t *testing.T) {
+	fixture.SetupHandler(t, resURL, "DELETE", "", "", 202)
+}
+
+func HandleEnableRoot(t *testing.T) {
+	fixture.SetupHandler(t, uRootURL, "POST", "", enableUserResp, 200)
+}
+
+func HandleIsRootEnabled(t *testing.T) {
+	fixture.SetupHandler(t, uRootURL, "GET", "", isUserEnabledResp, 200)
+}
+
+func HandleRestart(t *testing.T) {
+	fixture.SetupHandler(t, aURL, "POST", restartReq, "", 202)
+}
+
+func HandleResize(t *testing.T) {
+	fixture.SetupHandler(t, aURL, "POST", resizeReq, "", 202)
+}
+
+func HandleResizeVol(t *testing.T) {
+	fixture.SetupHandler(t, aURL, "POST", resizeVolReq, "", 202)
+}
diff --git a/openstack/db/v1/instances/testing/requests_test.go b/openstack/db/v1/instances/testing/requests_test.go
new file mode 100644
index 0000000..e3c81e3
--- /dev/null
+++ b/openstack/db/v1/instances/testing/requests_test.go
@@ -0,0 +1,134 @@
+package testing
+
+import (
+	"testing"
+
+	db "github.com/gophercloud/gophercloud/openstack/db/v1/databases"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/instances"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/users"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleCreate(t)
+
+	opts := instances.CreateOpts{
+		Name:      "json_rack_instance",
+		FlavorRef: "1",
+		Databases: db.BatchCreateOpts{
+			{CharSet: "utf8", Collate: "utf8_general_ci", Name: "sampledb"},
+			{Name: "nextround"},
+		},
+		Users: users.BatchCreateOpts{
+			{
+				Name:     "demouser",
+				Password: "demopassword",
+				Databases: db.BatchCreateOpts{
+					{Name: "sampledb"},
+				},
+			},
+		},
+		Size: 2,
+	}
+
+	instance, err := instances.Create(fake.ServiceClient(), opts).Extract()
+
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, &expectedInstance, instance)
+}
+
+func TestInstanceList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleList(t)
+
+	pages := 0
+	err := instances.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := instances.ExtractInstances(page)
+		if err != nil {
+			return false, err
+		}
+
+		th.CheckDeepEquals(t, []instances.Instance{expectedInstance}, actual)
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, pages)
+}
+
+func TestGetInstance(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGet(t)
+
+	instance, err := instances.Get(fake.ServiceClient(), instanceID).Extract()
+
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, &expectedInstance, instance)
+}
+
+func TestDeleteInstance(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDelete(t)
+
+	res := instances.Delete(fake.ServiceClient(), instanceID)
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestEnableRootUser(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleEnableRoot(t)
+
+	expected := &users.User{Name: "root", Password: "secretsecret"}
+	user, err := instances.EnableRootUser(fake.ServiceClient(), instanceID).Extract()
+
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, expected, user)
+}
+
+func TestIsRootEnabled(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleIsRootEnabled(t)
+
+	isEnabled, err := instances.IsRootEnabled(fake.ServiceClient(), instanceID).Extract()
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, true, isEnabled)
+}
+
+func TestRestart(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleRestart(t)
+
+	res := instances.Restart(fake.ServiceClient(), instanceID)
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestResize(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleResize(t)
+
+	res := instances.Resize(fake.ServiceClient(), instanceID, "2")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestResizeVolume(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleResizeVol(t)
+
+	res := instances.ResizeVolume(fake.ServiceClient(), instanceID, 4)
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/db/v1/instances/urls.go b/openstack/db/v1/instances/urls.go
index 28c0bec..76d1ca5 100644
--- a/openstack/db/v1/instances/urls.go
+++ b/openstack/db/v1/instances/urls.go
@@ -1,6 +1,6 @@
 package instances
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func baseURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("instances")
diff --git a/openstack/db/v1/users/fixtures.go b/openstack/db/v1/users/fixtures.go
deleted file mode 100644
index f5a4cc1..0000000
--- a/openstack/db/v1/users/fixtures.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// +build fixtures
-
-package users
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-const user1 = `
-{"databases": [{"name": "databaseA"}],"name": "dbuser3"%s}
-`
-
-const user2 = `
-{"databases": [{"name": "databaseB"},{"name": "databaseC"}],"name": "dbuser4"%s}
-`
-
-var (
-	instanceID = "{instanceID}"
-	_rootURL   = "/instances/" + instanceID + "/users"
-	pUser1     = fmt.Sprintf(user1, `,"password":"secretsecret"`)
-	pUser2     = fmt.Sprintf(user2, `,"password":"secretsecret"`)
-	createReq  = fmt.Sprintf(`{"users":[%s, %s]}`, pUser1, pUser2)
-	listResp   = fmt.Sprintf(`{"users":[%s, %s]}`, fmt.Sprintf(user1, ""), fmt.Sprintf(user2, ""))
-)
-
-func HandleCreate(t *testing.T) {
-	fixture.SetupHandler(t, _rootURL, "POST", createReq, "", 202)
-}
-
-func HandleList(t *testing.T) {
-	fixture.SetupHandler(t, _rootURL, "GET", "", listResp, 200)
-}
-
-func HandleDelete(t *testing.T) {
-	fixture.SetupHandler(t, _rootURL+"/{userName}", "DELETE", "", "", 202)
-}
diff --git a/openstack/db/v1/users/requests.go b/openstack/db/v1/users/requests.go
index 7533fc4..d342de3 100644
--- a/openstack/db/v1/users/requests.go
+++ b/openstack/db/v1/users/requests.go
@@ -1,11 +1,9 @@
 package users
 
 import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	db "github.com/gophercloud/gophercloud/openstack/db/v1/databases"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // CreateOptsBuilder is the top-level interface for creating JSON maps.
@@ -16,60 +14,35 @@
 // CreateOpts is the struct responsible for configuring a new user; often in the
 // context of an instance.
 type CreateOpts struct {
-	// [REQUIRED] Specifies a name for the user. Valid names can be composed
+	// Specifies a name for the user. Valid names can be composed
 	// of the following characters: letters (either case); numbers; these
 	// characters '@', '?', '#', ' ' but NEVER beginning a name string; '_' is
 	// permitted anywhere. Prohibited characters that are forbidden include:
 	// single quotes, double quotes, back quotes, semicolons, commas, backslashes,
 	// and forward slashes. Spaces at the front or end of a user name are also
 	// not permitted.
-	Name string
-
-	// [REQUIRED] Specifies a password for the user.
-	Password string
-
-	// [OPTIONAL] An array of databases that this user will connect to. The
+	Name string `json:"name" required:"true"`
+	// Specifies a password for the user.
+	Password string `json:"password" required:"true"`
+	// An array of databases that this user will connect to. The
 	// "name" field is the only requirement for each option.
-	Databases db.BatchCreateOpts
-
-	// [OPTIONAL] Specifies the host from which a user is allowed to connect to
+	Databases db.BatchCreateOpts `json:"databases,omitempty"`
+	// Specifies the host from which a user is allowed to connect to
 	// the database. Possible values are a string containing an IPv4 address or
 	// "%" to allow connecting from any host. Optional; the default is "%".
-	Host string
+	Host string `json:"host,omitempty"`
 }
 
 // ToMap is a convenience function for creating sub-maps for individual users.
 func (opts CreateOpts) ToMap() (map[string]interface{}, error) {
-
 	if opts.Name == "root" {
-		return nil, errors.New("root is a reserved user name and cannot be used")
+		err := gophercloud.ErrInvalidInput{}
+		err.Argument = "users.CreateOpts.Name"
+		err.Value = "root"
+		err.Info = "root is a reserved user name and cannot be used"
+		return nil, err
 	}
-	if opts.Name == "" {
-		return nil, errors.New("Name is a required field")
-	}
-	if opts.Password == "" {
-		return nil, errors.New("Password is a required field")
-	}
-
-	user := map[string]interface{}{
-		"name":     opts.Name,
-		"password": opts.Password,
-	}
-
-	if opts.Host != "" {
-		user["host"] = opts.Host
-	}
-
-	dbs := make([]map[string]string, len(opts.Databases))
-	for i, db := range opts.Databases {
-		dbs[i] = map[string]string{"name": db.Name}
-	}
-
-	if len(dbs) > 0 {
-		user["databases"] = dbs
-	}
-
-	return user, nil
+	return gophercloud.BuildRequestBody(opts, "")
 }
 
 // BatchCreateOpts allows multiple users to be created at once.
@@ -92,41 +65,27 @@
 // instance based on the configuration defined in CreateOpts. If databases are
 // assigned for a particular user, the user will be granted all privileges
 // for those specified databases. "root" is a reserved name and cannot be used.
-func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToUserCreateMap()
+func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToUserCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Request("POST", baseURL(client, instanceID), gophercloud.RequestOpts{
-		JSONBody: &reqBody,
-		OkCodes:  []int{202},
-	})
-
-	return res
+	_, r.Err = client.Post(baseURL(client, instanceID), &b, nil, nil)
+	return
 }
 
 // List will list all the users associated with a specified database instance,
 // along with their associated databases. This operation will not return any
 // system users or administrators for a database.
 func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager {
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, baseURL(client, instanceID), func(r pagination.PageResult) pagination.Page {
 		return UserPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, baseURL(client, instanceID), createPageFn)
+	})
 }
 
 // Delete will permanently delete a user from a specified database instance.
-func Delete(client *gophercloud.ServiceClient, instanceID, userName string) DeleteResult {
-	var res DeleteResult
-
-	_, res.Err = client.Request("DELETE", userURL(client, instanceID, userName), gophercloud.RequestOpts{
-		OkCodes: []int{202},
-	})
-
-	return res
+func Delete(client *gophercloud.ServiceClient, instanceID, userName string) (r DeleteResult) {
+	_, r.Err = client.Delete(userURL(client, instanceID, userName), nil)
+	return
 }
diff --git a/openstack/db/v1/users/requests_test.go b/openstack/db/v1/users/requests_test.go
deleted file mode 100644
index 5711f63..0000000
--- a/openstack/db/v1/users/requests_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package users
-
-import (
-	"testing"
-
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreate(t)
-
-	opts := BatchCreateOpts{
-		CreateOpts{
-			Databases: db.BatchCreateOpts{
-				db.CreateOpts{Name: "databaseA"},
-			},
-			Name:     "dbuser3",
-			Password: "secretsecret",
-		},
-		CreateOpts{
-			Databases: db.BatchCreateOpts{
-				db.CreateOpts{Name: "databaseB"},
-				db.CreateOpts{Name: "databaseC"},
-			},
-			Name:     "dbuser4",
-			Password: "secretsecret",
-		},
-	}
-
-	res := Create(fake.ServiceClient(), instanceID, opts)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestUserList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleList(t)
-
-	expectedUsers := []User{
-		User{
-			Databases: []db.Database{
-				db.Database{Name: "databaseA"},
-			},
-			Name: "dbuser3",
-		},
-		User{
-			Databases: []db.Database{
-				db.Database{Name: "databaseB"},
-				db.Database{Name: "databaseC"},
-			},
-			Name: "dbuser4",
-		},
-	}
-
-	pages := 0
-	err := List(fake.ServiceClient(), instanceID).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractUsers(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, expectedUsers, actual)
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDelete(t)
-
-	res := Delete(fake.ServiceClient(), instanceID, "{userName}")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/db/v1/users/results.go b/openstack/db/v1/users/results.go
index 217ddd8..d12a681 100644
--- a/openstack/db/v1/users/results.go
+++ b/openstack/db/v1/users/results.go
@@ -1,10 +1,9 @@
 package users
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	db "github.com/gophercloud/gophercloud/openstack/db/v1/databases"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // User represents a database user
@@ -37,37 +36,27 @@
 // IsEmpty checks to see whether the collection is empty.
 func (page UserPage) IsEmpty() (bool, error) {
 	users, err := ExtractUsers(page)
-	if err != nil {
-		return true, err
-	}
-	return len(users) == 0, nil
+	return len(users) == 0, err
 }
 
 // NextPageURL will retrieve the next page URL.
 func (page UserPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"users_links"`
+	var s struct {
+		Links []gophercloud.Link `json:"users_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(page.Body, &r)
+	err := page.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // ExtractUsers will convert a generic pagination struct into a more
 // relevant slice of User structs.
-func ExtractUsers(page pagination.Page) ([]User, error) {
-	casted := page.(UserPage).Body
-
-	var response struct {
-		Users []User `mapstructure:"users"`
+func ExtractUsers(r pagination.Page) ([]User, error) {
+	var s struct {
+		Users []User `json:"users"`
 	}
-
-	err := mapstructure.Decode(casted, &response)
-
-	return response.Users, err
+	err := (r.(UserPage)).ExtractInto(&s)
+	return s.Users, err
 }
diff --git a/openstack/db/v1/users/testing/doc.go b/openstack/db/v1/users/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/db/v1/users/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/db/v1/users/testing/fixtures.go b/openstack/db/v1/users/testing/fixtures.go
new file mode 100644
index 0000000..f49f46f
--- /dev/null
+++ b/openstack/db/v1/users/testing/fixtures.go
@@ -0,0 +1,37 @@
+package testing
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/testhelper/fixture"
+)
+
+const user1 = `
+{"databases": [{"name": "databaseA"}],"name": "dbuser3"%s}
+`
+
+const user2 = `
+{"databases": [{"name": "databaseB"},{"name": "databaseC"}],"name": "dbuser4"%s}
+`
+
+var (
+	instanceID = "{instanceID}"
+	_rootURL   = "/instances/" + instanceID + "/users"
+	pUser1     = fmt.Sprintf(user1, `,"password":"secretsecret"`)
+	pUser2     = fmt.Sprintf(user2, `,"password":"secretsecret"`)
+	createReq  = fmt.Sprintf(`{"users":[%s, %s]}`, pUser1, pUser2)
+	listResp   = fmt.Sprintf(`{"users":[%s, %s]}`, fmt.Sprintf(user1, ""), fmt.Sprintf(user2, ""))
+)
+
+func HandleCreate(t *testing.T) {
+	fixture.SetupHandler(t, _rootURL, "POST", createReq, "", 202)
+}
+
+func HandleList(t *testing.T) {
+	fixture.SetupHandler(t, _rootURL, "GET", "", listResp, 200)
+}
+
+func HandleDelete(t *testing.T) {
+	fixture.SetupHandler(t, _rootURL+"/{userName}", "DELETE", "", "", 202)
+}
diff --git a/openstack/db/v1/users/testing/requests_test.go b/openstack/db/v1/users/testing/requests_test.go
new file mode 100644
index 0000000..952f245
--- /dev/null
+++ b/openstack/db/v1/users/testing/requests_test.go
@@ -0,0 +1,85 @@
+package testing
+
+import (
+	"testing"
+
+	db "github.com/gophercloud/gophercloud/openstack/db/v1/databases"
+	"github.com/gophercloud/gophercloud/openstack/db/v1/users"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleCreate(t)
+
+	opts := users.BatchCreateOpts{
+		{
+			Databases: db.BatchCreateOpts{
+				db.CreateOpts{Name: "databaseA"},
+			},
+			Name:     "dbuser3",
+			Password: "secretsecret",
+		},
+		{
+			Databases: db.BatchCreateOpts{
+				{Name: "databaseB"},
+				{Name: "databaseC"},
+			},
+			Name:     "dbuser4",
+			Password: "secretsecret",
+		},
+	}
+
+	res := users.Create(fake.ServiceClient(), instanceID, opts)
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestUserList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleList(t)
+
+	expectedUsers := []users.User{
+		{
+			Databases: []db.Database{
+				db.Database{Name: "databaseA"},
+			},
+			Name: "dbuser3",
+		},
+		{
+			Databases: []db.Database{
+				{Name: "databaseB"},
+				{Name: "databaseC"},
+			},
+			Name: "dbuser4",
+		},
+	}
+
+	pages := 0
+	err := users.List(fake.ServiceClient(), instanceID).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := users.ExtractUsers(page)
+		if err != nil {
+			return false, err
+		}
+
+		th.CheckDeepEquals(t, expectedUsers, actual)
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, pages)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDelete(t)
+
+	res := users.Delete(fake.ServiceClient(), instanceID, "{userName}")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/db/v1/users/urls.go b/openstack/db/v1/users/urls.go
index 2a3cacd..8c36a39 100644
--- a/openstack/db/v1/users/urls.go
+++ b/openstack/db/v1/users/urls.go
@@ -1,6 +1,6 @@
 package users
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func baseURL(c *gophercloud.ServiceClient, instanceID string) string {
 	return c.ServiceURL("instances", instanceID, "users")
diff --git a/openstack/endpoint_location.go b/openstack/endpoint_location.go
index 29d02c4..ea37f5b 100644
--- a/openstack/endpoint_location.go
+++ b/openstack/endpoint_location.go
@@ -1,11 +1,9 @@
 package openstack
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
-	tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
+	"github.com/gophercloud/gophercloud"
+	tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
+	tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
 )
 
 // V2EndpointURL discovers the endpoint URL for a specific service from a ServiceCatalog acquired
@@ -29,7 +27,9 @@
 
 	// Report an error if the options were ambiguous.
 	if len(endpoints) > 1 {
-		return "", fmt.Errorf("Discovered %d matching endpoints: %#v", len(endpoints), endpoints)
+		err := &ErrMultipleMatchingEndpointsV2{}
+		err.Endpoints = endpoints
+		return "", err
 	}
 
 	// Extract the appropriate URL from the matching Endpoint.
@@ -42,12 +42,16 @@
 		case gophercloud.AvailabilityAdmin:
 			return gophercloud.NormalizeURL(endpoint.AdminURL), nil
 		default:
-			return "", fmt.Errorf("Unexpected availability in endpoint query: %s", opts.Availability)
+			err := &ErrInvalidAvailabilityProvided{}
+			err.Argument = "Availability"
+			err.Value = opts.Availability
+			return "", err
 		}
 	}
 
 	// Report an error if there were no matching endpoints.
-	return "", gophercloud.ErrEndpointNotFound
+	err := &gophercloud.ErrEndpointNotFound{}
+	return "", err
 }
 
 // V3EndpointURL discovers the endpoint URL for a specific service from a Catalog acquired
@@ -66,7 +70,10 @@
 				if opts.Availability != gophercloud.AvailabilityAdmin &&
 					opts.Availability != gophercloud.AvailabilityPublic &&
 					opts.Availability != gophercloud.AvailabilityInternal {
-					return "", fmt.Errorf("Unexpected availability in endpoint query: %s", opts.Availability)
+					err := &ErrInvalidAvailabilityProvided{}
+					err.Argument = "Availability"
+					err.Value = opts.Availability
+					return "", err
 				}
 				if (opts.Availability == gophercloud.Availability(endpoint.Interface)) &&
 					(opts.Region == "" || endpoint.Region == opts.Region) {
@@ -78,7 +85,7 @@
 
 	// Report an error if the options were ambiguous.
 	if len(endpoints) > 1 {
-		return "", fmt.Errorf("Discovered %d matching endpoints: %#v", len(endpoints), endpoints)
+		return "", ErrMultipleMatchingEndpointsV3{Endpoints: endpoints}
 	}
 
 	// Extract the URL from the matching Endpoint.
@@ -87,5 +94,6 @@
 	}
 
 	// Report an error if there were no matching endpoints.
-	return "", gophercloud.ErrEndpointNotFound
+	err := &gophercloud.ErrEndpointNotFound{}
+	return "", err
 }
diff --git a/openstack/endpoint_location_test.go b/openstack/endpoint_location_test.go
deleted file mode 100644
index 8e65918..0000000
--- a/openstack/endpoint_location_test.go
+++ /dev/null
@@ -1,228 +0,0 @@
-package openstack
-
-import (
-	"strings"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
-	tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-// Service catalog fixtures take too much vertical space!
-var catalog2 = tokens2.ServiceCatalog{
-	Entries: []tokens2.CatalogEntry{
-		tokens2.CatalogEntry{
-			Type: "same",
-			Name: "same",
-			Endpoints: []tokens2.Endpoint{
-				tokens2.Endpoint{
-					Region:      "same",
-					PublicURL:   "https://public.correct.com/",
-					InternalURL: "https://internal.correct.com/",
-					AdminURL:    "https://admin.correct.com/",
-				},
-				tokens2.Endpoint{
-					Region:    "different",
-					PublicURL: "https://badregion.com/",
-				},
-			},
-		},
-		tokens2.CatalogEntry{
-			Type: "same",
-			Name: "different",
-			Endpoints: []tokens2.Endpoint{
-				tokens2.Endpoint{
-					Region:    "same",
-					PublicURL: "https://badname.com/",
-				},
-				tokens2.Endpoint{
-					Region:    "different",
-					PublicURL: "https://badname.com/+badregion",
-				},
-			},
-		},
-		tokens2.CatalogEntry{
-			Type: "different",
-			Name: "different",
-			Endpoints: []tokens2.Endpoint{
-				tokens2.Endpoint{
-					Region:    "same",
-					PublicURL: "https://badtype.com/+badname",
-				},
-				tokens2.Endpoint{
-					Region:    "different",
-					PublicURL: "https://badtype.com/+badregion+badname",
-				},
-			},
-		},
-	},
-}
-
-func TestV2EndpointExact(t *testing.T) {
-	expectedURLs := map[gophercloud.Availability]string{
-		gophercloud.AvailabilityPublic:   "https://public.correct.com/",
-		gophercloud.AvailabilityAdmin:    "https://admin.correct.com/",
-		gophercloud.AvailabilityInternal: "https://internal.correct.com/",
-	}
-
-	for availability, expected := range expectedURLs {
-		actual, err := V2EndpointURL(&catalog2, gophercloud.EndpointOpts{
-			Type:         "same",
-			Name:         "same",
-			Region:       "same",
-			Availability: availability,
-		})
-		th.AssertNoErr(t, err)
-		th.CheckEquals(t, expected, actual)
-	}
-}
-
-func TestV2EndpointNone(t *testing.T) {
-	_, err := V2EndpointURL(&catalog2, gophercloud.EndpointOpts{
-		Type:         "nope",
-		Availability: gophercloud.AvailabilityPublic,
-	})
-	th.CheckEquals(t, gophercloud.ErrEndpointNotFound, err)
-}
-
-func TestV2EndpointMultiple(t *testing.T) {
-	_, err := V2EndpointURL(&catalog2, gophercloud.EndpointOpts{
-		Type:         "same",
-		Region:       "same",
-		Availability: gophercloud.AvailabilityPublic,
-	})
-	if !strings.HasPrefix(err.Error(), "Discovered 2 matching endpoints:") {
-		t.Errorf("Received unexpected error: %v", err)
-	}
-}
-
-func TestV2EndpointBadAvailability(t *testing.T) {
-	_, err := V2EndpointURL(&catalog2, gophercloud.EndpointOpts{
-		Type:         "same",
-		Name:         "same",
-		Region:       "same",
-		Availability: "wat",
-	})
-	th.CheckEquals(t, "Unexpected availability in endpoint query: wat", err.Error())
-}
-
-var catalog3 = tokens3.ServiceCatalog{
-	Entries: []tokens3.CatalogEntry{
-		tokens3.CatalogEntry{
-			Type: "same",
-			Name: "same",
-			Endpoints: []tokens3.Endpoint{
-				tokens3.Endpoint{
-					ID:        "1",
-					Region:    "same",
-					Interface: "public",
-					URL:       "https://public.correct.com/",
-				},
-				tokens3.Endpoint{
-					ID:        "2",
-					Region:    "same",
-					Interface: "admin",
-					URL:       "https://admin.correct.com/",
-				},
-				tokens3.Endpoint{
-					ID:        "3",
-					Region:    "same",
-					Interface: "internal",
-					URL:       "https://internal.correct.com/",
-				},
-				tokens3.Endpoint{
-					ID:        "4",
-					Region:    "different",
-					Interface: "public",
-					URL:       "https://badregion.com/",
-				},
-			},
-		},
-		tokens3.CatalogEntry{
-			Type: "same",
-			Name: "different",
-			Endpoints: []tokens3.Endpoint{
-				tokens3.Endpoint{
-					ID:        "5",
-					Region:    "same",
-					Interface: "public",
-					URL:       "https://badname.com/",
-				},
-				tokens3.Endpoint{
-					ID:        "6",
-					Region:    "different",
-					Interface: "public",
-					URL:       "https://badname.com/+badregion",
-				},
-			},
-		},
-		tokens3.CatalogEntry{
-			Type: "different",
-			Name: "different",
-			Endpoints: []tokens3.Endpoint{
-				tokens3.Endpoint{
-					ID:        "7",
-					Region:    "same",
-					Interface: "public",
-					URL:       "https://badtype.com/+badname",
-				},
-				tokens3.Endpoint{
-					ID:        "8",
-					Region:    "different",
-					Interface: "public",
-					URL:       "https://badtype.com/+badregion+badname",
-				},
-			},
-		},
-	},
-}
-
-func TestV3EndpointExact(t *testing.T) {
-	expectedURLs := map[gophercloud.Availability]string{
-		gophercloud.AvailabilityPublic:   "https://public.correct.com/",
-		gophercloud.AvailabilityAdmin:    "https://admin.correct.com/",
-		gophercloud.AvailabilityInternal: "https://internal.correct.com/",
-	}
-
-	for availability, expected := range expectedURLs {
-		actual, err := V3EndpointURL(&catalog3, gophercloud.EndpointOpts{
-			Type:         "same",
-			Name:         "same",
-			Region:       "same",
-			Availability: availability,
-		})
-		th.AssertNoErr(t, err)
-		th.CheckEquals(t, expected, actual)
-	}
-}
-
-func TestV3EndpointNone(t *testing.T) {
-	_, err := V3EndpointURL(&catalog3, gophercloud.EndpointOpts{
-		Type:         "nope",
-		Availability: gophercloud.AvailabilityPublic,
-	})
-	th.CheckEquals(t, gophercloud.ErrEndpointNotFound, err)
-}
-
-func TestV3EndpointMultiple(t *testing.T) {
-	_, err := V3EndpointURL(&catalog3, gophercloud.EndpointOpts{
-		Type:         "same",
-		Region:       "same",
-		Availability: gophercloud.AvailabilityPublic,
-	})
-	if !strings.HasPrefix(err.Error(), "Discovered 2 matching endpoints:") {
-		t.Errorf("Received unexpected error: %v", err)
-	}
-}
-
-func TestV3EndpointBadAvailability(t *testing.T) {
-	_, err := V3EndpointURL(&catalog3, gophercloud.EndpointOpts{
-		Type:         "same",
-		Name:         "same",
-		Region:       "same",
-		Availability: "wat",
-	})
-	th.CheckEquals(t, "Unexpected availability in endpoint query: wat", err.Error())
-}
diff --git a/openstack/errors.go b/openstack/errors.go
new file mode 100644
index 0000000..df410b1
--- /dev/null
+++ b/openstack/errors.go
@@ -0,0 +1,71 @@
+package openstack
+
+import (
+	"fmt"
+
+	"github.com/gophercloud/gophercloud"
+	tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
+	tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
+)
+
+// ErrEndpointNotFound is the error when no suitable endpoint can be found
+// in the user's catalog
+type ErrEndpointNotFound struct{ gophercloud.BaseError }
+
+func (e ErrEndpointNotFound) Error() string {
+	return "No suitable endpoint could be found in the service catalog."
+}
+
+// ErrInvalidAvailabilityProvided is the error when an invalid endpoint
+// availability is provided
+type ErrInvalidAvailabilityProvided struct{ gophercloud.ErrInvalidInput }
+
+func (e ErrInvalidAvailabilityProvided) Error() string {
+	return fmt.Sprintf("Unexpected availability in endpoint query: %s", e.Value)
+}
+
+// ErrMultipleMatchingEndpointsV2 is the error when more than one endpoint
+// for the given options is found in the v2 catalog
+type ErrMultipleMatchingEndpointsV2 struct {
+	gophercloud.BaseError
+	Endpoints []tokens2.Endpoint
+}
+
+func (e ErrMultipleMatchingEndpointsV2) Error() string {
+	return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints)
+}
+
+// ErrMultipleMatchingEndpointsV3 is the error when more than one endpoint
+// for the given options is found in the v3 catalog
+type ErrMultipleMatchingEndpointsV3 struct {
+	gophercloud.BaseError
+	Endpoints []tokens3.Endpoint
+}
+
+func (e ErrMultipleMatchingEndpointsV3) Error() string {
+	return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints)
+}
+
+// ErrNoAuthURL is the error when the OS_AUTH_URL environment variable is not
+// found
+type ErrNoAuthURL struct{ gophercloud.ErrInvalidInput }
+
+func (e ErrNoAuthURL) Error() string {
+	return "Environment variable OS_AUTH_URL needs to be set."
+}
+
+// ErrNoUsername is the error when the OS_USERNAME environment variable is not
+// found
+type ErrNoUsername struct{ gophercloud.ErrInvalidInput }
+
+func (e ErrNoUsername) Error() string {
+	return "Environment variable OS_USERNAME needs to be set."
+}
+
+// ErrNoPassword is the error when the OS_PASSWORD environment variable is not
+// found
+type ErrNoPassword struct{ gophercloud.ErrInvalidInput }
+
+func (e ErrNoPassword) Error() string {
+	return "Environment variable OS_PASSWORD needs to be set."
+}
diff --git a/openstack/identity/v2/extensions/admin/roles/fixtures.go b/openstack/identity/v2/extensions/admin/roles/fixtures.go
deleted file mode 100644
index 88bfecc..0000000
--- a/openstack/identity/v2/extensions/admin/roles/fixtures.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// +build fixtures
-
-package roles
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func MockListRoleResponse(t *testing.T) {
-	th.Mux.HandleFunc("/OS-KSADM/roles", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "roles": [
-        {
-            "id": "123",
-            "name": "compute:admin",
-            "description": "Nova Administrator"
-        }
-    ]
-}
-  `)
-	})
-}
-
-func MockAddUserRoleResponse(t *testing.T) {
-	th.Mux.HandleFunc("/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusCreated)
-	})
-}
-
-func MockDeleteUserRoleResponse(t *testing.T) {
-	th.Mux.HandleFunc("/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
diff --git a/openstack/identity/v2/extensions/admin/roles/requests.go b/openstack/identity/v2/extensions/admin/roles/requests.go
index 9a33314..4d27972 100644
--- a/openstack/identity/v2/extensions/admin/roles/requests.go
+++ b/openstack/identity/v2/extensions/admin/roles/requests.go
@@ -1,33 +1,30 @@
 package roles
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // List is the operation responsible for listing all available global roles
 // that a user can adopt.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, rootURL(client), func(r pagination.PageResult) pagination.Page {
 		return RolePage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, rootURL(client), createPage)
+	})
 }
 
-// AddUserRole is the operation responsible for assigning a particular role to
+// AddUser is the operation responsible for assigning a particular role to
 // a user. This is confined to the scope of the user's tenant - so the tenant
 // ID is a required argument.
-func AddUserRole(client *gophercloud.ServiceClient, tenantID, userID, roleID string) UserRoleResult {
-	var result UserRoleResult
-	_, result.Err = client.Put(userRoleURL(client, tenantID, userID, roleID), nil, nil, nil)
-	return result
+func AddUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) {
+	_, r.Err = client.Put(userRoleURL(client, tenantID, userID, roleID), nil, nil, nil)
+	return
 }
 
-// DeleteUserRole is the operation responsible for deleting a particular role
+// DeleteUser is the operation responsible for deleting a particular role
 // from a user. This is confined to the scope of the user's tenant - so the
 // tenant ID is a required argument.
-func DeleteUserRole(client *gophercloud.ServiceClient, tenantID, userID, roleID string) UserRoleResult {
-	var result UserRoleResult
-	_, result.Err = client.Delete(userRoleURL(client, tenantID, userID, roleID), nil)
-	return result
+func DeleteUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) {
+	_, r.Err = client.Delete(userRoleURL(client, tenantID, userID, roleID), nil)
+	return
 }
diff --git a/openstack/identity/v2/extensions/admin/roles/requests_test.go b/openstack/identity/v2/extensions/admin/roles/requests_test.go
deleted file mode 100644
index 7bfeea4..0000000
--- a/openstack/identity/v2/extensions/admin/roles/requests_test.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package roles
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestRole(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockListRoleResponse(t)
-
-	count := 0
-
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractRoles(page)
-		if err != nil {
-			t.Errorf("Failed to extract users: %v", err)
-			return false, err
-		}
-
-		expected := []Role{
-			Role{
-				ID:          "123",
-				Name:        "compute:admin",
-				Description: "Nova Administrator",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestAddUserRole(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockAddUserRoleResponse(t)
-
-	err := AddUserRole(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr()
-
-	th.AssertNoErr(t, err)
-}
-
-func TestDeleteUserRole(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockDeleteUserRoleResponse(t)
-
-	err := DeleteUserRole(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr()
-
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/identity/v2/extensions/admin/roles/results.go b/openstack/identity/v2/extensions/admin/roles/results.go
index ebb3aa5..28de6bb 100644
--- a/openstack/identity/v2/extensions/admin/roles/results.go
+++ b/openstack/identity/v2/extensions/admin/roles/results.go
@@ -1,9 +1,8 @@
 package roles
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Role represents an API role resource.
@@ -27,23 +26,18 @@
 }
 
 // IsEmpty determines whether or not a page of Tenants contains any results.
-func (page RolePage) IsEmpty() (bool, error) {
-	users, err := ExtractRoles(page)
-	if err != nil {
-		return false, err
-	}
-	return len(users) == 0, nil
+func (r RolePage) IsEmpty() (bool, error) {
+	users, err := ExtractRoles(r)
+	return len(users) == 0, err
 }
 
 // ExtractRoles returns a slice of roles contained in a single page of results.
-func ExtractRoles(page pagination.Page) ([]Role, error) {
-	casted := page.(RolePage).Body
-	var response struct {
-		Roles []Role `mapstructure:"roles"`
+func ExtractRoles(r pagination.Page) ([]Role, error) {
+	var s struct {
+		Roles []Role `json:"roles"`
 	}
-
-	err := mapstructure.Decode(casted, &response)
-	return response.Roles, err
+	err := (r.(RolePage)).ExtractInto(&s)
+	return s.Roles, err
 }
 
 // UserRoleResult represents the result of either an AddUserRole or
diff --git a/openstack/identity/v2/extensions/admin/roles/testing/doc.go b/openstack/identity/v2/extensions/admin/roles/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/identity/v2/extensions/admin/roles/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/identity/v2/extensions/admin/roles/testing/fixtures.go b/openstack/identity/v2/extensions/admin/roles/testing/fixtures.go
new file mode 100644
index 0000000..498c161
--- /dev/null
+++ b/openstack/identity/v2/extensions/admin/roles/testing/fixtures.go
@@ -0,0 +1,48 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func MockListRoleResponse(t *testing.T) {
+	th.Mux.HandleFunc("/OS-KSADM/roles", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "roles": [
+        {
+            "id": "123",
+            "name": "compute:admin",
+            "description": "Nova Administrator"
+        }
+    ]
+}
+  `)
+	})
+}
+
+func MockAddUserRoleResponse(t *testing.T) {
+	th.Mux.HandleFunc("/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusCreated)
+	})
+}
+
+func MockDeleteUserRoleResponse(t *testing.T) {
+	th.Mux.HandleFunc("/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
diff --git a/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go b/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go
new file mode 100644
index 0000000..8cf5395
--- /dev/null
+++ b/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go
@@ -0,0 +1,65 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/extensions/admin/roles"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestRole(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockListRoleResponse(t)
+
+	count := 0
+
+	err := roles.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := roles.ExtractRoles(page)
+		if err != nil {
+			t.Errorf("Failed to extract users: %v", err)
+			return false, err
+		}
+
+		expected := []roles.Role{
+			{
+				ID:          "123",
+				Name:        "compute:admin",
+				Description: "Nova Administrator",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, count)
+}
+
+func TestAddUser(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockAddUserRoleResponse(t)
+
+	err := roles.AddUser(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr()
+
+	th.AssertNoErr(t, err)
+}
+
+func TestDeleteUser(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockDeleteUserRoleResponse(t)
+
+	err := roles.DeleteUser(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr()
+
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/identity/v2/extensions/admin/roles/urls.go b/openstack/identity/v2/extensions/admin/roles/urls.go
index 61b3155..e4661e8 100644
--- a/openstack/identity/v2/extensions/admin/roles/urls.go
+++ b/openstack/identity/v2/extensions/admin/roles/urls.go
@@ -1,6 +1,6 @@
 package roles
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	ExtPath  = "OS-KSADM"
diff --git a/openstack/identity/v2/extensions/delegate.go b/openstack/identity/v2/extensions/delegate.go
index fd6e80e..cf6cc81 100644
--- a/openstack/identity/v2/extensions/delegate.go
+++ b/openstack/identity/v2/extensions/delegate.go
@@ -1,10 +1,9 @@
 package extensions
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	common "github.com/rackspace/gophercloud/openstack/common/extensions"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	common "github.com/gophercloud/gophercloud/openstack/common/extensions"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ExtensionPage is a single page of Extension results.
@@ -15,25 +14,20 @@
 // IsEmpty returns true if the current page contains at least one Extension.
 func (page ExtensionPage) IsEmpty() (bool, error) {
 	is, err := ExtractExtensions(page)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractExtensions accepts a Page struct, specifically an ExtensionPage struct, and extracts the
 // elements into a slice of Extension structs.
 func ExtractExtensions(page pagination.Page) ([]common.Extension, error) {
 	// Identity v2 adds an intermediate "values" object.
-
-	var resp struct {
+	var s struct {
 		Extensions struct {
-			Values []common.Extension `mapstructure:"values"`
-		} `mapstructure:"extensions"`
+			Values []common.Extension `json:"values"`
+		} `json:"extensions"`
 	}
-
-	err := mapstructure.Decode(page.(ExtensionPage).Body, &resp)
-	return resp.Extensions.Values, err
+	err := page.(ExtensionPage).ExtractInto(&s)
+	return s.Extensions.Values, err
 }
 
 // Get retrieves information for a specific extension using its alias.
diff --git a/openstack/identity/v2/extensions/delegate_test.go b/openstack/identity/v2/extensions/delegate_test.go
deleted file mode 100644
index 504118a..0000000
--- a/openstack/identity/v2/extensions/delegate_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package extensions
-
-import (
-	"testing"
-
-	common "github.com/rackspace/gophercloud/openstack/common/extensions"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListExtensionsSuccessfully(t)
-
-	count := 0
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractExtensions(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, common.ExpectedExtensions, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	common.HandleGetExtensionSuccessfully(t)
-
-	actual, err := Get(client.ServiceClient(), "agent").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, common.SingleExtension, actual)
-}
diff --git a/openstack/identity/v2/extensions/fixtures.go b/openstack/identity/v2/extensions/fixtures.go
deleted file mode 100644
index 96cb7d2..0000000
--- a/openstack/identity/v2/extensions/fixtures.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// +build fixtures
-
-package extensions
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ListOutput provides a single Extension result. It differs from the delegated implementation
-// by the introduction of an intermediate "values" member.
-const ListOutput = `
-{
-	"extensions": {
-		"values": [
-			{
-				"updated": "2013-01-20T00:00:00-00:00",
-				"name": "Neutron Service Type Management",
-				"links": [],
-				"namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
-				"alias": "service-type",
-				"description": "API for retrieving service providers for Neutron advanced services"
-			}
-		]
-	}
-}
-`
-
-// HandleListExtensionsSuccessfully creates an HTTP handler that returns ListOutput for a List
-// call.
-func HandleListExtensionsSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/extensions", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-
-		fmt.Fprintf(w, `
-{
-  "extensions": {
-    "values": [
-      {
-        "updated": "2013-01-20T00:00:00-00:00",
-        "name": "Neutron Service Type Management",
-        "links": [],
-        "namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
-        "alias": "service-type",
-        "description": "API for retrieving service providers for Neutron advanced services"
-      }
-    ]
-  }
-}
-    `)
-	})
-
-}
diff --git a/openstack/identity/v2/extensions/testing/delegate_test.go b/openstack/identity/v2/extensions/testing/delegate_test.go
new file mode 100644
index 0000000..e7869d8
--- /dev/null
+++ b/openstack/identity/v2/extensions/testing/delegate_test.go
@@ -0,0 +1,39 @@
+package testing
+
+import (
+	"testing"
+
+	common "github.com/gophercloud/gophercloud/openstack/common/extensions/testing"
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/extensions"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListExtensionsSuccessfully(t)
+
+	count := 0
+	err := extensions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := extensions.ExtractExtensions(page)
+		th.AssertNoErr(t, err)
+		th.CheckDeepEquals(t, common.ExpectedExtensions, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, 1, count)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	common.HandleGetExtensionSuccessfully(t)
+
+	actual, err := extensions.Get(client.ServiceClient(), "agent").Extract()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, common.SingleExtension, actual)
+}
diff --git a/openstack/identity/v2/extensions/testing/doc.go b/openstack/identity/v2/extensions/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/identity/v2/extensions/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/identity/v2/extensions/testing/fixtures.go b/openstack/identity/v2/extensions/testing/fixtures.go
new file mode 100644
index 0000000..60afb74
--- /dev/null
+++ b/openstack/identity/v2/extensions/testing/fixtures.go
@@ -0,0 +1,58 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ListOutput provides a single Extension result. It differs from the delegated implementation
+// by the introduction of an intermediate "values" member.
+const ListOutput = `
+{
+	"extensions": {
+		"values": [
+			{
+				"updated": "2013-01-20T00:00:00-00:00",
+				"name": "Neutron Service Type Management",
+				"links": [],
+				"namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
+				"alias": "service-type",
+				"description": "API for retrieving service providers for Neutron advanced services"
+			}
+		]
+	}
+}
+`
+
+// HandleListExtensionsSuccessfully creates an HTTP handler that returns ListOutput for a List
+// call.
+func HandleListExtensionsSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/extensions", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+
+		fmt.Fprintf(w, `
+{
+  "extensions": {
+    "values": [
+      {
+        "updated": "2013-01-20T00:00:00-00:00",
+        "name": "Neutron Service Type Management",
+        "links": [],
+        "namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
+        "alias": "service-type",
+        "description": "API for retrieving service providers for Neutron advanced services"
+      }
+    ]
+  }
+}
+    `)
+	})
+
+}
diff --git a/openstack/identity/v2/tenants/fixtures.go b/openstack/identity/v2/tenants/fixtures.go
deleted file mode 100644
index 7f044ac..0000000
--- a/openstack/identity/v2/tenants/fixtures.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// +build fixtures
-
-package tenants
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ListOutput provides a single page of Tenant results.
-const ListOutput = `
-{
-	"tenants": [
-		{
-			"id": "1234",
-			"name": "Red Team",
-			"description": "The team that is red",
-			"enabled": true
-		},
-		{
-			"id": "9876",
-			"name": "Blue Team",
-			"description": "The team that is blue",
-			"enabled": false
-		}
-	]
-}
-`
-
-// RedTeam is a Tenant fixture.
-var RedTeam = Tenant{
-	ID:          "1234",
-	Name:        "Red Team",
-	Description: "The team that is red",
-	Enabled:     true,
-}
-
-// BlueTeam is a Tenant fixture.
-var BlueTeam = Tenant{
-	ID:          "9876",
-	Name:        "Blue Team",
-	Description: "The team that is blue",
-	Enabled:     false,
-}
-
-// ExpectedTenantSlice is the slice of tenants expected to be returned from ListOutput.
-var ExpectedTenantSlice = []Tenant{RedTeam, BlueTeam}
-
-// HandleListTenantsSuccessfully creates an HTTP handler at `/tenants` on the test handler mux that
-// responds with a list of two tenants.
-func HandleListTenantsSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/tenants", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, ListOutput)
-	})
-}
diff --git a/openstack/identity/v2/tenants/requests.go b/openstack/identity/v2/tenants/requests.go
index 5a359f5..b9d7de6 100644
--- a/openstack/identity/v2/tenants/requests.go
+++ b/openstack/identity/v2/tenants/requests.go
@@ -1,25 +1,20 @@
 package tenants
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOpts filters the Tenants that are returned by the List call.
 type ListOpts struct {
 	// Marker is the ID of the last Tenant on the previous page.
 	Marker string `q:"marker"`
-
 	// Limit specifies the page size.
 	Limit int `q:"limit"`
 }
 
 // List enumerates the Tenants to which the current token has access.
 func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return TenantPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
 	url := listURL(client)
 	if opts != nil {
 		q, err := gophercloud.BuildQueryString(opts)
@@ -28,6 +23,7 @@
 		}
 		url += q.String()
 	}
-
-	return pagination.NewPager(client, url, createPage)
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
+		return TenantPage{pagination.LinkedPageBase{PageResult: r}}
+	})
 }
diff --git a/openstack/identity/v2/tenants/requests_test.go b/openstack/identity/v2/tenants/requests_test.go
deleted file mode 100644
index e8f172d..0000000
--- a/openstack/identity/v2/tenants/requests_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package tenants
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListTenants(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListTenantsSuccessfully(t)
-
-	count := 0
-	err := List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-
-		actual, err := ExtractTenants(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, ExpectedTenantSlice, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
diff --git a/openstack/identity/v2/tenants/results.go b/openstack/identity/v2/tenants/results.go
index c1220c3..3ce1e67 100644
--- a/openstack/identity/v2/tenants/results.go
+++ b/openstack/identity/v2/tenants/results.go
@@ -1,24 +1,23 @@
 package tenants
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Tenant is a grouping of users in the identity service.
 type Tenant struct {
 	// ID is a unique identifier for this tenant.
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Name is a friendlier user-facing name for this tenant.
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 
 	// Description is a human-readable explanation of this Tenant's purpose.
-	Description string `mapstructure:"description"`
+	Description string `json:"description"`
 
 	// Enabled indicates whether or not a tenant is active.
-	Enabled bool `mapstructure:"enabled"`
+	Enabled bool `json:"enabled"`
 }
 
 // TenantPage is a single page of Tenant results.
@@ -27,36 +26,28 @@
 }
 
 // IsEmpty determines whether or not a page of Tenants contains any results.
-func (page TenantPage) IsEmpty() (bool, error) {
-	tenants, err := ExtractTenants(page)
-	if err != nil {
-		return false, err
-	}
-	return len(tenants) == 0, nil
+func (r TenantPage) IsEmpty() (bool, error) {
+	tenants, err := ExtractTenants(r)
+	return len(tenants) == 0, err
 }
 
 // NextPageURL extracts the "next" link from the tenants_links section of the result.
-func (page TenantPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"tenants_links"`
+func (r TenantPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"tenants_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(page.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // ExtractTenants returns a slice of Tenants contained in a single page of results.
-func ExtractTenants(page pagination.Page) ([]Tenant, error) {
-	casted := page.(TenantPage).Body
-	var response struct {
-		Tenants []Tenant `mapstructure:"tenants"`
+func ExtractTenants(r pagination.Page) ([]Tenant, error) {
+	var s struct {
+		Tenants []Tenant `json:"tenants"`
 	}
-
-	err := mapstructure.Decode(casted, &response)
-	return response.Tenants, err
+	err := (r.(TenantPage)).ExtractInto(&s)
+	return s.Tenants, err
 }
diff --git a/openstack/identity/v2/tenants/testing/doc.go b/openstack/identity/v2/tenants/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/identity/v2/tenants/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/identity/v2/tenants/testing/fixtures.go b/openstack/identity/v2/tenants/testing/fixtures.go
new file mode 100644
index 0000000..7ddba45
--- /dev/null
+++ b/openstack/identity/v2/tenants/testing/fixtures.go
@@ -0,0 +1,64 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ListOutput provides a single page of Tenant results.
+const ListOutput = `
+{
+	"tenants": [
+		{
+			"id": "1234",
+			"name": "Red Team",
+			"description": "The team that is red",
+			"enabled": true
+		},
+		{
+			"id": "9876",
+			"name": "Blue Team",
+			"description": "The team that is blue",
+			"enabled": false
+		}
+	]
+}
+`
+
+// RedTeam is a Tenant fixture.
+var RedTeam = tenants.Tenant{
+	ID:          "1234",
+	Name:        "Red Team",
+	Description: "The team that is red",
+	Enabled:     true,
+}
+
+// BlueTeam is a Tenant fixture.
+var BlueTeam = tenants.Tenant{
+	ID:          "9876",
+	Name:        "Blue Team",
+	Description: "The team that is blue",
+	Enabled:     false,
+}
+
+// ExpectedTenantSlice is the slice of tenants expected to be returned from ListOutput.
+var ExpectedTenantSlice = []tenants.Tenant{RedTeam, BlueTeam}
+
+// HandleListTenantsSuccessfully creates an HTTP handler at `/tenants` on the test handler mux that
+// responds with a list of two tenants.
+func HandleListTenantsSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/tenants", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, ListOutput)
+	})
+}
diff --git a/openstack/identity/v2/tenants/testing/requests_test.go b/openstack/identity/v2/tenants/testing/requests_test.go
new file mode 100644
index 0000000..2a9b71c
--- /dev/null
+++ b/openstack/identity/v2/tenants/testing/requests_test.go
@@ -0,0 +1,30 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestListTenants(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListTenantsSuccessfully(t)
+
+	count := 0
+	err := tenants.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+
+		actual, err := tenants.ExtractTenants(page)
+		th.AssertNoErr(t, err)
+
+		th.CheckDeepEquals(t, ExpectedTenantSlice, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, count, 1)
+}
diff --git a/openstack/identity/v2/tenants/urls.go b/openstack/identity/v2/tenants/urls.go
index 1dd6ce0..101599b 100644
--- a/openstack/identity/v2/tenants/urls.go
+++ b/openstack/identity/v2/tenants/urls.go
@@ -1,6 +1,6 @@
 package tenants
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func listURL(client *gophercloud.ServiceClient) string {
 	return client.ServiceURL("tenants")
diff --git a/openstack/identity/v2/tokens/errors.go b/openstack/identity/v2/tokens/errors.go
deleted file mode 100644
index 3dfdc08..0000000
--- a/openstack/identity/v2/tokens/errors.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package tokens
-
-import (
-	"errors"
-	"fmt"
-)
-
-var (
-	// ErrUserIDProvided is returned if you attempt to authenticate with a UserID.
-	ErrUserIDProvided = unacceptedAttributeErr("UserID")
-
-	// ErrAPIKeyProvided is returned if you attempt to authenticate with an APIKey.
-	ErrAPIKeyProvided = unacceptedAttributeErr("APIKey")
-
-	// ErrDomainIDProvided is returned if you attempt to authenticate with a DomainID.
-	ErrDomainIDProvided = unacceptedAttributeErr("DomainID")
-
-	// ErrDomainNameProvided is returned if you attempt to authenticate with a DomainName.
-	ErrDomainNameProvided = unacceptedAttributeErr("DomainName")
-
-	// ErrUsernameRequired is returned if you attempt to authenticate without a Username.
-	ErrUsernameRequired = errors.New("You must supply a Username in your AuthOptions.")
-
-	// ErrPasswordRequired is returned if you don't provide a password.
-	ErrPasswordRequired = errors.New("Please supply a Password in your AuthOptions.")
-)
-
-func unacceptedAttributeErr(attribute string) error {
-	return fmt.Errorf("The base Identity V2 API does not accept authentication by %s", attribute)
-}
diff --git a/openstack/identity/v2/tokens/fixtures.go b/openstack/identity/v2/tokens/fixtures.go
deleted file mode 100644
index 6245259..0000000
--- a/openstack/identity/v2/tokens/fixtures.go
+++ /dev/null
@@ -1,195 +0,0 @@
-// +build fixtures
-
-package tokens
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
-	th "github.com/rackspace/gophercloud/testhelper"
-	thclient "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ExpectedToken is the token that should be parsed from TokenCreationResponse.
-var ExpectedToken = &Token{
-	ID:        "aaaabbbbccccdddd",
-	ExpiresAt: time.Date(2014, time.January, 31, 15, 30, 58, 0, time.UTC),
-	Tenant: tenants.Tenant{
-		ID:          "fc394f2ab2df4114bde39905f800dc57",
-		Name:        "test",
-		Description: "There are many tenants. This one is yours.",
-		Enabled:     true,
-	},
-}
-
-// ExpectedServiceCatalog is the service catalog that should be parsed from TokenCreationResponse.
-var ExpectedServiceCatalog = &ServiceCatalog{
-	Entries: []CatalogEntry{
-		CatalogEntry{
-			Name: "inscrutablewalrus",
-			Type: "something",
-			Endpoints: []Endpoint{
-				Endpoint{
-					PublicURL: "http://something0:1234/v2/",
-					Region:    "region0",
-				},
-				Endpoint{
-					PublicURL: "http://something1:1234/v2/",
-					Region:    "region1",
-				},
-			},
-		},
-		CatalogEntry{
-			Name: "arbitrarypenguin",
-			Type: "else",
-			Endpoints: []Endpoint{
-				Endpoint{
-					PublicURL: "http://else0:4321/v3/",
-					Region:    "region0",
-				},
-			},
-		},
-	},
-}
-
-// ExpectedUser is the token that should be parsed from TokenGetResponse.
-var ExpectedUser = &User{
-	ID:       "a530fefc3d594c4ba2693a4ecd6be74e",
-	Name:     "apiserver",
-	Roles:    []Role{{"member"}, {"service"}},
-	UserName: "apiserver",
-}
-
-// TokenCreationResponse is a JSON response that contains ExpectedToken and ExpectedServiceCatalog.
-const TokenCreationResponse = `
-{
-	"access": {
-		"token": {
-			"issued_at": "2014-01-30T15:30:58.000000Z",
-			"expires": "2014-01-31T15:30:58Z",
-			"id": "aaaabbbbccccdddd",
-			"tenant": {
-				"description": "There are many tenants. This one is yours.",
-				"enabled": true,
-				"id": "fc394f2ab2df4114bde39905f800dc57",
-				"name": "test"
-			}
-		},
-		"serviceCatalog": [
-			{
-				"endpoints": [
-					{
-						"publicURL": "http://something0:1234/v2/",
-						"region": "region0"
-					},
-					{
-						"publicURL": "http://something1:1234/v2/",
-						"region": "region1"
-					}
-				],
-				"type": "something",
-				"name": "inscrutablewalrus"
-			},
-			{
-				"endpoints": [
-					{
-						"publicURL": "http://else0:4321/v3/",
-						"region": "region0"
-					}
-				],
-				"type": "else",
-				"name": "arbitrarypenguin"
-			}
-		]
-	}
-}
-`
-
-// TokenGetResponse is a JSON response that contains ExpectedToken and ExpectedUser.
-const TokenGetResponse = `
-{
-    "access": {
-		"token": {
-			"issued_at": "2014-01-30T15:30:58.000000Z",
-			"expires": "2014-01-31T15:30:58Z",
-			"id": "aaaabbbbccccdddd",
-			"tenant": {
-				"description": "There are many tenants. This one is yours.",
-				"enabled": true,
-				"id": "fc394f2ab2df4114bde39905f800dc57",
-				"name": "test"
-			}
-		},
-        "serviceCatalog": [], 
-		"user": {
-            "id": "a530fefc3d594c4ba2693a4ecd6be74e", 
-            "name": "apiserver", 
-            "roles": [
-                {
-                    "name": "member"
-                }, 
-                {
-                    "name": "service"
-                }
-            ], 
-            "roles_links": [], 
-            "username": "apiserver"
-        }
-    }
-}`
-
-// HandleTokenPost expects a POST against a /tokens handler, ensures that the request body has been
-// constructed properly given certain auth options, and returns the result.
-func HandleTokenPost(t *testing.T, requestJSON string) {
-	th.Mux.HandleFunc("/tokens", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		if requestJSON != "" {
-			th.TestJSONRequest(t, r, requestJSON)
-		}
-
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, TokenCreationResponse)
-	})
-}
-
-// HandleTokenGet expects a Get against a /tokens handler, ensures that the request body has been
-// constructed properly given certain auth options, and returns the result.
-func HandleTokenGet(t *testing.T, token string) {
-	th.Mux.HandleFunc("/tokens/"+token, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestHeader(t, r, "X-Auth-Token", thclient.TokenID)
-
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, TokenGetResponse)
-	})
-}
-
-// IsSuccessful ensures that a CreateResult was successful and contains the correct token and
-// service catalog.
-func IsSuccessful(t *testing.T, result CreateResult) {
-	token, err := result.ExtractToken()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, ExpectedToken, token)
-
-	serviceCatalog, err := result.ExtractServiceCatalog()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, ExpectedServiceCatalog, serviceCatalog)
-}
-
-// GetIsSuccessful ensures that a GetResult was successful and contains the correct token and
-// User Info.
-func GetIsSuccessful(t *testing.T, result GetResult) {
-	token, err := result.ExtractToken()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, ExpectedToken, token)
-
-	user, err := result.ExtractUser()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, ExpectedUser, user)
-}
diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go
index 1f51438..4983031 100644
--- a/openstack/identity/v2/tokens/requests.go
+++ b/openstack/identity/v2/tokens/requests.go
@@ -1,99 +1,99 @@
 package tokens
 
-import (
-	"fmt"
+import "github.com/gophercloud/gophercloud"
 
-	"github.com/rackspace/gophercloud"
-)
+type PasswordCredentialsV2 struct {
+	Username string `json:"username" required:"true"`
+	Password string `json:"password" required:"true"`
+}
+
+type TokenCredentialsV2 struct {
+	ID string `json:"id,omitempty" required:"true"`
+}
+
+// AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder
+// interface.
+type AuthOptionsV2 struct {
+	PasswordCredentials *PasswordCredentialsV2 `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
+
+	// The TenantID and TenantName fields are optional for the Identity V2 API.
+	// Some providers allow you to specify a TenantName instead of the TenantId.
+	// Some require both. Your provider's authentication policies will determine
+	// how these fields influence authentication.
+	TenantID   string `json:"tenantId,omitempty"`
+	TenantName string `json:"tenantName,omitempty"`
+
+	// TokenCredentials allows users to authenticate (possibly as another user) with an
+	// authentication token ID.
+	TokenCredentials *TokenCredentialsV2 `json:"token,omitempty" xor:"PasswordCredentials"`
+}
 
 // AuthOptionsBuilder describes any argument that may be passed to the Create call.
 type AuthOptionsBuilder interface {
-
 	// ToTokenCreateMap assembles the Create request body, returning an error if parameters are
 	// missing or inconsistent.
-	ToTokenCreateMap() (map[string]interface{}, error)
+	ToTokenV2CreateMap() (map[string]interface{}, error)
 }
 
-// AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder
-// interface.
+// AuthOptions are the valid options for Openstack Identity v2 authentication.
+// For field descriptions, see gophercloud.AuthOptions.
 type AuthOptions struct {
-	gophercloud.AuthOptions
+	IdentityEndpoint string `json:"-"`
+	Username         string `json:"username,omitempty"`
+	Password         string `json:"password,omitempty"`
+	TenantID         string `json:"tenantId,omitempty"`
+	TenantName       string `json:"tenantName,omitempty"`
+	AllowReauth      bool   `json:"-"`
+	TokenID          string
 }
 
-// WrapOptions embeds a root AuthOptions struct in a package-specific one.
-func WrapOptions(original gophercloud.AuthOptions) AuthOptions {
-	return AuthOptions{AuthOptions: original}
-}
-
-// ToTokenCreateMap converts AuthOptions into nested maps that can be serialized into a JSON
-// request.
-func (auth AuthOptions) ToTokenCreateMap() (map[string]interface{}, error) {
-	// Error out if an unsupported auth option is present.
-	if auth.UserID != "" {
-		return nil, ErrUserIDProvided
-	}
-	if auth.APIKey != "" {
-		return nil, ErrAPIKeyProvided
-	}
-	if auth.DomainID != "" {
-		return nil, ErrDomainIDProvided
-	}
-	if auth.DomainName != "" {
-		return nil, ErrDomainNameProvided
+// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
+// interface in the v2 tokens package
+func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
+	v2Opts := AuthOptionsV2{
+		TenantID:   opts.TenantID,
+		TenantName: opts.TenantName,
 	}
 
-	// Populate the request map.
-	authMap := make(map[string]interface{})
-
-	if auth.Username != "" {
-		if auth.Password != "" {
-			authMap["passwordCredentials"] = map[string]interface{}{
-				"username": auth.Username,
-				"password": auth.Password,
-			}
-		} else {
-			return nil, ErrPasswordRequired
-		}
-	} else if auth.TokenID != "" {
-		authMap["token"] = map[string]interface{}{
-			"id": auth.TokenID,
+	if opts.Password != "" {
+		v2Opts.PasswordCredentials = &PasswordCredentialsV2{
+			Username: opts.Username,
+			Password: opts.Password,
 		}
 	} else {
-		return nil, fmt.Errorf("You must provide either username/password or tenantID/token values.")
+		v2Opts.TokenCredentials = &TokenCredentialsV2{
+			ID: opts.TokenID,
+		}
 	}
 
-	if auth.TenantID != "" {
-		authMap["tenantId"] = auth.TenantID
+	b, err := gophercloud.BuildRequestBody(v2Opts, "auth")
+	if err != nil {
+		return nil, err
 	}
-	if auth.TenantName != "" {
-		authMap["tenantName"] = auth.TenantName
-	}
-
-	return map[string]interface{}{"auth": authMap}, nil
+	return b, nil
 }
 
 // Create authenticates to the identity service and attempts to acquire a Token.
 // If successful, the CreateResult
 // Generally, rather than interact with this call directly, end users should call openstack.AuthenticatedClient(),
 // which abstracts all of the gory details about navigating service catalogs and such.
-func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) CreateResult {
-	request, err := auth.ToTokenCreateMap()
+func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) {
+	b, err := auth.ToTokenV2CreateMap()
 	if err != nil {
-		return CreateResult{gophercloud.Result{Err: err}}
+		r.Err = err
+		return
 	}
-
-	var result CreateResult
-	_, result.Err = client.Post(CreateURL(client), request, &result.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200, 203},
+	_, r.Err = client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{
+		OkCodes:     []int{200, 203},
+		MoreHeaders: map[string]string{"X-Auth-Token": ""},
 	})
-	return result
+	return
 }
 
-// Validates and retrieves information for user's token.
-func Get(client *gophercloud.ServiceClient, token string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(GetURL(client, token), &result.Body, &gophercloud.RequestOpts{
+// Get validates and retrieves information for user's token.
+func Get(client *gophercloud.ServiceClient, token string) (r GetResult) {
+	_, r.Err = client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 203},
 	})
-	return result
+	return
 }
diff --git a/openstack/identity/v2/tokens/requests_test.go b/openstack/identity/v2/tokens/requests_test.go
deleted file mode 100644
index f1ec339..0000000
--- a/openstack/identity/v2/tokens/requests_test.go
+++ /dev/null
@@ -1,152 +0,0 @@
-package tokens
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func tokenPost(t *testing.T, options gophercloud.AuthOptions, requestJSON string) CreateResult {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleTokenPost(t, requestJSON)
-
-	return Create(client.ServiceClient(), AuthOptions{options})
-}
-
-func tokenPostErr(t *testing.T, options gophercloud.AuthOptions, expectedErr error) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleTokenPost(t, "")
-
-	actualErr := Create(client.ServiceClient(), AuthOptions{options}).Err
-	th.CheckDeepEquals(t, expectedErr, actualErr)
-}
-
-func TestCreateWithPassword(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-		Password: "swordfish",
-	}
-
-	IsSuccessful(t, tokenPost(t, options, `
-    {
-      "auth": {
-        "passwordCredentials": {
-          "username": "me",
-          "password": "swordfish"
-        }
-      }
-    }
-  `))
-}
-
-func TestCreateTokenWithTenantID(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-		Password: "opensesame",
-		TenantID: "fc394f2ab2df4114bde39905f800dc57",
-	}
-
-	IsSuccessful(t, tokenPost(t, options, `
-    {
-      "auth": {
-        "tenantId": "fc394f2ab2df4114bde39905f800dc57",
-        "passwordCredentials": {
-          "username": "me",
-          "password": "opensesame"
-        }
-      }
-    }
-  `))
-}
-
-func TestCreateTokenWithTenantName(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username:   "me",
-		Password:   "opensesame",
-		TenantName: "demo",
-	}
-
-	IsSuccessful(t, tokenPost(t, options, `
-    {
-      "auth": {
-        "tenantName": "demo",
-        "passwordCredentials": {
-          "username": "me",
-          "password": "opensesame"
-        }
-      }
-    }
-  `))
-}
-
-func TestProhibitUserID(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-		UserID:   "1234",
-		Password: "thing",
-	}
-
-	tokenPostErr(t, options, ErrUserIDProvided)
-}
-
-func TestProhibitAPIKey(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-		Password: "thing",
-		APIKey:   "123412341234",
-	}
-
-	tokenPostErr(t, options, ErrAPIKeyProvided)
-}
-
-func TestProhibitDomainID(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-		Password: "thing",
-		DomainID: "1234",
-	}
-
-	tokenPostErr(t, options, ErrDomainIDProvided)
-}
-
-func TestProhibitDomainName(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username:   "me",
-		Password:   "thing",
-		DomainName: "wat",
-	}
-
-	tokenPostErr(t, options, ErrDomainNameProvided)
-}
-
-func TestRequireUsername(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Password: "thing",
-	}
-
-	tokenPostErr(t, options, fmt.Errorf("You must provide either username/password or tenantID/token values."))
-}
-
-func TestRequirePassword(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-	}
-
-	tokenPostErr(t, options, ErrPasswordRequired)
-}
-
-func tokenGet(t *testing.T, tokenId string) GetResult {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleTokenGet(t, tokenId)
-	return Get(client.ServiceClient(), tokenId)
-}
-
-func TestGetWithToken(t *testing.T) {
-	GetIsSuccessful(t, tokenGet(t, "db22caf43c934e6c829087c41ff8d8d6"))
-}
diff --git a/openstack/identity/v2/tokens/results.go b/openstack/identity/v2/tokens/results.go
index 67c577b..93c0554 100644
--- a/openstack/identity/v2/tokens/results.go
+++ b/openstack/identity/v2/tokens/results.go
@@ -3,9 +3,8 @@
 import (
 	"time"
 
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
 )
 
 // Token provides only the most basic information related to an authentication token.
@@ -25,15 +24,17 @@
 	Tenant tenants.Tenant
 }
 
-// Authorization need user info which can get from token authentication's response
+// Role is a role for a user.
 type Role struct {
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 }
+
+// User is an OpenStack user.
 type User struct {
-	ID       string `mapstructure:"id"`
-	Name     string `mapstructure:"name"`
-	UserName string `mapstructure:"username"`
-	Roles    []Role `mapstructure:"roles"`
+	ID       string `json:"id"`
+	Name     string `json:"name"`
+	UserName string `json:"username"`
+	Roles    []Role `json:"roles"`
 }
 
 // Endpoint represents a single API endpoint offered by a service.
@@ -45,14 +46,14 @@
 //
 // In all cases, fields which aren't supported by the provider and service combined will assume a zero-value ("").
 type Endpoint struct {
-	TenantID    string `mapstructure:"tenantId"`
-	PublicURL   string `mapstructure:"publicURL"`
-	InternalURL string `mapstructure:"internalURL"`
-	AdminURL    string `mapstructure:"adminURL"`
-	Region      string `mapstructure:"region"`
-	VersionID   string `mapstructure:"versionId"`
-	VersionInfo string `mapstructure:"versionInfo"`
-	VersionList string `mapstructure:"versionList"`
+	TenantID    string `json:"tenantId"`
+	PublicURL   string `json:"publicURL"`
+	InternalURL string `json:"internalURL"`
+	AdminURL    string `json:"adminURL"`
+	Region      string `json:"region"`
+	VersionID   string `json:"versionId"`
+	VersionInfo string `json:"versionInfo"`
+	VersionList string `json:"versionList"`
 }
 
 // CatalogEntry provides a type-safe interface to an Identity API V2 service catalog listing.
@@ -63,15 +64,15 @@
 // Otherwise, you'll tie the representation of the service to a specific provider.
 type CatalogEntry struct {
 	// Name will contain the provider-specified name for the service.
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 
 	// Type will contain a type string if OpenStack defines a type for the service.
 	// Otherwise, for provider-specific services, the provider may assign their own type strings.
-	Type string `mapstructure:"type"`
+	Type string `json:"type"`
 
 	// Endpoints will let the caller iterate over all the different endpoints that may exist for
 	// the service.
-	Endpoints []Endpoint `mapstructure:"endpoints"`
+	Endpoints []Endpoint `json:"endpoints"`
 }
 
 // ServiceCatalog provides a view into the service catalog from a previous, successful authentication.
@@ -92,56 +93,43 @@
 }
 
 // ExtractToken returns the just-created Token from a CreateResult.
-func (result CreateResult) ExtractToken() (*Token, error) {
-	if result.Err != nil {
-		return nil, result.Err
-	}
-
-	var response struct {
+func (r CreateResult) ExtractToken() (*Token, error) {
+	var s struct {
 		Access struct {
 			Token struct {
-				Expires string         `mapstructure:"expires"`
-				ID      string         `mapstructure:"id"`
-				Tenant  tenants.Tenant `mapstructure:"tenant"`
-			} `mapstructure:"token"`
-		} `mapstructure:"access"`
+				Expires string         `json:"expires"`
+				ID      string         `json:"id"`
+				Tenant  tenants.Tenant `json:"tenant"`
+			} `json:"token"`
+		} `json:"access"`
 	}
 
-	err := mapstructure.Decode(result.Body, &response)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return nil, err
 	}
 
-	expiresTs, err := time.Parse(gophercloud.RFC3339Milli, response.Access.Token.Expires)
+	expiresTs, err := time.Parse(gophercloud.RFC3339Milli, s.Access.Token.Expires)
 	if err != nil {
 		return nil, err
 	}
 
 	return &Token{
-		ID:        response.Access.Token.ID,
+		ID:        s.Access.Token.ID,
 		ExpiresAt: expiresTs,
-		Tenant:    response.Access.Token.Tenant,
+		Tenant:    s.Access.Token.Tenant,
 	}, nil
 }
 
 // ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token.
-func (result CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
-	if result.Err != nil {
-		return nil, result.Err
-	}
-
-	var response struct {
+func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
+	var s struct {
 		Access struct {
-			Entries []CatalogEntry `mapstructure:"serviceCatalog"`
-		} `mapstructure:"access"`
+			Entries []CatalogEntry `json:"serviceCatalog"`
+		} `json:"access"`
 	}
-
-	err := mapstructure.Decode(result.Body, &response)
-	if err != nil {
-		return nil, err
-	}
-
-	return &ServiceCatalog{Entries: response.Access.Entries}, nil
+	err := r.ExtractInto(&s)
+	return &ServiceCatalog{Entries: s.Access.Entries}, err
 }
 
 // createErr quickly packs an error in a CreateResult.
@@ -150,21 +138,12 @@
 }
 
 // ExtractUser returns the User from a GetResult.
-func (result GetResult) ExtractUser() (*User, error) {
-	if result.Err != nil {
-		return nil, result.Err
-	}
-
-	var response struct {
+func (r GetResult) ExtractUser() (*User, error) {
+	var s struct {
 		Access struct {
-			User User `mapstructure:"user"`
-		} `mapstructure:"access"`
+			User User `json:"user"`
+		} `json:"access"`
 	}
-
-	err := mapstructure.Decode(result.Body, &response)
-	if err != nil {
-		return nil, err
-	}
-
-	return &response.Access.User, nil
+	err := r.ExtractInto(&s)
+	return &s.Access.User, err
 }
diff --git a/openstack/identity/v2/tokens/testing/doc.go b/openstack/identity/v2/tokens/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/identity/v2/tokens/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/identity/v2/tokens/testing/fixtures.go b/openstack/identity/v2/tokens/testing/fixtures.go
new file mode 100644
index 0000000..d3a8f24
--- /dev/null
+++ b/openstack/identity/v2/tokens/testing/fixtures.go
@@ -0,0 +1,194 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	thclient "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ExpectedToken is the token that should be parsed from TokenCreationResponse.
+var ExpectedToken = &tokens.Token{
+	ID:        "aaaabbbbccccdddd",
+	ExpiresAt: time.Date(2014, time.January, 31, 15, 30, 58, 0, time.UTC),
+	Tenant: tenants.Tenant{
+		ID:          "fc394f2ab2df4114bde39905f800dc57",
+		Name:        "test",
+		Description: "There are many tenants. This one is yours.",
+		Enabled:     true,
+	},
+}
+
+// ExpectedServiceCatalog is the service catalog that should be parsed from TokenCreationResponse.
+var ExpectedServiceCatalog = &tokens.ServiceCatalog{
+	Entries: []tokens.CatalogEntry{
+		{
+			Name: "inscrutablewalrus",
+			Type: "something",
+			Endpoints: []tokens.Endpoint{
+				{
+					PublicURL: "http://something0:1234/v2/",
+					Region:    "region0",
+				},
+				{
+					PublicURL: "http://something1:1234/v2/",
+					Region:    "region1",
+				},
+			},
+		},
+		{
+			Name: "arbitrarypenguin",
+			Type: "else",
+			Endpoints: []tokens.Endpoint{
+				{
+					PublicURL: "http://else0:4321/v3/",
+					Region:    "region0",
+				},
+			},
+		},
+	},
+}
+
+// ExpectedUser is the token that should be parsed from TokenGetResponse.
+var ExpectedUser = &tokens.User{
+	ID:       "a530fefc3d594c4ba2693a4ecd6be74e",
+	Name:     "apiserver",
+	Roles:    []tokens.Role{{"member"}, {"service"}},
+	UserName: "apiserver",
+}
+
+// TokenCreationResponse is a JSON response that contains ExpectedToken and ExpectedServiceCatalog.
+const TokenCreationResponse = `
+{
+	"access": {
+		"token": {
+			"issued_at": "2014-01-30T15:30:58.000000Z",
+			"expires": "2014-01-31T15:30:58Z",
+			"id": "aaaabbbbccccdddd",
+			"tenant": {
+				"description": "There are many tenants. This one is yours.",
+				"enabled": true,
+				"id": "fc394f2ab2df4114bde39905f800dc57",
+				"name": "test"
+			}
+		},
+		"serviceCatalog": [
+			{
+				"endpoints": [
+					{
+						"publicURL": "http://something0:1234/v2/",
+						"region": "region0"
+					},
+					{
+						"publicURL": "http://something1:1234/v2/",
+						"region": "region1"
+					}
+				],
+				"type": "something",
+				"name": "inscrutablewalrus"
+			},
+			{
+				"endpoints": [
+					{
+						"publicURL": "http://else0:4321/v3/",
+						"region": "region0"
+					}
+				],
+				"type": "else",
+				"name": "arbitrarypenguin"
+			}
+		]
+	}
+}
+`
+
+// TokenGetResponse is a JSON response that contains ExpectedToken and ExpectedUser.
+const TokenGetResponse = `
+{
+    "access": {
+		"token": {
+			"issued_at": "2014-01-30T15:30:58.000000Z",
+			"expires": "2014-01-31T15:30:58Z",
+			"id": "aaaabbbbccccdddd",
+			"tenant": {
+				"description": "There are many tenants. This one is yours.",
+				"enabled": true,
+				"id": "fc394f2ab2df4114bde39905f800dc57",
+				"name": "test"
+			}
+		},
+        "serviceCatalog": [],
+		"user": {
+            "id": "a530fefc3d594c4ba2693a4ecd6be74e",
+            "name": "apiserver",
+            "roles": [
+                {
+                    "name": "member"
+                },
+                {
+                    "name": "service"
+                }
+            ],
+            "roles_links": [],
+            "username": "apiserver"
+        }
+    }
+}`
+
+// HandleTokenPost expects a POST against a /tokens handler, ensures that the request body has been
+// constructed properly given certain auth options, and returns the result.
+func HandleTokenPost(t *testing.T, requestJSON string) {
+	th.Mux.HandleFunc("/tokens", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		if requestJSON != "" {
+			th.TestJSONRequest(t, r, requestJSON)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, TokenCreationResponse)
+	})
+}
+
+// HandleTokenGet expects a Get against a /tokens handler, ensures that the request body has been
+// constructed properly given certain auth options, and returns the result.
+func HandleTokenGet(t *testing.T, token string) {
+	th.Mux.HandleFunc("/tokens/"+token, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "X-Auth-Token", thclient.TokenID)
+
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, TokenGetResponse)
+	})
+}
+
+// IsSuccessful ensures that a CreateResult was successful and contains the correct token and
+// service catalog.
+func IsSuccessful(t *testing.T, result tokens.CreateResult) {
+	token, err := result.ExtractToken()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, ExpectedToken, token)
+
+	serviceCatalog, err := result.ExtractServiceCatalog()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, ExpectedServiceCatalog, serviceCatalog)
+}
+
+// GetIsSuccessful ensures that a GetResult was successful and contains the correct token and
+// User Info.
+func GetIsSuccessful(t *testing.T, result tokens.GetResult) {
+	token, err := result.ExtractToken()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, ExpectedToken, token)
+
+	user, err := result.ExtractUser()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, ExpectedUser, user)
+}
diff --git a/openstack/identity/v2/tokens/testing/requests_test.go b/openstack/identity/v2/tokens/testing/requests_test.go
new file mode 100644
index 0000000..b687a92
--- /dev/null
+++ b/openstack/identity/v2/tokens/testing/requests_test.go
@@ -0,0 +1,104 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func tokenPost(t *testing.T, options gophercloud.AuthOptions, requestJSON string) tokens.CreateResult {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleTokenPost(t, requestJSON)
+
+	return tokens.Create(client.ServiceClient(), options)
+}
+
+func tokenPostErr(t *testing.T, options gophercloud.AuthOptions, expectedErr error) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleTokenPost(t, "")
+
+	actualErr := tokens.Create(client.ServiceClient(), options).Err
+	th.CheckDeepEquals(t, expectedErr, actualErr)
+}
+
+func TestCreateWithPassword(t *testing.T) {
+	options := gophercloud.AuthOptions{
+		Username: "me",
+		Password: "swordfish",
+	}
+
+	IsSuccessful(t, tokenPost(t, options, `
+    {
+      "auth": {
+        "passwordCredentials": {
+          "username": "me",
+          "password": "swordfish"
+        }
+      }
+    }
+  `))
+}
+
+func TestCreateTokenWithTenantID(t *testing.T) {
+	options := gophercloud.AuthOptions{
+		Username: "me",
+		Password: "opensesame",
+		TenantID: "fc394f2ab2df4114bde39905f800dc57",
+	}
+
+	IsSuccessful(t, tokenPost(t, options, `
+    {
+      "auth": {
+        "tenantId": "fc394f2ab2df4114bde39905f800dc57",
+        "passwordCredentials": {
+          "username": "me",
+          "password": "opensesame"
+        }
+      }
+    }
+  `))
+}
+
+func TestCreateTokenWithTenantName(t *testing.T) {
+	options := gophercloud.AuthOptions{
+		Username:   "me",
+		Password:   "opensesame",
+		TenantName: "demo",
+	}
+
+	IsSuccessful(t, tokenPost(t, options, `
+    {
+      "auth": {
+        "tenantName": "demo",
+        "passwordCredentials": {
+          "username": "me",
+          "password": "opensesame"
+        }
+      }
+    }
+  `))
+}
+
+func TestRequireUsername(t *testing.T) {
+	options := gophercloud.AuthOptions{
+		Password: "thing",
+	}
+
+	tokenPostErr(t, options, gophercloud.ErrMissingInput{Argument: "Username"})
+}
+
+func tokenGet(t *testing.T, tokenId string) tokens.GetResult {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleTokenGet(t, tokenId)
+	return tokens.Get(client.ServiceClient(), tokenId)
+}
+
+func TestGetWithToken(t *testing.T) {
+	GetIsSuccessful(t, tokenGet(t, "db22caf43c934e6c829087c41ff8d8d6"))
+}
diff --git a/openstack/identity/v2/tokens/urls.go b/openstack/identity/v2/tokens/urls.go
index ee13932..ee0a28f 100644
--- a/openstack/identity/v2/tokens/urls.go
+++ b/openstack/identity/v2/tokens/urls.go
@@ -1,6 +1,6 @@
 package tokens
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 // CreateURL generates the URL used to create new Tokens.
 func CreateURL(client *gophercloud.ServiceClient) string {
diff --git a/openstack/identity/v2/users/fixtures.go b/openstack/identity/v2/users/fixtures.go
deleted file mode 100644
index df3f774..0000000
--- a/openstack/identity/v2/users/fixtures.go
+++ /dev/null
@@ -1,165 +0,0 @@
-// +build fixtures
-
-package users
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func MockListUserResponse(t *testing.T) {
-	th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "users":[
-        {
-            "id": "u1000",
-						"name": "John Smith",
-            "username": "jqsmith",
-            "email": "john.smith@example.org",
-            "enabled": true,
-						"tenant_id": "12345"
-        },
-        {
-            "id": "u1001",
-						"name": "Jane Smith",
-            "username": "jqsmith",
-            "email": "jane.smith@example.org",
-            "enabled": true,
-						"tenant_id": "12345"
-        }
-    ]
-}
-  `)
-	})
-}
-
-func mockCreateUserResponse(t *testing.T) {
-	th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-    "user": {
-		    "name": "new_user",
-		    "tenant_id": "12345",
-				"enabled": false,
-				"email": "new_user@foo.com"
-    }
-}
-	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "user": {
-        "name": "new_user",
-        "tenant_id": "12345",
-        "enabled": false,
-        "email": "new_user@foo.com",
-        "id": "c39e3de9be2d4c779f1dfd6abacc176d"
-    }
-}
-`)
-	})
-}
-
-func mockGetUserResponse(t *testing.T) {
-	th.Mux.HandleFunc("/users/new_user", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-		"user": {
-				"name": "new_user",
-				"tenant_id": "12345",
-				"enabled": false,
-				"email": "new_user@foo.com",
-				"id": "c39e3de9be2d4c779f1dfd6abacc176d"
-		}
-}
-`)
-	})
-}
-
-func mockUpdateUserResponse(t *testing.T) {
-	th.Mux.HandleFunc("/users/c39e3de9be2d4c779f1dfd6abacc176d", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-    "user": {
-		    "name": "new_name",
-		    "enabled": true,
-		    "email": "new_email@foo.com"
-    }
-}
-`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-		"user": {
-				"name": "new_name",
-				"tenant_id": "12345",
-				"enabled": true,
-				"email": "new_email@foo.com",
-				"id": "c39e3de9be2d4c779f1dfd6abacc176d"
-		}
-}
-`)
-	})
-}
-
-func mockDeleteUserResponse(t *testing.T) {
-	th.Mux.HandleFunc("/users/c39e3de9be2d4c779f1dfd6abacc176d", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-func mockListRolesResponse(t *testing.T) {
-	th.Mux.HandleFunc("/tenants/1d8b6120dcc640fda4fc9194ffc80273/users/c39e3de9be2d4c779f1dfd6abacc176d/roles", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "roles": [
-        {
-            "id": "9fe2ff9ee4384b1894a90878d3e92bab",
-            "name": "foo_role"
-        },
-        {
-            "id": "1ea3d56793574b668e85960fbf651e13",
-            "name": "admin"
-        }
-    ]
-}
-	`)
-	})
-}
diff --git a/openstack/identity/v2/users/requests.go b/openstack/identity/v2/users/requests.go
index 88be45e..ef77d39 100644
--- a/openstack/identity/v2/users/requests.go
+++ b/openstack/identity/v2/users/requests.go
@@ -1,48 +1,31 @@
 package users
 
 import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
+// List lists the existing users.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, rootURL(client), func(r pagination.PageResult) pagination.Page {
 		return UserPage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, rootURL(client), createPage)
+	})
 }
 
-// EnabledState represents whether the user is enabled or not.
-type EnabledState *bool
-
-// Useful variables to use when creating or updating users.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Enabled  EnabledState = &iTrue
-	Disabled EnabledState = &iFalse
-)
-
 // CommonOpts are the parameters that are shared between CreateOpts and
 // UpdateOpts
 type CommonOpts struct {
 	// Either a name or username is required. When provided, the value must be
 	// unique or a 409 conflict error will be returned. If you provide a name but
 	// omit a username, the latter will be set to the former; and vice versa.
-	Name, Username string
-
+	Name     string `json:"name,omitempty"`
+	Username string `json:"username,omitempty"`
 	// The ID of the tenant to which you want to assign this user.
-	TenantID string
-
+	TenantID string `json:"tenant_id,omitempty"`
 	// Indicates whether this user is enabled or not.
-	Enabled EnabledState
-
+	Enabled *bool `json:"enabled,omitempty"`
 	// The email address of this user.
-	Email string
+	Email string `json:"email,omitempty"`
 }
 
 // CreateOpts represents the options needed when creating new users.
@@ -55,107 +38,69 @@
 
 // ToUserCreateMap assembles a request body based on the contents of a CreateOpts.
 func (opts CreateOpts) ToUserCreateMap() (map[string]interface{}, error) {
-	m := make(map[string]interface{})
-
 	if opts.Name == "" && opts.Username == "" {
-		return m, errors.New("Either a Name or Username must be provided")
+		err := gophercloud.ErrMissingInput{}
+		err.Argument = "users.CreateOpts.Name/users.CreateOpts.Username"
+		err.Info = "Either a Name or Username must be provided"
+		return nil, err
 	}
-
-	if opts.Name != "" {
-		m["name"] = opts.Name
-	}
-	if opts.Username != "" {
-		m["username"] = opts.Username
-	}
-	if opts.Enabled != nil {
-		m["enabled"] = &opts.Enabled
-	}
-	if opts.Email != "" {
-		m["email"] = opts.Email
-	}
-	if opts.TenantID != "" {
-		m["tenant_id"] = opts.TenantID
-	}
-
-	return map[string]interface{}{"user": m}, nil
+	return gophercloud.BuildRequestBody(opts, "user")
 }
 
 // Create is the operation responsible for creating new users.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToUserCreateMap()
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToUserCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = client.Post(rootURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201},
 	})
-
-	return res
+	return
 }
 
 // Get requests details on a single user, either by ID.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(ResourceURL(client, id), &result.Body, nil)
-	return result
+func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = client.Get(ResourceURL(client, id), &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder allows extensions to add additional attributes to the Update request.
 type UpdateOptsBuilder interface {
-	ToUserUpdateMap() map[string]interface{}
+	ToUserUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts specifies the base attributes that may be updated on an existing server.
 type UpdateOpts CommonOpts
 
 // ToUserUpdateMap formats an UpdateOpts structure into a request body.
-func (opts UpdateOpts) ToUserUpdateMap() map[string]interface{} {
-	m := make(map[string]interface{})
-
-	if opts.Name != "" {
-		m["name"] = opts.Name
-	}
-	if opts.Username != "" {
-		m["username"] = opts.Username
-	}
-	if opts.Enabled != nil {
-		m["enabled"] = &opts.Enabled
-	}
-	if opts.Email != "" {
-		m["email"] = opts.Email
-	}
-	if opts.TenantID != "" {
-		m["tenant_id"] = opts.TenantID
-	}
-
-	return map[string]interface{}{"user": m}
+func (opts UpdateOpts) ToUserUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "user")
 }
 
 // Update is the operation responsible for updating exist users by their UUID.
-func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var result UpdateResult
-	reqBody := opts.ToUserUpdateMap()
-	_, result.Err = client.Put(ResourceURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{
+func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToUserUpdateMap()
+	if err != nil {
+		r.Err = err
+		return
+	}
+	_, r.Err = client.Put(ResourceURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return result
+	return
 }
 
 // Delete is the operation responsible for permanently deleting an API user.
-func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var result DeleteResult
-	_, result.Err = client.Delete(ResourceURL(client, id), nil)
-	return result
+func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = client.Delete(ResourceURL(client, id), nil)
+	return
 }
 
+// ListRoles lists the existing roles that can be assigned to users.
 func ListRoles(client *gophercloud.ServiceClient, tenantID, userID string) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listRolesURL(client, tenantID, userID), func(r pagination.PageResult) pagination.Page {
 		return RolePage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, listRolesURL(client, tenantID, userID), createPage)
+	})
 }
diff --git a/openstack/identity/v2/users/requests_test.go b/openstack/identity/v2/users/requests_test.go
deleted file mode 100644
index 04f8371..0000000
--- a/openstack/identity/v2/users/requests_test.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package users
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockListUserResponse(t)
-
-	count := 0
-
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractUsers(page)
-		if err != nil {
-			t.Errorf("Failed to extract users: %v", err)
-			return false, err
-		}
-
-		expected := []User{
-			User{
-				ID:       "u1000",
-				Name:     "John Smith",
-				Username: "jqsmith",
-				Email:    "john.smith@example.org",
-				Enabled:  true,
-				TenantID: "12345",
-			},
-			User{
-				ID:       "u1001",
-				Name:     "Jane Smith",
-				Username: "jqsmith",
-				Email:    "jane.smith@example.org",
-				Enabled:  true,
-				TenantID: "12345",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestCreateUser(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockCreateUserResponse(t)
-
-	opts := CreateOpts{
-		Name:     "new_user",
-		TenantID: "12345",
-		Enabled:  Disabled,
-		Email:    "new_user@foo.com",
-	}
-
-	user, err := Create(client.ServiceClient(), opts).Extract()
-
-	th.AssertNoErr(t, err)
-
-	expected := &User{
-		Name:     "new_user",
-		ID:       "c39e3de9be2d4c779f1dfd6abacc176d",
-		Email:    "new_user@foo.com",
-		Enabled:  false,
-		TenantID: "12345",
-	}
-
-	th.AssertDeepEquals(t, expected, user)
-}
-
-func TestGetUser(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetUserResponse(t)
-
-	user, err := Get(client.ServiceClient(), "new_user").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &User{
-		Name:     "new_user",
-		ID:       "c39e3de9be2d4c779f1dfd6abacc176d",
-		Email:    "new_user@foo.com",
-		Enabled:  false,
-		TenantID: "12345",
-	}
-
-	th.AssertDeepEquals(t, expected, user)
-}
-
-func TestUpdateUser(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockUpdateUserResponse(t)
-
-	id := "c39e3de9be2d4c779f1dfd6abacc176d"
-	opts := UpdateOpts{
-		Name:    "new_name",
-		Enabled: Enabled,
-		Email:   "new_email@foo.com",
-	}
-
-	user, err := Update(client.ServiceClient(), id, opts).Extract()
-
-	th.AssertNoErr(t, err)
-
-	expected := &User{
-		Name:     "new_name",
-		ID:       id,
-		Email:    "new_email@foo.com",
-		Enabled:  true,
-		TenantID: "12345",
-	}
-
-	th.AssertDeepEquals(t, expected, user)
-}
-
-func TestDeleteUser(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteUserResponse(t)
-
-	res := Delete(client.ServiceClient(), "c39e3de9be2d4c779f1dfd6abacc176d")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestListingUserRoles(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListRolesResponse(t)
-
-	tenantID := "1d8b6120dcc640fda4fc9194ffc80273"
-	userID := "c39e3de9be2d4c779f1dfd6abacc176d"
-
-	err := ListRoles(client.ServiceClient(), tenantID, userID).EachPage(func(page pagination.Page) (bool, error) {
-		actual, err := ExtractRoles(page)
-		th.AssertNoErr(t, err)
-
-		expected := []Role{
-			Role{ID: "9fe2ff9ee4384b1894a90878d3e92bab", Name: "foo_role"},
-			Role{ID: "1ea3d56793574b668e85960fbf651e13", Name: "admin"},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
diff --git a/openstack/identity/v2/users/results.go b/openstack/identity/v2/users/results.go
index f531d5d..c493383 100644
--- a/openstack/identity/v2/users/results.go
+++ b/openstack/identity/v2/users/results.go
@@ -1,10 +1,8 @@
 package users
 
 import (
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // User represents a user resource that exists on the API.
@@ -25,7 +23,7 @@
 	Email string
 
 	// The ID of the tenant to which this user belongs.
-	TenantID string `mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 }
 
 // Role assigns specific responsibilities to users, allowing them to accomplish
@@ -49,43 +47,33 @@
 }
 
 // IsEmpty determines whether or not a page of Tenants contains any results.
-func (page UserPage) IsEmpty() (bool, error) {
-	users, err := ExtractUsers(page)
-	if err != nil {
-		return false, err
-	}
-	return len(users) == 0, nil
+func (r UserPage) IsEmpty() (bool, error) {
+	users, err := ExtractUsers(r)
+	return len(users) == 0, err
 }
 
 // ExtractUsers returns a slice of Tenants contained in a single page of results.
-func ExtractUsers(page pagination.Page) ([]User, error) {
-	casted := page.(UserPage).Body
-	var response struct {
-		Users []User `mapstructure:"users"`
+func ExtractUsers(r pagination.Page) ([]User, error) {
+	var s struct {
+		Users []User `json:"users"`
 	}
-
-	err := mapstructure.Decode(casted, &response)
-	return response.Users, err
+	err := (r.(UserPage)).ExtractInto(&s)
+	return s.Users, err
 }
 
 // IsEmpty determines whether or not a page of Tenants contains any results.
-func (page RolePage) IsEmpty() (bool, error) {
-	users, err := ExtractRoles(page)
-	if err != nil {
-		return false, err
-	}
-	return len(users) == 0, nil
+func (r RolePage) IsEmpty() (bool, error) {
+	users, err := ExtractRoles(r)
+	return len(users) == 0, err
 }
 
 // ExtractRoles returns a slice of Roles contained in a single page of results.
-func ExtractRoles(page pagination.Page) ([]Role, error) {
-	casted := page.(RolePage).Body
-	var response struct {
-		Roles []Role `mapstructure:"roles"`
+func ExtractRoles(r pagination.Page) ([]Role, error) {
+	var s struct {
+		Roles []Role `json:"roles"`
 	}
-
-	err := mapstructure.Decode(casted, &response)
-	return response.Roles, err
+	err := (r.(RolePage)).ExtractInto(&s)
+	return s.Roles, err
 }
 
 type commonResult struct {
@@ -94,17 +82,11 @@
 
 // Extract interprets any commonResult as a User, if possible.
 func (r commonResult) Extract() (*User, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		User *User `json:"user"`
 	}
-
-	var response struct {
-		User User `mapstructure:"user"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.User, err
+	err := r.ExtractInto(&s)
+	return s.User, err
 }
 
 // CreateResult represents the result of a Create operation
diff --git a/openstack/identity/v2/users/testing/doc.go b/openstack/identity/v2/users/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/identity/v2/users/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/identity/v2/users/testing/fixtures.go b/openstack/identity/v2/users/testing/fixtures.go
new file mode 100644
index 0000000..c1c3b89
--- /dev/null
+++ b/openstack/identity/v2/users/testing/fixtures.go
@@ -0,0 +1,163 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func MockListUserResponse(t *testing.T) {
+	th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "users":[
+        {
+            "id": "u1000",
+						"name": "John Smith",
+            "username": "jqsmith",
+            "email": "john.smith@example.org",
+            "enabled": true,
+						"tenant_id": "12345"
+        },
+        {
+            "id": "u1001",
+						"name": "Jane Smith",
+            "username": "jqsmith",
+            "email": "jane.smith@example.org",
+            "enabled": true,
+						"tenant_id": "12345"
+        }
+    ]
+}
+  `)
+	})
+}
+
+func mockCreateUserResponse(t *testing.T) {
+	th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		th.TestJSONRequest(t, r, `
+{
+    "user": {
+		    "name": "new_user",
+		    "tenant_id": "12345",
+				"enabled": false,
+				"email": "new_user@foo.com"
+    }
+}
+	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "user": {
+        "name": "new_user",
+        "tenant_id": "12345",
+        "enabled": false,
+        "email": "new_user@foo.com",
+        "id": "c39e3de9be2d4c779f1dfd6abacc176d"
+    }
+}
+`)
+	})
+}
+
+func mockGetUserResponse(t *testing.T) {
+	th.Mux.HandleFunc("/users/new_user", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+		"user": {
+				"name": "new_user",
+				"tenant_id": "12345",
+				"enabled": false,
+				"email": "new_user@foo.com",
+				"id": "c39e3de9be2d4c779f1dfd6abacc176d"
+		}
+}
+`)
+	})
+}
+
+func mockUpdateUserResponse(t *testing.T) {
+	th.Mux.HandleFunc("/users/c39e3de9be2d4c779f1dfd6abacc176d", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		th.TestJSONRequest(t, r, `
+{
+    "user": {
+		    "name": "new_name",
+		    "enabled": true,
+		    "email": "new_email@foo.com"
+    }
+}
+`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+		"user": {
+				"name": "new_name",
+				"tenant_id": "12345",
+				"enabled": true,
+				"email": "new_email@foo.com",
+				"id": "c39e3de9be2d4c779f1dfd6abacc176d"
+		}
+}
+`)
+	})
+}
+
+func mockDeleteUserResponse(t *testing.T) {
+	th.Mux.HandleFunc("/users/c39e3de9be2d4c779f1dfd6abacc176d", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+func mockListRolesResponse(t *testing.T) {
+	th.Mux.HandleFunc("/tenants/1d8b6120dcc640fda4fc9194ffc80273/users/c39e3de9be2d4c779f1dfd6abacc176d/roles", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "roles": [
+        {
+            "id": "9fe2ff9ee4384b1894a90878d3e92bab",
+            "name": "foo_role"
+        },
+        {
+            "id": "1ea3d56793574b668e85960fbf651e13",
+            "name": "admin"
+        }
+    ]
+}
+	`)
+	})
+}
diff --git a/openstack/identity/v2/users/testing/requests_test.go b/openstack/identity/v2/users/testing/requests_test.go
new file mode 100644
index 0000000..3cb047e
--- /dev/null
+++ b/openstack/identity/v2/users/testing/requests_test.go
@@ -0,0 +1,161 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/identity/v2/users"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockListUserResponse(t)
+
+	count := 0
+
+	err := users.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := users.ExtractUsers(page)
+		th.AssertNoErr(t, err)
+
+		expected := []users.User{
+			{
+				ID:       "u1000",
+				Name:     "John Smith",
+				Username: "jqsmith",
+				Email:    "john.smith@example.org",
+				Enabled:  true,
+				TenantID: "12345",
+			},
+			{
+				ID:       "u1001",
+				Name:     "Jane Smith",
+				Username: "jqsmith",
+				Email:    "jane.smith@example.org",
+				Enabled:  true,
+				TenantID: "12345",
+			},
+		}
+		th.CheckDeepEquals(t, expected, actual)
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, count)
+}
+
+func TestCreateUser(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockCreateUserResponse(t)
+
+	opts := users.CreateOpts{
+		Name:     "new_user",
+		TenantID: "12345",
+		Enabled:  gophercloud.Disabled,
+		Email:    "new_user@foo.com",
+	}
+
+	user, err := users.Create(client.ServiceClient(), opts).Extract()
+
+	th.AssertNoErr(t, err)
+
+	expected := &users.User{
+		Name:     "new_user",
+		ID:       "c39e3de9be2d4c779f1dfd6abacc176d",
+		Email:    "new_user@foo.com",
+		Enabled:  false,
+		TenantID: "12345",
+	}
+
+	th.AssertDeepEquals(t, expected, user)
+}
+
+func TestGetUser(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockGetUserResponse(t)
+
+	user, err := users.Get(client.ServiceClient(), "new_user").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &users.User{
+		Name:     "new_user",
+		ID:       "c39e3de9be2d4c779f1dfd6abacc176d",
+		Email:    "new_user@foo.com",
+		Enabled:  false,
+		TenantID: "12345",
+	}
+
+	th.AssertDeepEquals(t, expected, user)
+}
+
+func TestUpdateUser(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockUpdateUserResponse(t)
+
+	id := "c39e3de9be2d4c779f1dfd6abacc176d"
+	opts := users.UpdateOpts{
+		Name:    "new_name",
+		Enabled: gophercloud.Enabled,
+		Email:   "new_email@foo.com",
+	}
+
+	user, err := users.Update(client.ServiceClient(), id, opts).Extract()
+
+	th.AssertNoErr(t, err)
+
+	expected := &users.User{
+		Name:     "new_name",
+		ID:       id,
+		Email:    "new_email@foo.com",
+		Enabled:  true,
+		TenantID: "12345",
+	}
+
+	th.AssertDeepEquals(t, expected, user)
+}
+
+func TestDeleteUser(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockDeleteUserResponse(t)
+
+	res := users.Delete(client.ServiceClient(), "c39e3de9be2d4c779f1dfd6abacc176d")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestListingUserRoles(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	mockListRolesResponse(t)
+
+	tenantID := "1d8b6120dcc640fda4fc9194ffc80273"
+	userID := "c39e3de9be2d4c779f1dfd6abacc176d"
+
+	err := users.ListRoles(client.ServiceClient(), tenantID, userID).EachPage(func(page pagination.Page) (bool, error) {
+		actual, err := users.ExtractRoles(page)
+		th.AssertNoErr(t, err)
+
+		expected := []users.Role{
+			{ID: "9fe2ff9ee4384b1894a90878d3e92bab", Name: "foo_role"},
+			{ID: "1ea3d56793574b668e85960fbf651e13", Name: "admin"},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/identity/v2/users/urls.go b/openstack/identity/v2/users/urls.go
index 7ec4385..89f66f2 100644
--- a/openstack/identity/v2/users/urls.go
+++ b/openstack/identity/v2/users/urls.go
@@ -1,6 +1,6 @@
 package users
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	tenantPath = "tenants"
diff --git a/openstack/identity/v3/endpoints/errors.go b/openstack/identity/v3/endpoints/errors.go
deleted file mode 100644
index 854957f..0000000
--- a/openstack/identity/v3/endpoints/errors.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package endpoints
-
-import "fmt"
-
-func requiredAttribute(attribute string) error {
-	return fmt.Errorf("You must specify %s for this endpoint.", attribute)
-}
-
-var (
-	// ErrAvailabilityRequired is reported if an Endpoint is created without an Availability.
-	ErrAvailabilityRequired = requiredAttribute("an availability")
-
-	// ErrNameRequired is reported if an Endpoint is created without a Name.
-	ErrNameRequired = requiredAttribute("a name")
-
-	// ErrURLRequired is reported if an Endpoint is created without a URL.
-	ErrURLRequired = requiredAttribute("a URL")
-
-	// ErrServiceIDRequired is reported if an Endpoint is created without a ServiceID.
-	ErrServiceIDRequired = requiredAttribute("a serviceID")
-)
diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go
index 99a495d..fc44365 100644
--- a/openstack/identity/v3/endpoints/requests.go
+++ b/openstack/identity/v3/endpoints/requests.go
@@ -1,63 +1,41 @@
 package endpoints
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// EndpointOpts contains the subset of Endpoint attributes that should be used to create or update an Endpoint.
-type EndpointOpts struct {
-	Availability gophercloud.Availability
-	Name         string
-	Region       string
-	URL          string
-	ServiceID    string
+type CreateOptsBuilder interface {
+	ToEndpointCreateMap() (map[string]interface{}, error)
+}
+
+// CreateOpts contains the subset of Endpoint attributes that should be used to create an Endpoint.
+type CreateOpts struct {
+	Availability gophercloud.Availability `json:"interface" required:"true"`
+	Name         string                   `json:"name" required:"true"`
+	Region       string                   `json:"region,omitempty"`
+	URL          string                   `json:"url" required:"true"`
+	ServiceID    string                   `json:"service_id" required:"true"`
+}
+
+func (opts CreateOpts) ToEndpointCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "endpoint")
 }
 
 // Create inserts a new Endpoint into the service catalog.
 // Within EndpointOpts, Region may be omitted by being left as "", but all other fields are required.
-func Create(client *gophercloud.ServiceClient, opts EndpointOpts) CreateResult {
-	// Redefined so that Region can be re-typed as a *string, which can be omitted from the JSON output.
-	type endpoint struct {
-		Interface string  `json:"interface"`
-		Name      string  `json:"name"`
-		Region    *string `json:"region,omitempty"`
-		URL       string  `json:"url"`
-		ServiceID string  `json:"service_id"`
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToEndpointCreateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
+	_, r.Err = client.Post(listURL(client), &b, &r.Body, nil)
+	return
+}
 
-	type request struct {
-		Endpoint endpoint `json:"endpoint"`
-	}
-
-	// Ensure that EndpointOpts is fully populated.
-	if opts.Availability == "" {
-		return createErr(ErrAvailabilityRequired)
-	}
-	if opts.Name == "" {
-		return createErr(ErrNameRequired)
-	}
-	if opts.URL == "" {
-		return createErr(ErrURLRequired)
-	}
-	if opts.ServiceID == "" {
-		return createErr(ErrServiceIDRequired)
-	}
-
-	// Populate the request body.
-	reqBody := request{
-		Endpoint: endpoint{
-			Interface: string(opts.Availability),
-			Name:      opts.Name,
-			URL:       opts.URL,
-			ServiceID: opts.ServiceID,
-		},
-	}
-	reqBody.Endpoint.Region = gophercloud.MaybeString(opts.Region)
-
-	var result CreateResult
-	_, result.Err = client.Post(listURL(client), reqBody, &result.Body, nil)
-	return result
+type ListOptsBuilder interface {
+	ToEndpointListParams() (string, error)
 }
 
 // ListOpts allows finer control over the endpoints returned by a List call.
@@ -69,55 +47,56 @@
 	PerPage      int                      `q:"per_page"`
 }
 
-// List enumerates endpoints in a paginated collection, optionally filtered by ListOpts criteria.
-func List(client *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
-	u := listURL(client)
+func (opts ListOpts) ToEndpointListParams() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return pagination.Pager{Err: err}
-	}
-	u += q.String()
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return EndpointPage{pagination.LinkedPageBase{PageResult: r}}
-	}
+	return q.String(), err
+}
 
-	return pagination.NewPager(client, u, createPage)
+// List enumerates endpoints in a paginated collection, optionally filtered by ListOpts criteria.
+func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
+	u := listURL(client)
+	if opts != nil {
+		q, err := gophercloud.BuildQueryString(opts)
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		u += q.String()
+	}
+	return pagination.NewPager(client, u, func(r pagination.PageResult) pagination.Page {
+		return EndpointPage{pagination.LinkedPageBase{PageResult: r}}
+	})
+}
+
+type UpdateOptsBuilder interface {
+	ToEndpointUpdateMap() (map[string]interface{}, error)
+}
+
+// UpdateOpts contains the subset of Endpoint attributes that should be used to update an Endpoint.
+type UpdateOpts struct {
+	Availability gophercloud.Availability `json:"interface,omitempty"`
+	Name         string                   `json:"name,omitempty"`
+	Region       string                   `json:"region,omitempty"`
+	URL          string                   `json:"url,omitempty"`
+	ServiceID    string                   `json:"service_id,omitempty"`
+}
+
+func (opts UpdateOpts) ToEndpointUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "endpoint")
 }
 
 // Update changes an existing endpoint with new data.
-// All fields are optional in the provided EndpointOpts.
-func Update(client *gophercloud.ServiceClient, endpointID string, opts EndpointOpts) UpdateResult {
-	type endpoint struct {
-		Interface *string `json:"interface,omitempty"`
-		Name      *string `json:"name,omitempty"`
-		Region    *string `json:"region,omitempty"`
-		URL       *string `json:"url,omitempty"`
-		ServiceID *string `json:"service_id,omitempty"`
+func Update(client *gophercloud.ServiceClient, endpointID string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToEndpointUpdateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-
-	type request struct {
-		Endpoint endpoint `json:"endpoint"`
-	}
-
-	reqBody := request{Endpoint: endpoint{}}
-	reqBody.Endpoint.Interface = gophercloud.MaybeString(string(opts.Availability))
-	reqBody.Endpoint.Name = gophercloud.MaybeString(opts.Name)
-	reqBody.Endpoint.Region = gophercloud.MaybeString(opts.Region)
-	reqBody.Endpoint.URL = gophercloud.MaybeString(opts.URL)
-	reqBody.Endpoint.ServiceID = gophercloud.MaybeString(opts.ServiceID)
-
-	var result UpdateResult
-	_, result.Err = client.Request("PATCH", endpointURL(client, endpointID), gophercloud.RequestOpts{
-		JSONBody:     &reqBody,
-		JSONResponse: &result.Body,
-		OkCodes:      []int{200},
-	})
-	return result
+	_, r.Err = client.Patch(endpointURL(client, endpointID), &b, &r.Body, nil)
+	return
 }
 
 // Delete removes an endpoint from the service catalog.
-func Delete(client *gophercloud.ServiceClient, endpointID string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(endpointURL(client, endpointID), nil)
-	return res
+func Delete(client *gophercloud.ServiceClient, endpointID string) (r DeleteResult) {
+	_, r.Err = client.Delete(endpointURL(client, endpointID), nil)
+	return
 }
diff --git a/openstack/identity/v3/endpoints/requests_test.go b/openstack/identity/v3/endpoints/requests_test.go
deleted file mode 100644
index 80687c4..0000000
--- a/openstack/identity/v3/endpoints/requests_test.go
+++ /dev/null
@@ -1,226 +0,0 @@
-package endpoints
-
-import (
-	"fmt"
-	"net/http"
-	"reflect"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestCreateSuccessful(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	testhelper.Mux.HandleFunc("/endpoints", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "POST")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		testhelper.TestJSONRequest(t, r, `
-      {
-        "endpoint": {
-          "interface": "public",
-          "name": "the-endiest-of-points",
-          "region": "underground",
-          "url": "https://1.2.3.4:9000/",
-          "service_id": "asdfasdfasdfasdf"
-        }
-      }
-    `)
-
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, `
-      {
-        "endpoint": {
-          "id": "12",
-          "interface": "public",
-          "links": {
-            "self": "https://localhost:5000/v3/endpoints/12"
-          },
-          "name": "the-endiest-of-points",
-          "region": "underground",
-          "service_id": "asdfasdfasdfasdf",
-          "url": "https://1.2.3.4:9000/"
-        }
-      }
-    `)
-	})
-
-	actual, err := Create(client.ServiceClient(), EndpointOpts{
-		Availability: gophercloud.AvailabilityPublic,
-		Name:         "the-endiest-of-points",
-		Region:       "underground",
-		URL:          "https://1.2.3.4:9000/",
-		ServiceID:    "asdfasdfasdfasdf",
-	}).Extract()
-	if err != nil {
-		t.Fatalf("Unable to create an endpoint: %v", err)
-	}
-
-	expected := &Endpoint{
-		ID:           "12",
-		Availability: gophercloud.AvailabilityPublic,
-		Name:         "the-endiest-of-points",
-		Region:       "underground",
-		ServiceID:    "asdfasdfasdfasdf",
-		URL:          "https://1.2.3.4:9000/",
-	}
-
-	if !reflect.DeepEqual(actual, expected) {
-		t.Errorf("Expected %#v, was %#v", expected, actual)
-	}
-}
-
-func TestListEndpoints(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	testhelper.Mux.HandleFunc("/endpoints", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "GET")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `
-			{
-				"endpoints": [
-					{
-						"id": "12",
-						"interface": "public",
-						"links": {
-							"self": "https://localhost:5000/v3/endpoints/12"
-						},
-						"name": "the-endiest-of-points",
-						"region": "underground",
-						"service_id": "asdfasdfasdfasdf",
-						"url": "https://1.2.3.4:9000/"
-					},
-					{
-						"id": "13",
-						"interface": "internal",
-						"links": {
-							"self": "https://localhost:5000/v3/endpoints/13"
-						},
-						"name": "shhhh",
-						"region": "underground",
-						"service_id": "asdfasdfasdfasdf",
-						"url": "https://1.2.3.4:9001/"
-					}
-				],
-				"links": {
-					"next": null,
-					"previous": null
-				}
-			}
-		`)
-	})
-
-	count := 0
-	List(client.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractEndpoints(page)
-		if err != nil {
-			t.Errorf("Failed to extract endpoints: %v", err)
-			return false, err
-		}
-
-		expected := []Endpoint{
-			Endpoint{
-				ID:           "12",
-				Availability: gophercloud.AvailabilityPublic,
-				Name:         "the-endiest-of-points",
-				Region:       "underground",
-				ServiceID:    "asdfasdfasdfasdf",
-				URL:          "https://1.2.3.4:9000/",
-			},
-			Endpoint{
-				ID:           "13",
-				Availability: gophercloud.AvailabilityInternal,
-				Name:         "shhhh",
-				Region:       "underground",
-				ServiceID:    "asdfasdfasdfasdf",
-				URL:          "https://1.2.3.4:9001/",
-			},
-		}
-
-		if !reflect.DeepEqual(expected, actual) {
-			t.Errorf("Expected %#v, got %#v", expected, actual)
-		}
-
-		return true, nil
-	})
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestUpdateEndpoint(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	testhelper.Mux.HandleFunc("/endpoints/12", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "PATCH")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		testhelper.TestJSONRequest(t, r, `
-		{
-	    "endpoint": {
-	      "name": "renamed",
-				"region": "somewhere-else"
-	    }
-		}
-	`)
-
-		fmt.Fprintf(w, `
-		{
-			"endpoint": {
-				"id": "12",
-				"interface": "public",
-				"links": {
-					"self": "https://localhost:5000/v3/endpoints/12"
-				},
-				"name": "renamed",
-				"region": "somewhere-else",
-				"service_id": "asdfasdfasdfasdf",
-				"url": "https://1.2.3.4:9000/"
-			}
-		}
-	`)
-	})
-
-	actual, err := Update(client.ServiceClient(), "12", EndpointOpts{
-		Name:   "renamed",
-		Region: "somewhere-else",
-	}).Extract()
-	if err != nil {
-		t.Fatalf("Unexpected error from Update: %v", err)
-	}
-
-	expected := &Endpoint{
-		ID:           "12",
-		Availability: gophercloud.AvailabilityPublic,
-		Name:         "renamed",
-		Region:       "somewhere-else",
-		ServiceID:    "asdfasdfasdfasdf",
-		URL:          "https://1.2.3.4:9000/",
-	}
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("Expected %#v, was %#v", expected, actual)
-	}
-}
-
-func TestDeleteEndpoint(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	testhelper.Mux.HandleFunc("/endpoints/34", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "DELETE")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(client.ServiceClient(), "34")
-	testhelper.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go
index 1281122..2788f16 100644
--- a/openstack/identity/v3/endpoints/results.go
+++ b/openstack/identity/v3/endpoints/results.go
@@ -1,9 +1,8 @@
 package endpoints
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 type commonResult struct {
@@ -13,17 +12,11 @@
 // Extract interprets a GetResult, CreateResult or UpdateResult as a concrete Endpoint.
 // An error is returned if the original call or the extraction failed.
 func (r commonResult) Extract() (*Endpoint, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Endpoint *Endpoint `json:"endpoint"`
 	}
-
-	var res struct {
-		Endpoint `json:"endpoint"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return &res.Endpoint, err
+	err := r.ExtractInto(&s)
+	return s.Endpoint, err
 }
 
 // CreateResult is the deferred result of a Create call.
@@ -48,12 +41,12 @@
 
 // Endpoint describes the entry point for another service's API.
 type Endpoint struct {
-	ID           string                   `mapstructure:"id" json:"id"`
-	Availability gophercloud.Availability `mapstructure:"interface" json:"interface"`
-	Name         string                   `mapstructure:"name" json:"name"`
-	Region       string                   `mapstructure:"region" json:"region"`
-	ServiceID    string                   `mapstructure:"service_id" json:"service_id"`
-	URL          string                   `mapstructure:"url" json:"url"`
+	ID           string                   `json:"id"`
+	Availability gophercloud.Availability `json:"interface"`
+	Name         string                   `json:"name"`
+	Region       string                   `json:"region"`
+	ServiceID    string                   `json:"service_id"`
+	URL          string                   `json:"url"`
 }
 
 // EndpointPage is a single page of Endpoint results.
@@ -62,21 +55,16 @@
 }
 
 // IsEmpty returns true if no Endpoints were returned.
-func (p EndpointPage) IsEmpty() (bool, error) {
-	es, err := ExtractEndpoints(p)
-	if err != nil {
-		return true, err
-	}
-	return len(es) == 0, nil
+func (r EndpointPage) IsEmpty() (bool, error) {
+	es, err := ExtractEndpoints(r)
+	return len(es) == 0, err
 }
 
 // ExtractEndpoints extracts an Endpoint slice from a Page.
-func ExtractEndpoints(page pagination.Page) ([]Endpoint, error) {
-	var response struct {
-		Endpoints []Endpoint `mapstructure:"endpoints"`
+func ExtractEndpoints(r pagination.Page) ([]Endpoint, error) {
+	var s struct {
+		Endpoints []Endpoint `json:"endpoints"`
 	}
-
-	err := mapstructure.Decode(page.(EndpointPage).Body, &response)
-
-	return response.Endpoints, err
+	err := (r.(EndpointPage)).ExtractInto(&s)
+	return s.Endpoints, err
 }
diff --git a/openstack/identity/v3/endpoints/testing/doc.go b/openstack/identity/v3/endpoints/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/identity/v3/endpoints/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go
new file mode 100644
index 0000000..53d8488
--- /dev/null
+++ b/openstack/identity/v3/endpoints/testing/requests_test.go
@@ -0,0 +1,214 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/identity/v3/endpoints"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestCreateSuccessful(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/endpoints", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `
+      {
+        "endpoint": {
+          "interface": "public",
+          "name": "the-endiest-of-points",
+          "region": "underground",
+          "url": "https://1.2.3.4:9000/",
+          "service_id": "asdfasdfasdfasdf"
+        }
+      }
+    `)
+
+		w.WriteHeader(http.StatusCreated)
+		fmt.Fprintf(w, `
+      {
+        "endpoint": {
+          "id": "12",
+          "interface": "public",
+          "links": {
+            "self": "https://localhost:5000/v3/endpoints/12"
+          },
+          "name": "the-endiest-of-points",
+          "region": "underground",
+          "service_id": "asdfasdfasdfasdf",
+          "url": "https://1.2.3.4:9000/"
+        }
+      }
+    `)
+	})
+
+	actual, err := endpoints.Create(client.ServiceClient(), endpoints.CreateOpts{
+		Availability: gophercloud.AvailabilityPublic,
+		Name:         "the-endiest-of-points",
+		Region:       "underground",
+		URL:          "https://1.2.3.4:9000/",
+		ServiceID:    "asdfasdfasdfasdf",
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &endpoints.Endpoint{
+		ID:           "12",
+		Availability: gophercloud.AvailabilityPublic,
+		Name:         "the-endiest-of-points",
+		Region:       "underground",
+		ServiceID:    "asdfasdfasdfasdf",
+		URL:          "https://1.2.3.4:9000/",
+	}
+
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestListEndpoints(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/endpoints", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `
+			{
+				"endpoints": [
+					{
+						"id": "12",
+						"interface": "public",
+						"links": {
+							"self": "https://localhost:5000/v3/endpoints/12"
+						},
+						"name": "the-endiest-of-points",
+						"region": "underground",
+						"service_id": "asdfasdfasdfasdf",
+						"url": "https://1.2.3.4:9000/"
+					},
+					{
+						"id": "13",
+						"interface": "internal",
+						"links": {
+							"self": "https://localhost:5000/v3/endpoints/13"
+						},
+						"name": "shhhh",
+						"region": "underground",
+						"service_id": "asdfasdfasdfasdf",
+						"url": "https://1.2.3.4:9001/"
+					}
+				],
+				"links": {
+					"next": null,
+					"previous": null
+				}
+			}
+		`)
+	})
+
+	count := 0
+	endpoints.List(client.ServiceClient(), endpoints.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := endpoints.ExtractEndpoints(page)
+		if err != nil {
+			t.Errorf("Failed to extract endpoints: %v", err)
+			return false, err
+		}
+
+		expected := []endpoints.Endpoint{
+			{
+				ID:           "12",
+				Availability: gophercloud.AvailabilityPublic,
+				Name:         "the-endiest-of-points",
+				Region:       "underground",
+				ServiceID:    "asdfasdfasdfasdf",
+				URL:          "https://1.2.3.4:9000/",
+			},
+			{
+				ID:           "13",
+				Availability: gophercloud.AvailabilityInternal,
+				Name:         "shhhh",
+				Region:       "underground",
+				ServiceID:    "asdfasdfasdfasdf",
+				URL:          "https://1.2.3.4:9001/",
+			},
+		}
+		th.AssertDeepEquals(t, expected, actual)
+		return true, nil
+	})
+	th.AssertEquals(t, 1, count)
+}
+
+func TestUpdateEndpoint(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/endpoints/12", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PATCH")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `
+		{
+	    "endpoint": {
+	      "name": "renamed",
+				"region": "somewhere-else"
+	    }
+		}
+	`)
+
+		fmt.Fprintf(w, `
+		{
+			"endpoint": {
+				"id": "12",
+				"interface": "public",
+				"links": {
+					"self": "https://localhost:5000/v3/endpoints/12"
+				},
+				"name": "renamed",
+				"region": "somewhere-else",
+				"service_id": "asdfasdfasdfasdf",
+				"url": "https://1.2.3.4:9000/"
+			}
+		}
+	`)
+	})
+
+	actual, err := endpoints.Update(client.ServiceClient(), "12", endpoints.UpdateOpts{
+		Name:   "renamed",
+		Region: "somewhere-else",
+	}).Extract()
+	if err != nil {
+		t.Fatalf("Unexpected error from Update: %v", err)
+	}
+
+	expected := &endpoints.Endpoint{
+		ID:           "12",
+		Availability: gophercloud.AvailabilityPublic,
+		Name:         "renamed",
+		Region:       "somewhere-else",
+		ServiceID:    "asdfasdfasdfasdf",
+		URL:          "https://1.2.3.4:9000/",
+	}
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestDeleteEndpoint(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/endpoints/34", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := endpoints.Delete(client.ServiceClient(), "34")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/identity/v3/endpoints/urls.go b/openstack/identity/v3/endpoints/urls.go
index 547d7b1..80cf57e 100644
--- a/openstack/identity/v3/endpoints/urls.go
+++ b/openstack/identity/v3/endpoints/urls.go
@@ -1,6 +1,6 @@
 package endpoints
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func listURL(client *gophercloud.ServiceClient) string {
 	return client.ServiceURL("endpoints")
diff --git a/openstack/identity/v3/endpoints/urls_test.go b/openstack/identity/v3/endpoints/urls_test.go
deleted file mode 100644
index 0b183b7..0000000
--- a/openstack/identity/v3/endpoints/urls_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package endpoints
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-)
-
-func TestGetListURL(t *testing.T) {
-	client := gophercloud.ServiceClient{Endpoint: "http://localhost:5000/v3/"}
-	url := listURL(&client)
-	if url != "http://localhost:5000/v3/endpoints" {
-		t.Errorf("Unexpected list URL generated: [%s]", url)
-	}
-}
-
-func TestGetEndpointURL(t *testing.T) {
-	client := gophercloud.ServiceClient{Endpoint: "http://localhost:5000/v3/"}
-	url := endpointURL(&client, "1234")
-	if url != "http://localhost:5000/v3/endpoints/1234" {
-		t.Errorf("Unexpected service URL generated: [%s]", url)
-	}
-}
diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go
index d95c1e5..de65c51 100644
--- a/openstack/identity/v3/roles/requests.go
+++ b/openstack/identity/v3/roles/requests.go
@@ -1,8 +1,8 @@
 package roles
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListAssignmentsOptsBuilder allows extensions to add additional parameters to
@@ -17,34 +17,31 @@
 // Effective lists effective assignments at the user, project, and domain level,
 // allowing for the effects of group membership.
 type ListAssignmentsOpts struct {
-	GroupId        string `q:"group.id"`
-	RoleId         string `q:"role.id"`
-	ScopeDomainId  string `q:"scope.domain.id"`
-	ScopeProjectId string `q:"scope.project.id"`
-	UserId         string `q:"user.id"`
-	Effective      bool   `q:"effective"`
+	GroupID        string `q:"group.id"`
+	RoleID         string `q:"role.id"`
+	ScopeDomainID  string `q:"scope.domain.id"`
+	ScopeProjectID string `q:"scope.project.id"`
+	UserID         string `q:"user.id"`
+	Effective      *bool  `q:"effective"`
 }
 
 // ToRolesListAssignmentsQuery formats a ListAssignmentsOpts into a query string.
 func (opts ListAssignmentsOpts) ToRolesListAssignmentsQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // ListAssignments enumerates the roles assigned to a specified resource.
 func ListAssignments(client *gophercloud.ServiceClient, opts ListAssignmentsOptsBuilder) pagination.Pager {
 	url := listAssignmentsURL(client)
-	query, err := opts.ToRolesListAssignmentsQuery()
-	if err != nil {
-		return pagination.Pager{Err: err}
+	if opts != nil {
+		query, err := opts.ToRolesListAssignmentsQuery()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		url += query
 	}
-	url += query
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return RoleAssignmentsPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, url, createPage)
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
+		return RoleAssignmentPage{pagination.LinkedPageBase{PageResult: r}}
+	})
 }
diff --git a/openstack/identity/v3/roles/requests_test.go b/openstack/identity/v3/roles/requests_test.go
deleted file mode 100644
index d62dbff..0000000
--- a/openstack/identity/v3/roles/requests_test.go
+++ /dev/null
@@ -1,104 +0,0 @@
-package roles
-
-import (
-	"fmt"
-	"net/http"
-	"reflect"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListSinglePage(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	testhelper.Mux.HandleFunc("/role_assignments", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "GET")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `
-			{
-                "role_assignments": [
-                    {
-                        "links": {
-                            "assignment": "http://identity:35357/v3/domains/161718/users/313233/roles/123456"
-                        },
-                        "role": {
-                            "id": "123456"
-                        },
-                        "scope": {
-                            "domain": {
-                                "id": "161718"
-                            }
-                        },
-                        "user": {
-                            "id": "313233"
-                        }
-                    },
-                    {
-                        "links": {
-                            "assignment": "http://identity:35357/v3/projects/456789/groups/101112/roles/123456",
-                            "membership": "http://identity:35357/v3/groups/101112/users/313233"
-                        },
-                        "role": {
-                            "id": "123456"
-                        },
-                        "scope": {
-                            "project": {
-                                "id": "456789"
-                            }
-                        },
-                        "user": {
-                            "id": "313233"
-                        }
-                    }
-                ],
-                "links": {
-                    "self": "http://identity:35357/v3/role_assignments?effective",
-                    "previous": null,
-                    "next": null
-                }
-            }
-		`)
-	})
-
-	count := 0
-	err := ListAssignments(client.ServiceClient(), ListAssignmentsOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractRoleAssignments(page)
-		if err != nil {
-			return false, err
-		}
-
-		expected := []RoleAssignment{
-			RoleAssignment{
-				Role:  Role{ID: "123456"},
-				Scope: Scope{Domain: Domain{ID: "161718"}},
-				User:  User{ID: "313233"},
-				Group: Group{},
-			},
-			RoleAssignment{
-				Role:  Role{ID: "123456"},
-				Scope: Scope{Project: Project{ID: "456789"}},
-				User:  User{ID: "313233"},
-				Group: Group{},
-			},
-		}
-
-		if !reflect.DeepEqual(expected, actual) {
-			t.Errorf("Expected %#v, got %#v", expected, actual)
-		}
-
-		return true, nil
-	})
-	if err != nil {
-		t.Errorf("Unexpected error while paging: %v", err)
-	}
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go
index d25abd2..e8a3aa9 100644
--- a/openstack/identity/v3/roles/results.go
+++ b/openstack/identity/v3/roles/results.go
@@ -1,10 +1,6 @@
 package roles
 
-import (
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
-)
+import "github.com/gophercloud/gophercloud/pagination"
 
 // RoleAssignment is the result of a role assignments query.
 type RoleAssignment struct {
@@ -20,7 +16,7 @@
 
 type Scope struct {
 	Domain  Domain  `json:"domain,omitempty"`
-	Project Project `json:"domain,omitempty"`
+	Project Project `json:"project,omitempty"`
 }
 
 type Domain struct {
@@ -39,43 +35,33 @@
 	ID string `json:"id,omitempty"`
 }
 
-// RoleAssignmentsPage is a single page of RoleAssignments results.
-type RoleAssignmentsPage struct {
+// RoleAssignmentPage is a single page of RoleAssignments results.
+type RoleAssignmentPage struct {
 	pagination.LinkedPageBase
 }
 
 // IsEmpty returns true if the page contains no results.
-func (p RoleAssignmentsPage) IsEmpty() (bool, error) {
-	roleAssignments, err := ExtractRoleAssignments(p)
-	if err != nil {
-		return true, err
-	}
-	return len(roleAssignments) == 0, nil
+func (r RoleAssignmentPage) IsEmpty() (bool, error) {
+	roleAssignments, err := ExtractRoleAssignments(r)
+	return len(roleAssignments) == 0, err
 }
 
 // NextPageURL uses the response's embedded link reference to navigate to the next page of results.
-func (page RoleAssignmentsPage) NextPageURL() (string, error) {
-	type resp struct {
+func (r RoleAssignmentPage) NextPageURL() (string, error) {
+	var s struct {
 		Links struct {
-			Next string `mapstructure:"next"`
-		} `mapstructure:"links"`
+			Next string `json:"next"`
+		} `json:"links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(page.Body, &r)
-	if err != nil {
-		return "", err
-	}
-
-	return r.Links.Next, nil
+	err := r.ExtractInto(&s)
+	return s.Links.Next, err
 }
 
 // ExtractRoleAssignments extracts a slice of RoleAssignments from a Collection acquired from List.
-func ExtractRoleAssignments(page pagination.Page) ([]RoleAssignment, error) {
-	var response struct {
-		RoleAssignments []RoleAssignment `mapstructure:"role_assignments"`
+func ExtractRoleAssignments(r pagination.Page) ([]RoleAssignment, error) {
+	var s struct {
+		RoleAssignments []RoleAssignment `json:"role_assignments"`
 	}
-
-	err := mapstructure.Decode(page.(RoleAssignmentsPage).Body, &response)
-	return response.RoleAssignments, err
+	err := (r.(RoleAssignmentPage)).ExtractInto(&s)
+	return s.RoleAssignments, err
 }
diff --git a/openstack/identity/v3/roles/testing/doc.go b/openstack/identity/v3/roles/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/identity/v3/roles/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go
new file mode 100644
index 0000000..dd9b704
--- /dev/null
+++ b/openstack/identity/v3/roles/testing/requests_test.go
@@ -0,0 +1,105 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/identity/v3/roles"
+	"github.com/gophercloud/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestListSinglePage(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/role_assignments", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "GET")
+		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `
+			{
+                "role_assignments": [
+                    {
+                        "links": {
+                            "assignment": "http://identity:35357/v3/domains/161718/users/313233/roles/123456"
+                        },
+                        "role": {
+                            "id": "123456"
+                        },
+                        "scope": {
+                            "domain": {
+                                "id": "161718"
+                            }
+                        },
+                        "user": {
+                            "id": "313233"
+                        }
+                    },
+                    {
+                        "links": {
+                            "assignment": "http://identity:35357/v3/projects/456789/groups/101112/roles/123456",
+                            "membership": "http://identity:35357/v3/groups/101112/users/313233"
+                        },
+                        "role": {
+                            "id": "123456"
+                        },
+                        "scope": {
+                            "project": {
+                                "id": "456789"
+                            }
+                        },
+                        "user": {
+                            "id": "313233"
+                        }
+                    }
+                ],
+                "links": {
+                    "self": "http://identity:35357/v3/role_assignments?effective",
+                    "previous": null,
+                    "next": null
+                }
+            }
+		`)
+	})
+
+	count := 0
+	err := roles.ListAssignments(client.ServiceClient(), roles.ListAssignmentsOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := roles.ExtractRoleAssignments(page)
+		if err != nil {
+			return false, err
+		}
+
+		expected := []roles.RoleAssignment{
+			{
+				Role:  roles.Role{ID: "123456"},
+				Scope: roles.Scope{Domain: roles.Domain{ID: "161718"}},
+				User:  roles.User{ID: "313233"},
+				Group: roles.Group{},
+			},
+			{
+				Role:  roles.Role{ID: "123456"},
+				Scope: roles.Scope{Project: roles.Project{ID: "456789"}},
+				User:  roles.User{ID: "313233"},
+				Group: roles.Group{},
+			},
+		}
+
+		if !reflect.DeepEqual(expected, actual) {
+			t.Errorf("Expected %#v, got %#v", expected, actual)
+		}
+
+		return true, nil
+	})
+	if err != nil {
+		t.Errorf("Unexpected error while paging: %v", err)
+	}
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
diff --git a/openstack/identity/v3/roles/urls.go b/openstack/identity/v3/roles/urls.go
index b009340..8d87b6e 100644
--- a/openstack/identity/v3/roles/urls.go
+++ b/openstack/identity/v3/roles/urls.go
@@ -1,6 +1,6 @@
 package roles
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func listAssignmentsURL(client *gophercloud.ServiceClient) string {
 	return client.ServiceURL("role_assignments")
diff --git a/openstack/identity/v3/roles/urls_test.go b/openstack/identity/v3/roles/urls_test.go
deleted file mode 100644
index 04679da..0000000
--- a/openstack/identity/v3/roles/urls_test.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package roles
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-)
-
-func TestListAssignmentsURL(t *testing.T) {
-	client := gophercloud.ServiceClient{Endpoint: "http://localhost:5000/v3/"}
-	url := listAssignmentsURL(&client)
-	if url != "http://localhost:5000/v3/role_assignments" {
-		t.Errorf("Unexpected list URL generated: [%s]", url)
-	}
-}
diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go
index 3ee924f..bb7bb04 100644
--- a/openstack/identity/v3/services/requests.go
+++ b/openstack/identity/v3/services/requests.go
@@ -1,25 +1,19 @@
 package services
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
-type response struct {
-	Service Service `json:"service"`
+// Create adds a new service of the requested type to the catalog.
+func Create(client *gophercloud.ServiceClient, serviceType string) (r CreateResult) {
+	b := map[string]string{"type": serviceType}
+	_, r.Err = client.Post(listURL(client), b, &r.Body, nil)
+	return
 }
 
-// Create adds a new service of the requested type to the catalog.
-func Create(client *gophercloud.ServiceClient, serviceType string) CreateResult {
-	type request struct {
-		Type string `json:"type"`
-	}
-
-	req := request{Type: serviceType}
-
-	var result CreateResult
-	_, result.Err = client.Post(listURL(client), req, &result.Body, nil)
-	return result
+type ListOptsBuilder interface {
+	ToServiceListMap() (string, error)
 }
 
 // ListOpts allows you to query the List method.
@@ -29,49 +23,42 @@
 	Page        int    `q:"page"`
 }
 
-// List enumerates the services available to a specific user.
-func List(client *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
-	u := listURL(client)
+func (opts ListOpts) ToServiceListMap() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return pagination.Pager{Err: err}
-	}
-	u += q.String()
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return ServicePage{pagination.LinkedPageBase{PageResult: r}}
-	}
+	return q.String(), err
+}
 
-	return pagination.NewPager(client, u, createPage)
+// List enumerates the services available to a specific user.
+func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
+	u := listURL(client)
+	if opts != nil {
+		q, err := opts.ToServiceListMap()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		u += q
+	}
+	return pagination.NewPager(client, u, func(r pagination.PageResult) pagination.Page {
+		return ServicePage{pagination.LinkedPageBase{PageResult: r}}
+	})
 }
 
 // Get returns additional information about a service, given its ID.
-func Get(client *gophercloud.ServiceClient, serviceID string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(serviceURL(client, serviceID), &result.Body, nil)
-	return result
+func Get(client *gophercloud.ServiceClient, serviceID string) (r GetResult) {
+	_, r.Err = client.Get(serviceURL(client, serviceID), &r.Body, nil)
+	return
 }
 
 // Update changes the service type of an existing service.
-func Update(client *gophercloud.ServiceClient, serviceID string, serviceType string) UpdateResult {
-	type request struct {
-		Type string `json:"type"`
-	}
-
-	req := request{Type: serviceType}
-
-	var result UpdateResult
-	_, result.Err = client.Request("PATCH", serviceURL(client, serviceID), gophercloud.RequestOpts{
-		JSONBody:     &req,
-		JSONResponse: &result.Body,
-		OkCodes:      []int{200},
-	})
-	return result
+func Update(client *gophercloud.ServiceClient, serviceID string, serviceType string) (r UpdateResult) {
+	b := map[string]string{"type": serviceType}
+	_, r.Err = client.Patch(serviceURL(client, serviceID), &b, &r.Body, nil)
+	return
 }
 
 // Delete removes an existing service.
 // It either deletes all associated endpoints, or fails until all endpoints are deleted.
-func Delete(client *gophercloud.ServiceClient, serviceID string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(serviceURL(client, serviceID), nil)
-	return res
+func Delete(client *gophercloud.ServiceClient, serviceID string) (r DeleteResult) {
+	_, r.Err = client.Delete(serviceURL(client, serviceID), nil)
+	return
 }
diff --git a/openstack/identity/v3/services/requests_test.go b/openstack/identity/v3/services/requests_test.go
deleted file mode 100644
index 42f05d3..0000000
--- a/openstack/identity/v3/services/requests_test.go
+++ /dev/null
@@ -1,209 +0,0 @@
-package services
-
-import (
-	"fmt"
-	"net/http"
-	"reflect"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestCreateSuccessful(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	testhelper.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "POST")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		testhelper.TestJSONRequest(t, r, `{ "type": "compute" }`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, `{
-        "service": {
-          "description": "Here's your service",
-          "id": "1234",
-          "name": "InscrutableOpenStackProjectName",
-          "type": "compute"
-        }
-    }`)
-	})
-
-	result, err := Create(client.ServiceClient(), "compute").Extract()
-	if err != nil {
-		t.Fatalf("Unexpected error from Create: %v", err)
-	}
-
-	if result.Description == nil || *result.Description != "Here's your service" {
-		t.Errorf("Service description was unexpected [%s]", *result.Description)
-	}
-	if result.ID != "1234" {
-		t.Errorf("Service ID was unexpected [%s]", result.ID)
-	}
-	if result.Name != "InscrutableOpenStackProjectName" {
-		t.Errorf("Service name was unexpected [%s]", result.Name)
-	}
-	if result.Type != "compute" {
-		t.Errorf("Service type was unexpected [%s]", result.Type)
-	}
-}
-
-func TestListSinglePage(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	testhelper.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "GET")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `
-			{
-				"links": {
-					"next": null,
-					"previous": null
-				},
-				"services": [
-					{
-						"description": "Service One",
-						"id": "1234",
-						"name": "service-one",
-						"type": "identity"
-					},
-					{
-						"description": "Service Two",
-						"id": "9876",
-						"name": "service-two",
-						"type": "compute"
-					}
-				]
-			}
-		`)
-	})
-
-	count := 0
-	err := List(client.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractServices(page)
-		if err != nil {
-			return false, err
-		}
-
-		desc0 := "Service One"
-		desc1 := "Service Two"
-		expected := []Service{
-			Service{
-				Description: &desc0,
-				ID:          "1234",
-				Name:        "service-one",
-				Type:        "identity",
-			},
-			Service{
-				Description: &desc1,
-				ID:          "9876",
-				Name:        "service-two",
-				Type:        "compute",
-			},
-		}
-
-		if !reflect.DeepEqual(expected, actual) {
-			t.Errorf("Expected %#v, got %#v", expected, actual)
-		}
-
-		return true, nil
-	})
-	if err != nil {
-		t.Errorf("Unexpected error while paging: %v", err)
-	}
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGetSuccessful(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	testhelper.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "GET")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `
-			{
-				"service": {
-						"description": "Service One",
-						"id": "12345",
-						"name": "service-one",
-						"type": "identity"
-				}
-			}
-		`)
-	})
-
-	result, err := Get(client.ServiceClient(), "12345").Extract()
-	if err != nil {
-		t.Fatalf("Error fetching service information: %v", err)
-	}
-
-	if result.ID != "12345" {
-		t.Errorf("Unexpected service ID: %s", result.ID)
-	}
-	if *result.Description != "Service One" {
-		t.Errorf("Unexpected service description: [%s]", *result.Description)
-	}
-	if result.Name != "service-one" {
-		t.Errorf("Unexpected service name: [%s]", result.Name)
-	}
-	if result.Type != "identity" {
-		t.Errorf("Unexpected service type: [%s]", result.Type)
-	}
-}
-
-func TestUpdateSuccessful(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	testhelper.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "PATCH")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		testhelper.TestJSONRequest(t, r, `{ "type": "lasermagic" }`)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `
-			{
-				"service": {
-						"id": "12345",
-						"type": "lasermagic"
-				}
-			}
-		`)
-	})
-
-	result, err := Update(client.ServiceClient(), "12345", "lasermagic").Extract()
-	if err != nil {
-		t.Fatalf("Unable to update service: %v", err)
-	}
-
-	if result.ID != "12345" {
-		t.Fatalf("Expected ID 12345, was %s", result.ID)
-	}
-}
-
-func TestDeleteSuccessful(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	testhelper.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "DELETE")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(client.ServiceClient(), "12345")
-	testhelper.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go
index 1d0d141..9ebcc20 100644
--- a/openstack/identity/v3/services/results.go
+++ b/openstack/identity/v3/services/results.go
@@ -1,10 +1,8 @@
 package services
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 type commonResult struct {
@@ -14,17 +12,11 @@
 // Extract interprets a GetResult, CreateResult or UpdateResult as a concrete Service.
 // An error is returned if the original call or the extraction failed.
 func (r commonResult) Extract() (*Service, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Service *Service `json:"service"`
 	}
-
-	var res struct {
-		Service `json:"service"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return &res.Service, err
+	err := r.ExtractInto(&s)
+	return s.Service, err
 }
 
 // CreateResult is the deferred result of a Create call.
@@ -49,10 +41,10 @@
 
 // Service is the result of a list or information query.
 type Service struct {
-	Description *string `json:"description,omitempty"`
-	ID          string  `json:"id"`
-	Name        string  `json:"name"`
-	Type        string  `json:"type"`
+	Description string `json:"description`
+	ID          string `json:"id"`
+	Name        string `json:"name"`
+	Type        string `json:"type"`
 }
 
 // ServicePage is a single page of Service results.
@@ -63,18 +55,14 @@
 // IsEmpty returns true if the page contains no results.
 func (p ServicePage) IsEmpty() (bool, error) {
 	services, err := ExtractServices(p)
-	if err != nil {
-		return true, err
-	}
-	return len(services) == 0, nil
+	return len(services) == 0, err
 }
 
 // ExtractServices extracts a slice of Services from a Collection acquired from List.
-func ExtractServices(page pagination.Page) ([]Service, error) {
-	var response struct {
-		Services []Service `mapstructure:"services"`
+func ExtractServices(r pagination.Page) ([]Service, error) {
+	var s struct {
+		Services []Service `json:"services"`
 	}
-
-	err := mapstructure.Decode(page.(ServicePage).Body, &response)
-	return response.Services, err
+	err := (r.(ServicePage)).ExtractInto(&s)
+	return s.Services, err
 }
diff --git a/openstack/identity/v3/services/testing/doc.go b/openstack/identity/v3/services/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/identity/v3/services/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/identity/v3/services/testing/requests_test.go b/openstack/identity/v3/services/testing/requests_test.go
new file mode 100644
index 0000000..0a065a2
--- /dev/null
+++ b/openstack/identity/v3/services/testing/requests_test.go
@@ -0,0 +1,187 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/identity/v3/services"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestCreateSuccessful(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "type": "compute" }`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+		fmt.Fprintf(w, `{
+        "service": {
+          "description": "Here's your service",
+          "id": "1234",
+          "name": "InscrutableOpenStackProjectName",
+          "type": "compute"
+        }
+    }`)
+	})
+
+	expected := &services.Service{
+		Description: "Here's your service",
+		ID:          "1234",
+		Name:        "InscrutableOpenStackProjectName",
+		Type:        "compute",
+	}
+
+	actual, err := services.Create(client.ServiceClient(), "compute").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected error from Create: %v", err)
+	}
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestListSinglePage(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `
+			{
+				"links": {
+					"next": null,
+					"previous": null
+				},
+				"services": [
+					{
+						"description": "Service One",
+						"id": "1234",
+						"name": "service-one",
+						"type": "identity"
+					},
+					{
+						"description": "Service Two",
+						"id": "9876",
+						"name": "service-two",
+						"type": "compute"
+					}
+				]
+			}
+		`)
+	})
+
+	count := 0
+	err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := services.ExtractServices(page)
+		if err != nil {
+			return false, err
+		}
+
+		expected := []services.Service{
+			{
+				Description: "Service One",
+				ID:          "1234",
+				Name:        "service-one",
+				Type:        "identity",
+			},
+			{
+				Description: "Service Two",
+				ID:          "9876",
+				Name:        "service-two",
+				Type:        "compute",
+			},
+		}
+		th.AssertDeepEquals(t, expected, actual)
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, count)
+}
+
+func TestGetSuccessful(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `
+			{
+				"service": {
+						"description": "Service One",
+						"id": "12345",
+						"name": "service-one",
+						"type": "identity"
+				}
+			}
+		`)
+	})
+
+	actual, err := services.Get(client.ServiceClient(), "12345").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &services.Service{
+		ID:          "12345",
+		Description: "Service One",
+		Name:        "service-one",
+		Type:        "identity",
+	}
+
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestUpdateSuccessful(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PATCH")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "type": "lasermagic" }`)
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `
+			{
+				"service": {
+						"id": "12345",
+						"type": "lasermagic"
+				}
+			}
+		`)
+	})
+
+	expected := &services.Service{
+		ID:   "12345",
+		Type: "lasermagic",
+	}
+
+	actual, err := services.Update(client.ServiceClient(), "12345", "lasermagic").Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestDeleteSuccessful(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := services.Delete(client.ServiceClient(), "12345")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/identity/v3/services/urls.go b/openstack/identity/v3/services/urls.go
index 85443a4..c5ae268 100644
--- a/openstack/identity/v3/services/urls.go
+++ b/openstack/identity/v3/services/urls.go
@@ -1,6 +1,6 @@
 package services
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func listURL(client *gophercloud.ServiceClient) string {
 	return client.ServiceURL("services")
diff --git a/openstack/identity/v3/services/urls_test.go b/openstack/identity/v3/services/urls_test.go
deleted file mode 100644
index 5a31b32..0000000
--- a/openstack/identity/v3/services/urls_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package services
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-)
-
-func TestListURL(t *testing.T) {
-	client := gophercloud.ServiceClient{Endpoint: "http://localhost:5000/v3/"}
-	url := listURL(&client)
-	if url != "http://localhost:5000/v3/services" {
-		t.Errorf("Unexpected list URL generated: [%s]", url)
-	}
-}
-
-func TestServiceURL(t *testing.T) {
-	client := gophercloud.ServiceClient{Endpoint: "http://localhost:5000/v3/"}
-	url := serviceURL(&client, "1234")
-	if url != "http://localhost:5000/v3/services/1234" {
-		t.Errorf("Unexpected service URL generated: [%s]", url)
-	}
-}
diff --git a/openstack/identity/v3/tokens/errors.go b/openstack/identity/v3/tokens/errors.go
index 4476109..9cc1d59 100644
--- a/openstack/identity/v3/tokens/errors.go
+++ b/openstack/identity/v3/tokens/errors.go
@@ -1,72 +1,139 @@
 package tokens
 
 import (
-	"errors"
 	"fmt"
+
+	"github.com/gophercloud/gophercloud"
 )
 
-func unacceptedAttributeErr(attribute string) error {
-	return fmt.Errorf("The base Identity V3 API does not accept authentication by %s", attribute)
+func unacceptedAttributeErr(attribute string) string {
+	return fmt.Sprintf("The base Identity V3 API does not accept authentication by %s", attribute)
 }
 
-func redundantWithTokenErr(attribute string) error {
-	return fmt.Errorf("%s may not be provided when authenticating with a TokenID", attribute)
+func redundantWithTokenErr(attribute string) string {
+	return fmt.Sprintf("%s may not be provided when authenticating with a TokenID", attribute)
 }
 
-func redundantWithUserID(attribute string) error {
-	return fmt.Errorf("%s may not be provided when authenticating with a UserID", attribute)
+func redundantWithUserID(attribute string) string {
+	return fmt.Sprintf("%s may not be provided when authenticating with a UserID", attribute)
 }
 
-var (
-	// ErrAPIKeyProvided indicates that an APIKey was provided but can't be used.
-	ErrAPIKeyProvided = unacceptedAttributeErr("APIKey")
+// ErrAPIKeyProvided indicates that an APIKey was provided but can't be used.
+type ErrAPIKeyProvided struct{ gophercloud.BaseError }
 
-	// ErrTenantIDProvided indicates that a TenantID was provided but can't be used.
-	ErrTenantIDProvided = unacceptedAttributeErr("TenantID")
+func (e ErrAPIKeyProvided) Error() string {
+	return unacceptedAttributeErr("APIKey")
+}
 
-	// ErrTenantNameProvided indicates that a TenantName was provided but can't be used.
-	ErrTenantNameProvided = unacceptedAttributeErr("TenantName")
+// ErrTenantIDProvided indicates that a TenantID was provided but can't be used.
+type ErrTenantIDProvided struct{ gophercloud.BaseError }
 
-	// ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead.
-	ErrUsernameWithToken = redundantWithTokenErr("Username")
+func (e ErrTenantIDProvided) Error() string {
+	return unacceptedAttributeErr("TenantID")
+}
 
-	// ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead.
-	ErrUserIDWithToken = redundantWithTokenErr("UserID")
+// ErrTenantNameProvided indicates that a TenantName was provided but can't be used.
+type ErrTenantNameProvided struct{ gophercloud.BaseError }
 
-	// ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead.
-	ErrDomainIDWithToken = redundantWithTokenErr("DomainID")
+func (e ErrTenantNameProvided) Error() string {
+	return unacceptedAttributeErr("TenantName")
+}
 
-	// ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s
-	ErrDomainNameWithToken = redundantWithTokenErr("DomainName")
+// ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead.
+type ErrUsernameWithToken struct{ gophercloud.BaseError }
 
-	// ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once.
-	ErrUsernameOrUserID = errors.New("Exactly one of Username and UserID must be provided for password authentication")
+func (e ErrUsernameWithToken) Error() string {
+	return redundantWithTokenErr("Username")
+}
 
-	// ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used.
-	ErrDomainIDWithUserID = redundantWithUserID("DomainID")
+// ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead.
+type ErrUserIDWithToken struct{ gophercloud.BaseError }
 
-	// ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used.
-	ErrDomainNameWithUserID = redundantWithUserID("DomainName")
+func (e ErrUserIDWithToken) Error() string {
+	return redundantWithTokenErr("UserID")
+}
 
-	// ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it.
-	// It may also indicate that both a DomainID and a DomainName were provided at once.
-	ErrDomainIDOrDomainName = errors.New("You must provide exactly one of DomainID or DomainName to authenticate by Username")
+// ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead.
+type ErrDomainIDWithToken struct{ gophercloud.BaseError }
 
-	// ErrMissingPassword indicates that no password was provided and no token is available.
-	ErrMissingPassword = errors.New("You must provide a password to authenticate")
+func (e ErrDomainIDWithToken) Error() string {
+	return redundantWithTokenErr("DomainID")
+}
 
-	// ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present.
-	ErrScopeDomainIDOrDomainName = errors.New("You must provide exactly one of DomainID or DomainName in a Scope with ProjectName")
+// ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s
+type ErrDomainNameWithToken struct{ gophercloud.BaseError }
 
-	// ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope.
-	ErrScopeProjectIDOrProjectName = errors.New("You must provide at most one of ProjectID or ProjectName in a Scope")
+func (e ErrDomainNameWithToken) Error() string {
+	return redundantWithTokenErr("DomainName")
+}
 
-	// ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope.
-	ErrScopeProjectIDAlone = errors.New("ProjectID must be supplied alone in a Scope")
+// ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once.
+type ErrUsernameOrUserID struct{ gophercloud.BaseError }
 
-	// ErrScopeDomainName indicates that a DomainName was provided alone in a Scope.
-	ErrScopeDomainName = errors.New("DomainName must be supplied with a ProjectName or ProjectID in a Scope.")
+func (e ErrUsernameOrUserID) Error() string {
+	return "Exactly one of Username and UserID must be provided for password authentication"
+}
 
-	// ErrScopeEmpty indicates that no credentials were provided in a Scope.
-	ErrScopeEmpty = errors.New("You must provide either a Project or Domain in a Scope")
-)
+// ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used.
+type ErrDomainIDWithUserID struct{ gophercloud.BaseError }
+
+func (e ErrDomainIDWithUserID) Error() string {
+	return redundantWithUserID("DomainID")
+}
+
+// ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used.
+type ErrDomainNameWithUserID struct{ gophercloud.BaseError }
+
+func (e ErrDomainNameWithUserID) Error() string {
+	return redundantWithUserID("DomainName")
+}
+
+// ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it.
+// It may also indicate that both a DomainID and a DomainName were provided at once.
+type ErrDomainIDOrDomainName struct{ gophercloud.BaseError }
+
+func (e ErrDomainIDOrDomainName) Error() string {
+	return "You must provide exactly one of DomainID or DomainName to authenticate by Username"
+}
+
+// ErrMissingPassword indicates that no password was provided and no token is available.
+type ErrMissingPassword struct{ gophercloud.BaseError }
+
+func (e ErrMissingPassword) Error() string {
+	return "You must provide a password to authenticate"
+}
+
+// ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present.
+type ErrScopeDomainIDOrDomainName struct{ gophercloud.BaseError }
+
+func (e ErrScopeDomainIDOrDomainName) Error() string {
+	return "You must provide exactly one of DomainID or DomainName in a Scope with ProjectName"
+}
+
+// ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope.
+type ErrScopeProjectIDOrProjectName struct{ gophercloud.BaseError }
+
+func (e ErrScopeProjectIDOrProjectName) Error() string {
+	return "You must provide at most one of ProjectID or ProjectName in a Scope"
+}
+
+// ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope.
+type ErrScopeProjectIDAlone struct{ gophercloud.BaseError }
+
+func (e ErrScopeProjectIDAlone) Error() string {
+	return "ProjectID must be supplied alone in a Scope"
+}
+
+// ErrScopeDomainName indicates that a DomainName was provided alone in a Scope.
+type ErrScopeDomainName struct{ gophercloud.BaseError }
+
+func (e ErrScopeDomainName) Error() string {
+	return "DomainName must be supplied with a ProjectName or ProjectID in a Scope"
+}
+
+// ErrScopeEmpty indicates that no credentials were provided in a Scope.
+type ErrScopeEmpty struct{ gophercloud.BaseError }
+
+func (e ErrScopeEmpty) Error() string {
+	return "You must provide either a Project or Domain in a Scope"
+}
diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go
index d63b1bb..856c363 100644
--- a/openstack/identity/v3/tokens/requests.go
+++ b/openstack/identity/v3/tokens/requests.go
@@ -1,27 +1,62 @@
 package tokens
 
-import (
-	"net/http"
-
-	"github.com/rackspace/gophercloud"
-)
+import "github.com/gophercloud/gophercloud"
 
 // Scope allows a created token to be limited to a specific domain or project.
 type Scope struct {
-	ProjectID   string
-	ProjectName string
-	DomainID    string
-	DomainName  string
+	ProjectID   string `json:"scope.project.id,omitempty" not:"ProjectName,DomainID,DomainName"`
+	ProjectName string `json:"scope.project.name,omitempty"`
+	DomainID    string `json:"scope.project.id,omitempty" not:"ProjectName,ProjectID,DomainName"`
+	DomainName  string `json:"scope.project.id,omitempty"`
 }
 
-func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string {
-	return map[string]string{
-		"X-Subject-Token": subjectToken,
-	}
+// AuthOptionsBuilder describes any argument that may be passed to the Create call.
+type AuthOptionsBuilder interface {
+	// ToTokenV3CreateMap assembles the Create request body, returning an error if parameters are
+	// missing or inconsistent.
+	ToTokenV3CreateMap(*Scope) (map[string]interface{}, error)
 }
 
-// Create authenticates and either generates a new token, or changes the Scope of an existing token.
-func Create(c *gophercloud.ServiceClient, options gophercloud.AuthOptions, scope *Scope) CreateResult {
+type AuthOptions struct {
+	// IdentityEndpoint specifies the HTTP endpoint that is required to work with
+	// the Identity API of the appropriate version. While it's ultimately needed by
+	// all of the identity services, it will often be populated by a provider-level
+	// function.
+	IdentityEndpoint string `json:"-"`
+
+	// Username is required if using Identity V2 API. Consult with your provider's
+	// control panel to discover your account's username. In Identity V3, either
+	// UserID or a combination of Username and DomainID or DomainName are needed.
+	Username string `json:"username,omitempty"`
+	UserID   string `json:"id,omitempty"`
+
+	Password string `json:"password,omitempty"`
+
+	// At most one of DomainID and DomainName must be provided if using Username
+	// with Identity V3. Otherwise, either are optional.
+	DomainID   string `json:"id,omitempty"`
+	DomainName string `json:"name,omitempty"`
+
+	// The TenantID and TenantName fields are optional for the Identity V2 API.
+	// Some providers allow you to specify a TenantName instead of the TenantId.
+	// Some require both. Your provider's authentication policies will determine
+	// how these fields influence authentication.
+	TenantID   string `json:"tenantId,omitempty"`
+	TenantName string `json:"tenantName,omitempty"`
+
+	// AllowReauth should be set to true if you grant permission for Gophercloud to
+	// cache your credentials in memory, and to allow Gophercloud to attempt to
+	// re-authenticate automatically if/when your token expires.  If you set it to
+	// false, it will not cache these settings, but re-authentication will not be
+	// possible.  This setting defaults to false.
+	AllowReauth bool `json:"-"`
+
+	// TokenID allows users to authenticate (possibly as another user) with an
+	// authentication token ID.
+	TokenID string
+}
+
+func (opts AuthOptions) ToTokenV3CreateMap(scope *Scope) (map[string]interface{}, error) {
 	type domainReq struct {
 		ID   *string `json:"id,omitempty"`
 		Name *string `json:"name,omitempty"`
@@ -73,101 +108,98 @@
 	var req request
 
 	// Test first for unrecognized arguments.
-	if options.APIKey != "" {
-		return createErr(ErrAPIKeyProvided)
+	if opts.TenantID != "" {
+		return nil, ErrTenantIDProvided{}
 	}
-	if options.TenantID != "" {
-		return createErr(ErrTenantIDProvided)
-	}
-	if options.TenantName != "" {
-		return createErr(ErrTenantNameProvided)
+	if opts.TenantName != "" {
+		return nil, ErrTenantNameProvided{}
 	}
 
-	if options.Password == "" {
-		if c.TokenID != "" {
+	if opts.Password == "" {
+		if opts.TokenID != "" {
 			// Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
 			// parameters.
-			if options.Username != "" {
-				return createErr(ErrUsernameWithToken)
+			if opts.Username != "" {
+				return nil, ErrUsernameWithToken{}
 			}
-			if options.UserID != "" {
-				return createErr(ErrUserIDWithToken)
+			if opts.UserID != "" {
+				return nil, ErrUserIDWithToken{}
 			}
-			if options.DomainID != "" {
-				return createErr(ErrDomainIDWithToken)
+			if opts.DomainID != "" {
+				return nil, ErrDomainIDWithToken{}
 			}
-			if options.DomainName != "" {
-				return createErr(ErrDomainNameWithToken)
+			if opts.DomainName != "" {
+				return nil, ErrDomainNameWithToken{}
 			}
 
 			// Configure the request for Token authentication.
 			req.Auth.Identity.Methods = []string{"token"}
 			req.Auth.Identity.Token = &tokenReq{
-				ID: c.TokenID,
+				ID: opts.TokenID,
 			}
 		} else {
 			// If no password or token ID are available, authentication can't continue.
-			return createErr(ErrMissingPassword)
+			return nil, ErrMissingPassword{}
 		}
 	} else {
 		// Password authentication.
 		req.Auth.Identity.Methods = []string{"password"}
 
 		// At least one of Username and UserID must be specified.
-		if options.Username == "" && options.UserID == "" {
-			return createErr(ErrUsernameOrUserID)
+		if opts.Username == "" && opts.UserID == "" {
+			return nil, ErrUsernameOrUserID{}
 		}
 
-		if options.Username != "" {
+		if opts.Username != "" {
 			// If Username is provided, UserID may not be provided.
-			if options.UserID != "" {
-				return createErr(ErrUsernameOrUserID)
+			if opts.UserID != "" {
+				return nil, ErrUsernameOrUserID{}
 			}
 
 			// Either DomainID or DomainName must also be specified.
-			if options.DomainID == "" && options.DomainName == "" {
-				return createErr(ErrDomainIDOrDomainName)
+			if opts.DomainID == "" && opts.DomainName == "" {
+				return nil, ErrDomainIDOrDomainName{}
 			}
 
-			if options.DomainID != "" {
-				if options.DomainName != "" {
-					return createErr(ErrDomainIDOrDomainName)
+			if opts.DomainID != "" {
+				if opts.DomainName != "" {
+					return nil, ErrDomainIDOrDomainName{}
 				}
 
 				// Configure the request for Username and Password authentication with a DomainID.
 				req.Auth.Identity.Password = &passwordReq{
 					User: userReq{
-						Name:     &options.Username,
-						Password: options.Password,
-						Domain:   &domainReq{ID: &options.DomainID},
+						Name:     &opts.Username,
+						Password: opts.Password,
+						Domain:   &domainReq{ID: &opts.DomainID},
 					},
 				}
 			}
 
-			if options.DomainName != "" {
+			if opts.DomainName != "" {
 				// Configure the request for Username and Password authentication with a DomainName.
 				req.Auth.Identity.Password = &passwordReq{
 					User: userReq{
-						Name:     &options.Username,
-						Password: options.Password,
-						Domain:   &domainReq{Name: &options.DomainName},
+						Name:     &opts.Username,
+						Password: opts.Password,
+						Domain:   &domainReq{Name: &opts.DomainName},
 					},
 				}
 			}
 		}
 
-		if options.UserID != "" {
+		if opts.UserID != "" {
 			// If UserID is specified, neither DomainID nor DomainName may be.
-			if options.DomainID != "" {
-				return createErr(ErrDomainIDWithUserID)
+			if opts.DomainID != "" {
+				return nil, ErrDomainIDWithUserID{}
 			}
-			if options.DomainName != "" {
-				return createErr(ErrDomainNameWithUserID)
+			if opts.DomainName != "" {
+				return nil, ErrDomainNameWithUserID{}
 			}
 
 			// Configure the request for UserID and Password authentication.
 			req.Auth.Identity.Password = &passwordReq{
-				User: userReq{ID: &options.UserID, Password: options.Password},
+				User: userReq{ID: &opts.UserID, Password: opts.Password},
 			}
 		}
 	}
@@ -178,10 +210,10 @@
 			// ProjectName provided: either DomainID or DomainName must also be supplied.
 			// ProjectID may not be supplied.
 			if scope.DomainID == "" && scope.DomainName == "" {
-				return createErr(ErrScopeDomainIDOrDomainName)
+				return nil, ErrScopeDomainIDOrDomainName{}
 			}
 			if scope.ProjectID != "" {
-				return createErr(ErrScopeProjectIDOrProjectName)
+				return nil, ErrScopeProjectIDOrProjectName{}
 			}
 
 			if scope.DomainID != "" {
@@ -206,10 +238,10 @@
 		} else if scope.ProjectID != "" {
 			// ProjectID provided. ProjectName, DomainID, and DomainName may not be provided.
 			if scope.DomainID != "" {
-				return createErr(ErrScopeProjectIDAlone)
+				return nil, ErrScopeProjectIDAlone{}
 			}
 			if scope.DomainName != "" {
-				return createErr(ErrScopeProjectIDAlone)
+				return nil, ErrScopeProjectIDAlone{}
 			}
 
 			// ProjectID
@@ -219,7 +251,7 @@
 		} else if scope.DomainID != "" {
 			// DomainID provided. ProjectID, ProjectName, and DomainName may not be provided.
 			if scope.DomainName != "" {
-				return createErr(ErrScopeDomainIDOrDomainName)
+				return nil, ErrScopeDomainIDOrDomainName{}
 			}
 
 			// DomainID
@@ -227,40 +259,58 @@
 				Domain: &domainReq{ID: &scope.DomainID},
 			}
 		} else if scope.DomainName != "" {
-			return createErr(ErrScopeDomainName)
+			return nil, ErrScopeDomainName{}
 		} else {
-			return createErr(ErrScopeEmpty)
+			return nil, ErrScopeEmpty{}
 		}
 	}
 
-	var result CreateResult
-	var response *http.Response
-	response, result.Err = c.Post(tokenURL(c), req, &result.Body, nil)
-	if result.Err != nil {
-		return result
+	b, err2 := gophercloud.BuildRequestBody(req, "")
+	if err2 != nil {
+		return nil, err2
 	}
-	result.Header = response.Header
-	return result
+	return b, nil
+}
+
+func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string {
+	return map[string]string{
+		"X-Subject-Token": subjectToken,
+	}
+}
+
+// Create authenticates and either generates a new token, or changes the Scope of an existing token.
+func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder, scopeOpts *Scope) (r CreateResult) {
+	b, err := opts.ToTokenV3CreateMap(scopeOpts)
+	if err != nil {
+		r.Err = err
+		return
+	}
+	resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{
+		MoreHeaders: map[string]string{"X-Auth-Token": ""},
+	})
+	if resp != nil {
+		r.Err = err
+		r.Header = resp.Header
+	}
+	return
 }
 
 // Get validates and retrieves information about another token.
-func Get(c *gophercloud.ServiceClient, token string) GetResult {
-	var result GetResult
-	var response *http.Response
-	response, result.Err = c.Get(tokenURL(c), &result.Body, &gophercloud.RequestOpts{
+func Get(c *gophercloud.ServiceClient, token string) (r GetResult) {
+	resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{
 		MoreHeaders: subjectTokenHeaders(c, token),
 		OkCodes:     []int{200, 203},
 	})
-	if result.Err != nil {
-		return result
+	if resp != nil {
+		r.Err = err
+		r.Header = resp.Header
 	}
-	result.Header = response.Header
-	return result
+	return
 }
 
 // Validate determines if a specified token is valid or not.
 func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
-	response, err := c.Request("HEAD", tokenURL(c), gophercloud.RequestOpts{
+	resp, err := c.Request("HEAD", tokenURL(c), &gophercloud.RequestOpts{
 		MoreHeaders: subjectTokenHeaders(c, token),
 		OkCodes:     []int{204, 404},
 	})
@@ -268,14 +318,13 @@
 		return false, err
 	}
 
-	return response.StatusCode == 204, nil
+	return resp.StatusCode == 204, nil
 }
 
 // Revoke immediately makes specified token invalid.
-func Revoke(c *gophercloud.ServiceClient, token string) RevokeResult {
-	var res RevokeResult
-	_, res.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{
+func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) {
+	_, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{
 		MoreHeaders: subjectTokenHeaders(c, token),
 	})
-	return res
+	return
 }
diff --git a/openstack/identity/v3/tokens/requests_test.go b/openstack/identity/v3/tokens/requests_test.go
deleted file mode 100644
index 2b26e4a..0000000
--- a/openstack/identity/v3/tokens/requests_test.go
+++ /dev/null
@@ -1,514 +0,0 @@
-package tokens
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/testhelper"
-)
-
-// authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure.
-func authTokenPost(t *testing.T, options gophercloud.AuthOptions, scope *Scope, requestJSON string) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	client := gophercloud.ServiceClient{
-		ProviderClient: &gophercloud.ProviderClient{
-			TokenID: "12345abcdef",
-		},
-		Endpoint: testhelper.Endpoint(),
-	}
-
-	testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "POST")
-		testhelper.TestHeader(t, r, "Content-Type", "application/json")
-		testhelper.TestHeader(t, r, "Accept", "application/json")
-		testhelper.TestJSONRequest(t, r, requestJSON)
-
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, `{
-			"token": {
-				"expires_at": "2014-10-02T13:45:00.000000Z"
-			}
-		}`)
-	})
-
-	_, err := Create(&client, options, scope).Extract()
-	if err != nil {
-		t.Errorf("Create returned an error: %v", err)
-	}
-}
-
-func authTokenPostErr(t *testing.T, options gophercloud.AuthOptions, scope *Scope, includeToken bool, expectedErr error) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	client := gophercloud.ServiceClient{
-		ProviderClient: &gophercloud.ProviderClient{},
-		Endpoint:       testhelper.Endpoint(),
-	}
-	if includeToken {
-		client.TokenID = "abcdef123456"
-	}
-
-	_, err := Create(&client, options, scope).Extract()
-	if err == nil {
-		t.Errorf("Create did NOT return an error")
-	}
-	if err != expectedErr {
-		t.Errorf("Create returned an unexpected error: wanted %v, got %v", expectedErr, err)
-	}
-}
-
-func TestCreateUserIDAndPassword(t *testing.T) {
-	authTokenPost(t, gophercloud.AuthOptions{UserID: "me", Password: "squirrel!"}, nil, `
-		{
-			"auth": {
-				"identity": {
-					"methods": ["password"],
-					"password": {
-						"user": { "id": "me", "password": "squirrel!" }
-					}
-				}
-			}
-		}
-	`)
-}
-
-func TestCreateUsernameDomainIDPassword(t *testing.T) {
-	authTokenPost(t, gophercloud.AuthOptions{Username: "fakey", Password: "notpassword", DomainID: "abc123"}, nil, `
-		{
-			"auth": {
-				"identity": {
-					"methods": ["password"],
-					"password": {
-						"user": {
-							"domain": {
-								"id": "abc123"
-							},
-							"name": "fakey",
-							"password": "notpassword"
-						}
-					}
-				}
-			}
-		}
-	`)
-}
-
-func TestCreateUsernameDomainNamePassword(t *testing.T) {
-	authTokenPost(t, gophercloud.AuthOptions{Username: "frank", Password: "swordfish", DomainName: "spork.net"}, nil, `
-		{
-			"auth": {
-				"identity": {
-					"methods": ["password"],
-					"password": {
-						"user": {
-							"domain": {
-								"name": "spork.net"
-							},
-							"name": "frank",
-							"password": "swordfish"
-						}
-					}
-				}
-			}
-		}
-	`)
-}
-
-func TestCreateTokenID(t *testing.T) {
-	authTokenPost(t, gophercloud.AuthOptions{}, nil, `
-		{
-			"auth": {
-				"identity": {
-					"methods": ["token"],
-					"token": {
-						"id": "12345abcdef"
-					}
-				}
-			}
-		}
-	`)
-}
-
-func TestCreateProjectIDScope(t *testing.T) {
-	options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
-	scope := &Scope{ProjectID: "123456"}
-	authTokenPost(t, options, scope, `
-		{
-			"auth": {
-				"identity": {
-					"methods": ["password"],
-					"password": {
-						"user": {
-							"id": "fenris",
-							"password": "g0t0h311"
-						}
-					}
-				},
-				"scope": {
-					"project": {
-						"id": "123456"
-					}
-				}
-			}
-		}
-	`)
-}
-
-func TestCreateDomainIDScope(t *testing.T) {
-	options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
-	scope := &Scope{DomainID: "1000"}
-	authTokenPost(t, options, scope, `
-		{
-			"auth": {
-				"identity": {
-					"methods": ["password"],
-					"password": {
-						"user": {
-							"id": "fenris",
-							"password": "g0t0h311"
-						}
-					}
-				},
-				"scope": {
-					"domain": {
-						"id": "1000"
-					}
-				}
-			}
-		}
-	`)
-}
-
-func TestCreateProjectNameAndDomainIDScope(t *testing.T) {
-	options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
-	scope := &Scope{ProjectName: "world-domination", DomainID: "1000"}
-	authTokenPost(t, options, scope, `
-		{
-			"auth": {
-				"identity": {
-					"methods": ["password"],
-					"password": {
-						"user": {
-							"id": "fenris",
-							"password": "g0t0h311"
-						}
-					}
-				},
-				"scope": {
-					"project": {
-						"domain": {
-							"id": "1000"
-						},
-						"name": "world-domination"
-					}
-				}
-			}
-		}
-	`)
-}
-
-func TestCreateProjectNameAndDomainNameScope(t *testing.T) {
-	options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
-	scope := &Scope{ProjectName: "world-domination", DomainName: "evil-plans"}
-	authTokenPost(t, options, scope, `
-		{
-			"auth": {
-				"identity": {
-					"methods": ["password"],
-					"password": {
-						"user": {
-							"id": "fenris",
-							"password": "g0t0h311"
-						}
-					}
-				},
-				"scope": {
-					"project": {
-						"domain": {
-							"name": "evil-plans"
-						},
-						"name": "world-domination"
-					}
-				}
-			}
-		}
-	`)
-}
-
-func TestCreateExtractsTokenFromResponse(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	client := gophercloud.ServiceClient{
-		ProviderClient: &gophercloud.ProviderClient{},
-		Endpoint:       testhelper.Endpoint(),
-	}
-
-	testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Add("X-Subject-Token", "aaa111")
-
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, `{
-			"token": {
-				"expires_at": "2014-10-02T13:45:00.000000Z"
-			}
-		}`)
-	})
-
-	options := gophercloud.AuthOptions{UserID: "me", Password: "shhh"}
-	token, err := Create(&client, options, nil).Extract()
-	if err != nil {
-		t.Fatalf("Create returned an error: %v", err)
-	}
-
-	if token.ID != "aaa111" {
-		t.Errorf("Expected token to be aaa111, but was %s", token.ID)
-	}
-}
-
-func TestCreateFailureEmptyAuth(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{}, nil, false, ErrMissingPassword)
-}
-
-func TestCreateFailureAPIKey(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{APIKey: "something"}, nil, false, ErrAPIKeyProvided)
-}
-
-func TestCreateFailureTenantID(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{TenantID: "something"}, nil, false, ErrTenantIDProvided)
-}
-
-func TestCreateFailureTenantName(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{TenantName: "something"}, nil, false, ErrTenantNameProvided)
-}
-
-func TestCreateFailureTokenIDUsername(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{Username: "something"}, nil, true, ErrUsernameWithToken)
-}
-
-func TestCreateFailureTokenIDUserID(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{UserID: "something"}, nil, true, ErrUserIDWithToken)
-}
-
-func TestCreateFailureTokenIDDomainID(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{DomainID: "something"}, nil, true, ErrDomainIDWithToken)
-}
-
-func TestCreateFailureTokenIDDomainName(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{DomainName: "something"}, nil, true, ErrDomainNameWithToken)
-}
-
-func TestCreateFailureMissingUser(t *testing.T) {
-	options := gophercloud.AuthOptions{Password: "supersecure"}
-	authTokenPostErr(t, options, nil, false, ErrUsernameOrUserID)
-}
-
-func TestCreateFailureBothUser(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Password: "supersecure",
-		Username: "oops",
-		UserID:   "redundancy",
-	}
-	authTokenPostErr(t, options, nil, false, ErrUsernameOrUserID)
-}
-
-func TestCreateFailureMissingDomain(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Password: "supersecure",
-		Username: "notuniqueenough",
-	}
-	authTokenPostErr(t, options, nil, false, ErrDomainIDOrDomainName)
-}
-
-func TestCreateFailureBothDomain(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Password:   "supersecure",
-		Username:   "someone",
-		DomainID:   "hurf",
-		DomainName: "durf",
-	}
-	authTokenPostErr(t, options, nil, false, ErrDomainIDOrDomainName)
-}
-
-func TestCreateFailureUserIDDomainID(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		UserID:   "100",
-		Password: "stuff",
-		DomainID: "oops",
-	}
-	authTokenPostErr(t, options, nil, false, ErrDomainIDWithUserID)
-}
-
-func TestCreateFailureUserIDDomainName(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		UserID:     "100",
-		Password:   "sssh",
-		DomainName: "oops",
-	}
-	authTokenPostErr(t, options, nil, false, ErrDomainNameWithUserID)
-}
-
-func TestCreateFailureScopeProjectNameAlone(t *testing.T) {
-	options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
-	scope := &Scope{ProjectName: "notenough"}
-	authTokenPostErr(t, options, scope, false, ErrScopeDomainIDOrDomainName)
-}
-
-func TestCreateFailureScopeProjectNameAndID(t *testing.T) {
-	options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
-	scope := &Scope{ProjectName: "whoops", ProjectID: "toomuch", DomainID: "1234"}
-	authTokenPostErr(t, options, scope, false, ErrScopeProjectIDOrProjectName)
-}
-
-func TestCreateFailureScopeProjectIDAndDomainID(t *testing.T) {
-	options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
-	scope := &Scope{ProjectID: "toomuch", DomainID: "notneeded"}
-	authTokenPostErr(t, options, scope, false, ErrScopeProjectIDAlone)
-}
-
-func TestCreateFailureScopeProjectIDAndDomainNAme(t *testing.T) {
-	options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
-	scope := &Scope{ProjectID: "toomuch", DomainName: "notneeded"}
-	authTokenPostErr(t, options, scope, false, ErrScopeProjectIDAlone)
-}
-
-func TestCreateFailureScopeDomainIDAndDomainName(t *testing.T) {
-	options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
-	scope := &Scope{DomainID: "toomuch", DomainName: "notneeded"}
-	authTokenPostErr(t, options, scope, false, ErrScopeDomainIDOrDomainName)
-}
-
-func TestCreateFailureScopeDomainNameAlone(t *testing.T) {
-	options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
-	scope := &Scope{DomainName: "notenough"}
-	authTokenPostErr(t, options, scope, false, ErrScopeDomainName)
-}
-
-func TestCreateFailureEmptyScope(t *testing.T) {
-	options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
-	scope := &Scope{}
-	authTokenPostErr(t, options, scope, false, ErrScopeEmpty)
-}
-
-func TestGetRequest(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	client := gophercloud.ServiceClient{
-		ProviderClient: &gophercloud.ProviderClient{
-			TokenID: "12345abcdef",
-		},
-		Endpoint: testhelper.Endpoint(),
-	}
-
-	testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "GET")
-		testhelper.TestHeader(t, r, "Content-Type", "")
-		testhelper.TestHeader(t, r, "Accept", "application/json")
-		testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef")
-		testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345")
-
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-			{ "token": { "expires_at": "2014-08-29T13:10:01.000000Z" } }
-		`)
-	})
-
-	token, err := Get(&client, "abcdef12345").Extract()
-	if err != nil {
-		t.Errorf("Info returned an error: %v", err)
-	}
-
-	expected, _ := time.Parse(time.UnixDate, "Fri Aug 29 13:10:01 UTC 2014")
-	if token.ExpiresAt != expected {
-		t.Errorf("Expected expiration time %s, but was %s", expected.Format(time.UnixDate), token.ExpiresAt.Format(time.UnixDate))
-	}
-}
-
-func prepareAuthTokenHandler(t *testing.T, expectedMethod string, status int) gophercloud.ServiceClient {
-	client := gophercloud.ServiceClient{
-		ProviderClient: &gophercloud.ProviderClient{
-			TokenID: "12345abcdef",
-		},
-		Endpoint: testhelper.Endpoint(),
-	}
-
-	testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, expectedMethod)
-		testhelper.TestHeader(t, r, "Content-Type", "")
-		testhelper.TestHeader(t, r, "Accept", "application/json")
-		testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef")
-		testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345")
-
-		w.WriteHeader(status)
-	})
-
-	return client
-}
-
-func TestValidateRequestSuccessful(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-	client := prepareAuthTokenHandler(t, "HEAD", http.StatusNoContent)
-
-	ok, err := Validate(&client, "abcdef12345")
-	if err != nil {
-		t.Errorf("Unexpected error from Validate: %v", err)
-	}
-
-	if !ok {
-		t.Errorf("Validate returned false for a valid token")
-	}
-}
-
-func TestValidateRequestFailure(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-	client := prepareAuthTokenHandler(t, "HEAD", http.StatusNotFound)
-
-	ok, err := Validate(&client, "abcdef12345")
-	if err != nil {
-		t.Errorf("Unexpected error from Validate: %v", err)
-	}
-
-	if ok {
-		t.Errorf("Validate returned true for an invalid token")
-	}
-}
-
-func TestValidateRequestError(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-	client := prepareAuthTokenHandler(t, "HEAD", http.StatusUnauthorized)
-
-	_, err := Validate(&client, "abcdef12345")
-	if err == nil {
-		t.Errorf("Missing expected error from Validate")
-	}
-}
-
-func TestRevokeRequestSuccessful(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-	client := prepareAuthTokenHandler(t, "DELETE", http.StatusNoContent)
-
-	res := Revoke(&client, "abcdef12345")
-	testhelper.AssertNoErr(t, res.Err)
-}
-
-func TestRevokeRequestError(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-	client := prepareAuthTokenHandler(t, "DELETE", http.StatusNotFound)
-
-	res := Revoke(&client, "abcdef12345")
-	if res.Err == nil {
-		t.Errorf("Missing expected error from Revoke")
-	}
-}
diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go
index d134f7d..7dd2c0b 100644
--- a/openstack/identity/v3/tokens/results.go
+++ b/openstack/identity/v3/tokens/results.go
@@ -3,8 +3,7 @@
 import (
 	"time"
 
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 // Endpoint represents a single API endpoint offered by a service.
@@ -12,10 +11,10 @@
 // If supported, it contains a region specifier, again if provided.
 // The significance of the Region field will depend upon your provider.
 type Endpoint struct {
-	ID        string `mapstructure:"id"`
-	Region    string `mapstructure:"region"`
-	Interface string `mapstructure:"interface"`
-	URL       string `mapstructure:"url"`
+	ID        string `json:"id"`
+	Region    string `json:"region"`
+	Interface string `json:"interface"`
+	URL       string `json:"url"`
 }
 
 // CatalogEntry provides a type-safe interface to an Identity API V3 service catalog listing.
@@ -27,18 +26,18 @@
 type CatalogEntry struct {
 
 	// Service ID
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Name will contain the provider-specified name for the service.
-	Name string `mapstructure:"name"`
+	Name string `json:"name"`
 
 	// Type will contain a type string if OpenStack defines a type for the service.
 	// Otherwise, for provider-specific services, the provider may assign their own type strings.
-	Type string `mapstructure:"type"`
+	Type string `json:"type"`
 
 	// Endpoints will let the caller iterate over all the different endpoints that may exist for
 	// the service.
-	Endpoints []Endpoint `mapstructure:"endpoints"`
+	Endpoints []Endpoint `json:"endpoints"`
 }
 
 // ServiceCatalog provides a view into the service catalog from a previous, successful authentication.
@@ -59,14 +58,10 @@
 
 // ExtractToken interprets a commonResult as a Token.
 func (r commonResult) ExtractToken() (*Token, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
+	var s struct {
 		Token struct {
-			ExpiresAt string `mapstructure:"expires_at"`
-		} `mapstructure:"token"`
+			ExpiresAt string `json:"expires_at"`
+		} `json:"token"`
 	}
 
 	var token Token
@@ -74,35 +69,26 @@
 	// Parse the token itself from the stored headers.
 	token.ID = r.Header.Get("X-Subject-Token")
 
-	err := mapstructure.Decode(r.Body, &response)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return nil, err
 	}
 
 	// Attempt to parse the timestamp.
-	token.ExpiresAt, err = time.Parse(gophercloud.RFC3339Milli, response.Token.ExpiresAt)
+	token.ExpiresAt, err = time.Parse(gophercloud.RFC3339Milli, s.Token.ExpiresAt)
 
 	return &token, err
 }
 
 // ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token.
-func (result CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
-	if result.Err != nil {
-		return nil, result.Err
-	}
-
-	var response struct {
+func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
+	var s struct {
 		Token struct {
-			Entries []CatalogEntry `mapstructure:"catalog"`
-		} `mapstructure:"token"`
+			Entries []CatalogEntry `json:"catalog"`
+		} `json:"token"`
 	}
-
-	err := mapstructure.Decode(result.Body, &response)
-	if err != nil {
-		return nil, err
-	}
-
-	return &ServiceCatalog{Entries: response.Token.Entries}, nil
+	err := r.ExtractInto(&s)
+	return &ServiceCatalog{Entries: s.Token.Entries}, err
 }
 
 // CreateResult defers the interpretation of a created token.
diff --git a/openstack/identity/v3/tokens/testing/doc.go b/openstack/identity/v3/tokens/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/identity/v3/tokens/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go
new file mode 100644
index 0000000..cbb675f
--- /dev/null
+++ b/openstack/identity/v3/tokens/testing/requests_test.go
@@ -0,0 +1,509 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
+	"github.com/gophercloud/gophercloud/testhelper"
+)
+
+// authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure.
+func authTokenPost(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope, requestJSON string) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	client := gophercloud.ServiceClient{
+		ProviderClient: &gophercloud.ProviderClient{},
+		Endpoint:       testhelper.Endpoint(),
+	}
+
+	testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "POST")
+		testhelper.TestHeader(t, r, "Content-Type", "application/json")
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		testhelper.TestJSONRequest(t, r, requestJSON)
+
+		w.WriteHeader(http.StatusCreated)
+		fmt.Fprintf(w, `{
+			"token": {
+				"expires_at": "2014-10-02T13:45:00.000000Z"
+			}
+		}`)
+	})
+
+	_, err := tokens.Create(&client, options, scope).Extract()
+	if err != nil {
+		t.Errorf("Create returned an error: %v", err)
+	}
+}
+
+func authTokenPostErr(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope, includeToken bool, expectedErr error) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	client := gophercloud.ServiceClient{
+		ProviderClient: &gophercloud.ProviderClient{},
+		Endpoint:       testhelper.Endpoint(),
+	}
+	if includeToken {
+		client.TokenID = "abcdef123456"
+	}
+
+	_, err := tokens.Create(&client, options, scope).Extract()
+	if err == nil {
+		t.Errorf("Create did NOT return an error")
+	}
+	if err != expectedErr {
+		t.Errorf("Create returned an unexpected error: wanted %v, got %v", expectedErr, err)
+	}
+}
+
+func TestCreateUserIDAndPassword(t *testing.T) {
+	authTokenPost(t, tokens.AuthOptions{UserID: "me", Password: "squirrel!"}, nil, `
+		{
+			"auth": {
+				"identity": {
+					"methods": ["password"],
+					"password": {
+						"user": { "id": "me", "password": "squirrel!" }
+					}
+				}
+			}
+		}
+	`)
+}
+
+func TestCreateUsernameDomainIDPassword(t *testing.T) {
+	authTokenPost(t, tokens.AuthOptions{Username: "fakey", Password: "notpassword", DomainID: "abc123"}, nil, `
+		{
+			"auth": {
+				"identity": {
+					"methods": ["password"],
+					"password": {
+						"user": {
+							"domain": {
+								"id": "abc123"
+							},
+							"name": "fakey",
+							"password": "notpassword"
+						}
+					}
+				}
+			}
+		}
+	`)
+}
+
+func TestCreateUsernameDomainNamePassword(t *testing.T) {
+	authTokenPost(t, tokens.AuthOptions{Username: "frank", Password: "swordfish", DomainName: "spork.net"}, nil, `
+		{
+			"auth": {
+				"identity": {
+					"methods": ["password"],
+					"password": {
+						"user": {
+							"domain": {
+								"name": "spork.net"
+							},
+							"name": "frank",
+							"password": "swordfish"
+						}
+					}
+				}
+			}
+		}
+	`)
+}
+
+func TestCreateTokenID(t *testing.T) {
+	authTokenPost(t, tokens.AuthOptions{TokenID: "12345abcdef"}, nil, `
+		{
+			"auth": {
+				"identity": {
+					"methods": ["token"],
+					"token": {
+						"id": "12345abcdef"
+					}
+				}
+			}
+		}
+	`)
+}
+
+func TestCreateProjectIDScope(t *testing.T) {
+	options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
+	scope := &tokens.Scope{ProjectID: "123456"}
+	authTokenPost(t, options, scope, `
+		{
+			"auth": {
+				"identity": {
+					"methods": ["password"],
+					"password": {
+						"user": {
+							"id": "fenris",
+							"password": "g0t0h311"
+						}
+					}
+				},
+				"scope": {
+					"project": {
+						"id": "123456"
+					}
+				}
+			}
+		}
+	`)
+}
+
+func TestCreateDomainIDScope(t *testing.T) {
+	options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
+	scope := &tokens.Scope{DomainID: "1000"}
+	authTokenPost(t, options, scope, `
+		{
+			"auth": {
+				"identity": {
+					"methods": ["password"],
+					"password": {
+						"user": {
+							"id": "fenris",
+							"password": "g0t0h311"
+						}
+					}
+				},
+				"scope": {
+					"domain": {
+						"id": "1000"
+					}
+				}
+			}
+		}
+	`)
+}
+
+func TestCreateProjectNameAndDomainIDScope(t *testing.T) {
+	options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
+	scope := &tokens.Scope{ProjectName: "world-domination", DomainID: "1000"}
+	authTokenPost(t, options, scope, `
+		{
+			"auth": {
+				"identity": {
+					"methods": ["password"],
+					"password": {
+						"user": {
+							"id": "fenris",
+							"password": "g0t0h311"
+						}
+					}
+				},
+				"scope": {
+					"project": {
+						"domain": {
+							"id": "1000"
+						},
+						"name": "world-domination"
+					}
+				}
+			}
+		}
+	`)
+}
+
+func TestCreateProjectNameAndDomainNameScope(t *testing.T) {
+	options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
+	scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"}
+	authTokenPost(t, options, scope, `
+		{
+			"auth": {
+				"identity": {
+					"methods": ["password"],
+					"password": {
+						"user": {
+							"id": "fenris",
+							"password": "g0t0h311"
+						}
+					}
+				},
+				"scope": {
+					"project": {
+						"domain": {
+							"name": "evil-plans"
+						},
+						"name": "world-domination"
+					}
+				}
+			}
+		}
+	`)
+}
+
+func TestCreateExtractsTokenFromResponse(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	client := gophercloud.ServiceClient{
+		ProviderClient: &gophercloud.ProviderClient{},
+		Endpoint:       testhelper.Endpoint(),
+	}
+
+	testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("X-Subject-Token", "aaa111")
+
+		w.WriteHeader(http.StatusCreated)
+		fmt.Fprintf(w, `{
+			"token": {
+				"expires_at": "2014-10-02T13:45:00.000000Z"
+			}
+		}`)
+	})
+
+	options := tokens.AuthOptions{UserID: "me", Password: "shhh"}
+	token, err := tokens.Create(&client, options, nil).Extract()
+	if err != nil {
+		t.Fatalf("Create returned an error: %v", err)
+	}
+
+	if token.ID != "aaa111" {
+		t.Errorf("Expected token to be aaa111, but was %s", token.ID)
+	}
+}
+
+func TestCreateFailureEmptyAuth(t *testing.T) {
+	authTokenPostErr(t, tokens.AuthOptions{}, nil, false, tokens.ErrMissingPassword{})
+}
+
+func TestCreateFailureTenantID(t *testing.T) {
+	authTokenPostErr(t, tokens.AuthOptions{TenantID: "something"}, nil, false, tokens.ErrTenantIDProvided{})
+}
+
+func TestCreateFailureTenantName(t *testing.T) {
+	authTokenPostErr(t, tokens.AuthOptions{TenantName: "something"}, nil, false, tokens.ErrTenantNameProvided{})
+}
+
+func TestCreateFailureTokenIDUsername(t *testing.T) {
+	authTokenPostErr(t, tokens.AuthOptions{Username: "something", TokenID: "12345"}, nil, true, tokens.ErrUsernameWithToken{})
+}
+
+func TestCreateFailureTokenIDUserID(t *testing.T) {
+	authTokenPostErr(t, tokens.AuthOptions{UserID: "something", TokenID: "12345"}, nil, true, tokens.ErrUserIDWithToken{})
+}
+
+func TestCreateFailureTokenIDDomainID(t *testing.T) {
+	authTokenPostErr(t, tokens.AuthOptions{DomainID: "something", TokenID: "12345"}, nil, true, tokens.ErrDomainIDWithToken{})
+}
+
+func TestCreateFailureTokenIDDomainName(t *testing.T) {
+	authTokenPostErr(t, tokens.AuthOptions{DomainName: "something", TokenID: "12345"}, nil, true, tokens.ErrDomainNameWithToken{})
+}
+
+func TestCreateFailureMissingUser(t *testing.T) {
+	options := tokens.AuthOptions{Password: "supersecure"}
+	authTokenPostErr(t, options, nil, false, tokens.ErrUsernameOrUserID{})
+}
+
+func TestCreateFailureBothUser(t *testing.T) {
+	options := tokens.AuthOptions{
+		Password: "supersecure",
+		Username: "oops",
+		UserID:   "redundancy",
+	}
+	authTokenPostErr(t, options, nil, false, tokens.ErrUsernameOrUserID{})
+}
+
+func TestCreateFailureMissingDomain(t *testing.T) {
+	options := tokens.AuthOptions{
+		Password: "supersecure",
+		Username: "notuniqueenough",
+	}
+	authTokenPostErr(t, options, nil, false, tokens.ErrDomainIDOrDomainName{})
+}
+
+func TestCreateFailureBothDomain(t *testing.T) {
+	options := tokens.AuthOptions{
+		Password:   "supersecure",
+		Username:   "someone",
+		DomainID:   "hurf",
+		DomainName: "durf",
+	}
+	authTokenPostErr(t, options, nil, false, tokens.ErrDomainIDOrDomainName{})
+}
+
+func TestCreateFailureUserIDDomainID(t *testing.T) {
+	options := tokens.AuthOptions{
+		UserID:   "100",
+		Password: "stuff",
+		DomainID: "oops",
+	}
+	authTokenPostErr(t, options, nil, false, tokens.ErrDomainIDWithUserID{})
+}
+
+func TestCreateFailureUserIDDomainName(t *testing.T) {
+	options := tokens.AuthOptions{
+		UserID:     "100",
+		Password:   "sssh",
+		DomainName: "oops",
+	}
+	authTokenPostErr(t, options, nil, false, tokens.ErrDomainNameWithUserID{})
+}
+
+func TestCreateFailureScopeProjectNameAlone(t *testing.T) {
+	options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &tokens.Scope{ProjectName: "notenough"}
+	authTokenPostErr(t, options, scope, false, tokens.ErrScopeDomainIDOrDomainName{})
+}
+
+func TestCreateFailureScopeProjectNameAndID(t *testing.T) {
+	options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &tokens.Scope{ProjectName: "whoops", ProjectID: "toomuch", DomainID: "1234"}
+	authTokenPostErr(t, options, scope, false, tokens.ErrScopeProjectIDOrProjectName{})
+}
+
+func TestCreateFailureScopeProjectIDAndDomainID(t *testing.T) {
+	options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &tokens.Scope{ProjectID: "toomuch", DomainID: "notneeded"}
+	authTokenPostErr(t, options, scope, false, tokens.ErrScopeProjectIDAlone{})
+}
+
+func TestCreateFailureScopeProjectIDAndDomainNAme(t *testing.T) {
+	options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &tokens.Scope{ProjectID: "toomuch", DomainName: "notneeded"}
+	authTokenPostErr(t, options, scope, false, tokens.ErrScopeProjectIDAlone{})
+}
+
+func TestCreateFailureScopeDomainIDAndDomainName(t *testing.T) {
+	options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &tokens.Scope{DomainID: "toomuch", DomainName: "notneeded"}
+	authTokenPostErr(t, options, scope, false, tokens.ErrScopeDomainIDOrDomainName{})
+}
+
+func TestCreateFailureScopeDomainNameAlone(t *testing.T) {
+	options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &tokens.Scope{DomainName: "notenough"}
+	authTokenPostErr(t, options, scope, false, tokens.ErrScopeDomainName{})
+}
+
+func TestCreateFailureEmptyScope(t *testing.T) {
+	options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &tokens.Scope{}
+	authTokenPostErr(t, options, scope, false, tokens.ErrScopeEmpty{})
+}
+
+func TestGetRequest(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	client := gophercloud.ServiceClient{
+		ProviderClient: &gophercloud.ProviderClient{
+			TokenID: "12345abcdef",
+		},
+		Endpoint: testhelper.Endpoint(),
+	}
+
+	testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "GET")
+		testhelper.TestHeader(t, r, "Content-Type", "")
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef")
+		testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345")
+
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `
+			{ "token": { "expires_at": "2014-08-29T13:10:01.000000Z" } }
+		`)
+	})
+
+	token, err := tokens.Get(&client, "abcdef12345").Extract()
+	if err != nil {
+		t.Errorf("Info returned an error: %v", err)
+	}
+
+	expected, _ := time.Parse(time.UnixDate, "Fri Aug 29 13:10:01 UTC 2014")
+	if token.ExpiresAt != expected {
+		t.Errorf("Expected expiration time %s, but was %s", expected.Format(time.UnixDate), token.ExpiresAt.Format(time.UnixDate))
+	}
+}
+
+func prepareAuthTokenHandler(t *testing.T, expectedMethod string, status int) gophercloud.ServiceClient {
+	client := gophercloud.ServiceClient{
+		ProviderClient: &gophercloud.ProviderClient{
+			TokenID: "12345abcdef",
+		},
+		Endpoint: testhelper.Endpoint(),
+	}
+
+	testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, expectedMethod)
+		testhelper.TestHeader(t, r, "Content-Type", "")
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef")
+		testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345")
+
+		w.WriteHeader(status)
+	})
+
+	return client
+}
+
+func TestValidateRequestSuccessful(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	client := prepareAuthTokenHandler(t, "HEAD", http.StatusNoContent)
+
+	ok, err := tokens.Validate(&client, "abcdef12345")
+	if err != nil {
+		t.Errorf("Unexpected error from Validate: %v", err)
+	}
+
+	if !ok {
+		t.Errorf("Validate returned false for a valid token")
+	}
+}
+
+func TestValidateRequestFailure(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	client := prepareAuthTokenHandler(t, "HEAD", http.StatusNotFound)
+
+	ok, err := tokens.Validate(&client, "abcdef12345")
+	if err != nil {
+		t.Errorf("Unexpected error from Validate: %v", err)
+	}
+
+	if ok {
+		t.Errorf("Validate returned true for an invalid token")
+	}
+}
+
+func TestValidateRequestError(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	client := prepareAuthTokenHandler(t, "HEAD", http.StatusUnauthorized)
+
+	_, err := tokens.Validate(&client, "abcdef12345")
+	if err == nil {
+		t.Errorf("Missing expected error from Validate")
+	}
+}
+
+func TestRevokeRequestSuccessful(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	client := prepareAuthTokenHandler(t, "DELETE", http.StatusNoContent)
+
+	res := tokens.Revoke(&client, "abcdef12345")
+	testhelper.AssertNoErr(t, res.Err)
+}
+
+func TestRevokeRequestError(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	client := prepareAuthTokenHandler(t, "DELETE", http.StatusNotFound)
+
+	res := tokens.Revoke(&client, "abcdef12345")
+	if res.Err == nil {
+		t.Errorf("Missing expected error from Revoke")
+	}
+}
diff --git a/openstack/identity/v3/tokens/urls.go b/openstack/identity/v3/tokens/urls.go
index 360b60a..2f864a3 100644
--- a/openstack/identity/v3/tokens/urls.go
+++ b/openstack/identity/v3/tokens/urls.go
@@ -1,6 +1,6 @@
 package tokens
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func tokenURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("auth", "tokens")
diff --git a/openstack/identity/v3/tokens/urls_test.go b/openstack/identity/v3/tokens/urls_test.go
deleted file mode 100644
index 549c398..0000000
--- a/openstack/identity/v3/tokens/urls_test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package tokens
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestTokenURL(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	client := gophercloud.ServiceClient{Endpoint: testhelper.Endpoint()}
-
-	expected := testhelper.Endpoint() + "auth/tokens"
-	actual := tokenURL(&client)
-	if actual != expected {
-		t.Errorf("Expected URL %s, but was %s", expected, actual)
-	}
-}
diff --git a/openstack/networking/v2/apiversions/errors.go b/openstack/networking/v2/apiversions/errors.go
deleted file mode 100644
index 76bdb14..0000000
--- a/openstack/networking/v2/apiversions/errors.go
+++ /dev/null
@@ -1 +0,0 @@
-package apiversions
diff --git a/openstack/networking/v2/apiversions/requests.go b/openstack/networking/v2/apiversions/requests.go
index 9fb6de1..59ece85 100644
--- a/openstack/networking/v2/apiversions/requests.go
+++ b/openstack/networking/v2/apiversions/requests.go
@@ -1,8 +1,8 @@
 package apiversions
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListVersions lists all the Neutron API versions available to end-users
diff --git a/openstack/networking/v2/apiversions/requests_test.go b/openstack/networking/v2/apiversions/requests_test.go
deleted file mode 100644
index d35af9f..0000000
--- a/openstack/networking/v2/apiversions/requests_test.go
+++ /dev/null
@@ -1,182 +0,0 @@
-package apiversions
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListVersions(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "versions": [
-        {
-            "status": "CURRENT",
-            "id": "v2.0",
-            "links": [
-                {
-                    "href": "http://23.253.228.211:9696/v2.0",
-                    "rel": "self"
-                }
-            ]
-        }
-    ]
-}`)
-	})
-
-	count := 0
-
-	ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractAPIVersions(page)
-		if err != nil {
-			t.Errorf("Failed to extract API versions: %v", err)
-			return false, err
-		}
-
-		expected := []APIVersion{
-			APIVersion{
-				Status: "CURRENT",
-				ID:     "v2.0",
-			},
-		}
-
-		th.AssertDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestNonJSONCannotBeExtractedIntoAPIVersions(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		w.WriteHeader(http.StatusOK)
-	})
-
-	ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		if _, err := ExtractAPIVersions(page); err == nil {
-			t.Fatalf("Expected error, got nil")
-		}
-		return true, nil
-	})
-}
-
-func TestAPIInfo(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "resources": [
-        {
-            "links": [
-                {
-                    "href": "http://23.253.228.211:9696/v2.0/subnets",
-                    "rel": "self"
-                }
-            ],
-            "name": "subnet",
-            "collection": "subnets"
-        },
-        {
-            "links": [
-                {
-                    "href": "http://23.253.228.211:9696/v2.0/networks",
-                    "rel": "self"
-                }
-            ],
-            "name": "network",
-            "collection": "networks"
-        },
-        {
-            "links": [
-                {
-                    "href": "http://23.253.228.211:9696/v2.0/ports",
-                    "rel": "self"
-                }
-            ],
-            "name": "port",
-            "collection": "ports"
-        }
-    ]
-}
-			`)
-	})
-
-	count := 0
-
-	ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractVersionResources(page)
-		if err != nil {
-			t.Errorf("Failed to extract version resources: %v", err)
-			return false, err
-		}
-
-		expected := []APIVersionResource{
-			APIVersionResource{
-				Name:       "subnet",
-				Collection: "subnets",
-			},
-			APIVersionResource{
-				Name:       "network",
-				Collection: "networks",
-			},
-			APIVersionResource{
-				Name:       "port",
-				Collection: "ports",
-			},
-		}
-
-		th.AssertDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestNonJSONCannotBeExtractedIntoAPIVersionResources(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		w.WriteHeader(http.StatusOK)
-	})
-
-	ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(func(page pagination.Page) (bool, error) {
-		if _, err := ExtractVersionResources(page); err == nil {
-			t.Fatalf("Expected error, got nil")
-		}
-		return true, nil
-	})
-}
diff --git a/openstack/networking/v2/apiversions/results.go b/openstack/networking/v2/apiversions/results.go
index 9715934..eff4485 100644
--- a/openstack/networking/v2/apiversions/results.go
+++ b/openstack/networking/v2/apiversions/results.go
@@ -1,15 +1,14 @@
 package apiversions
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // APIVersion represents an API version for Neutron. It contains the status of
 // the API, and its unique ID.
 type APIVersion struct {
-	Status string `mapstructure:"status" json:"status"`
-	ID     string `mapstructure:"id" json:"id"`
+	Status string `son:"status"`
+	ID     string `json:"id"`
 }
 
 // APIVersionPage is the page returned by a pager when traversing over a
@@ -21,29 +20,24 @@
 // IsEmpty checks whether an APIVersionPage struct is empty.
 func (r APIVersionPage) IsEmpty() (bool, error) {
 	is, err := ExtractAPIVersions(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractAPIVersions takes a collection page, extracts all of the elements,
 // and returns them a slice of APIVersion structs. It is effectively a cast.
-func ExtractAPIVersions(page pagination.Page) ([]APIVersion, error) {
-	var resp struct {
-		Versions []APIVersion `mapstructure:"versions"`
+func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) {
+	var s struct {
+		Versions []APIVersion `json:"versions"`
 	}
-
-	err := mapstructure.Decode(page.(APIVersionPage).Body, &resp)
-
-	return resp.Versions, err
+	err := (r.(APIVersionPage)).ExtractInto(&s)
+	return s.Versions, err
 }
 
 // APIVersionResource represents a generic API resource. It contains the name
 // of the resource and its plural collection name.
 type APIVersionResource struct {
-	Name       string `mapstructure:"name" json:"name"`
-	Collection string `mapstructure:"collection" json:"collection"`
+	Name       string `json:"name"`
+	Collection string `json:"collection"`
 }
 
 // APIVersionResourcePage is a concrete type which embeds the common
@@ -56,22 +50,17 @@
 // APIVersionResourcePage is empty or not.
 func (r APIVersionResourcePage) IsEmpty() (bool, error) {
 	is, err := ExtractVersionResources(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractVersionResources accepts a Page struct, specifically a
 // APIVersionResourcePage struct, and extracts the elements into a slice of
 // APIVersionResource structs. In other words, the collection is mapped into
 // a relevant slice.
-func ExtractVersionResources(page pagination.Page) ([]APIVersionResource, error) {
-	var resp struct {
-		APIVersionResources []APIVersionResource `mapstructure:"resources"`
+func ExtractVersionResources(r pagination.Page) ([]APIVersionResource, error) {
+	var s struct {
+		APIVersionResources []APIVersionResource `json:"resources"`
 	}
-
-	err := mapstructure.Decode(page.(APIVersionResourcePage).Body, &resp)
-
-	return resp.APIVersionResources, err
+	err := (r.(APIVersionResourcePage)).ExtractInto(&s)
+	return s.APIVersionResources, err
 }
diff --git a/openstack/networking/v2/apiversions/testing/doc.go b/openstack/networking/v2/apiversions/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/apiversions/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/apiversions/testing/requests_test.go b/openstack/networking/v2/apiversions/testing/requests_test.go
new file mode 100644
index 0000000..5a66a2a
--- /dev/null
+++ b/openstack/networking/v2/apiversions/testing/requests_test.go
@@ -0,0 +1,183 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/apiversions"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestListVersions(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "versions": [
+        {
+            "status": "CURRENT",
+            "id": "v2.0",
+            "links": [
+                {
+                    "href": "http://23.253.228.211:9696/v2.0",
+                    "rel": "self"
+                }
+            ]
+        }
+    ]
+}`)
+	})
+
+	count := 0
+
+	apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := apiversions.ExtractAPIVersions(page)
+		if err != nil {
+			t.Errorf("Failed to extract API versions: %v", err)
+			return false, err
+		}
+
+		expected := []apiversions.APIVersion{
+			{
+				Status: "CURRENT",
+				ID:     "v2.0",
+			},
+		}
+
+		th.AssertDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestNonJSONCannotBeExtractedIntoAPIVersions(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(http.StatusOK)
+	})
+
+	apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		if _, err := apiversions.ExtractAPIVersions(page); err == nil {
+			t.Fatalf("Expected error, got nil")
+		}
+		return true, nil
+	})
+}
+
+func TestAPIInfo(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "resources": [
+        {
+            "links": [
+                {
+                    "href": "http://23.253.228.211:9696/v2.0/subnets",
+                    "rel": "self"
+                }
+            ],
+            "name": "subnet",
+            "collection": "subnets"
+        },
+        {
+            "links": [
+                {
+                    "href": "http://23.253.228.211:9696/v2.0/networks",
+                    "rel": "self"
+                }
+            ],
+            "name": "network",
+            "collection": "networks"
+        },
+        {
+            "links": [
+                {
+                    "href": "http://23.253.228.211:9696/v2.0/ports",
+                    "rel": "self"
+                }
+            ],
+            "name": "port",
+            "collection": "ports"
+        }
+    ]
+}
+			`)
+	})
+
+	count := 0
+
+	apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := apiversions.ExtractVersionResources(page)
+		if err != nil {
+			t.Errorf("Failed to extract version resources: %v", err)
+			return false, err
+		}
+
+		expected := []apiversions.APIVersionResource{
+			{
+				Name:       "subnet",
+				Collection: "subnets",
+			},
+			{
+				Name:       "network",
+				Collection: "networks",
+			},
+			{
+				Name:       "port",
+				Collection: "ports",
+			},
+		}
+
+		th.AssertDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestNonJSONCannotBeExtractedIntoAPIVersionResources(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(http.StatusOK)
+	})
+
+	apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(func(page pagination.Page) (bool, error) {
+		if _, err := apiversions.ExtractVersionResources(page); err == nil {
+			t.Fatalf("Expected error, got nil")
+		}
+		return true, nil
+	})
+}
diff --git a/openstack/networking/v2/apiversions/urls.go b/openstack/networking/v2/apiversions/urls.go
index 58aa2b6..0fa7437 100644
--- a/openstack/networking/v2/apiversions/urls.go
+++ b/openstack/networking/v2/apiversions/urls.go
@@ -3,7 +3,7 @@
 import (
 	"strings"
 
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 func apiVersionsURL(c *gophercloud.ServiceClient) string {
diff --git a/openstack/networking/v2/apiversions/urls_test.go b/openstack/networking/v2/apiversions/urls_test.go
deleted file mode 100644
index 7dd069c..0000000
--- a/openstack/networking/v2/apiversions/urls_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package apiversions
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestAPIVersionsURL(t *testing.T) {
-	actual := apiVersionsURL(endpointClient())
-	expected := endpoint
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestAPIInfoURL(t *testing.T) {
-	actual := apiInfoURL(endpointClient(), "v2.0")
-	expected := endpoint + "v2.0/"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/networking/v2/common/common_tests.go b/openstack/networking/v2/common/common_tests.go
index 4160351..7e1d917 100644
--- a/openstack/networking/v2/common/common_tests.go
+++ b/openstack/networking/v2/common/common_tests.go
@@ -1,8 +1,8 @@
 package common
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/testhelper/client"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/testhelper/client"
 )
 
 const TokenID = client.TokenID
diff --git a/openstack/networking/v2/extensions/delegate.go b/openstack/networking/v2/extensions/delegate.go
index d08e1fd..0c43689 100644
--- a/openstack/networking/v2/extensions/delegate.go
+++ b/openstack/networking/v2/extensions/delegate.go
@@ -1,9 +1,9 @@
 package extensions
 
 import (
-	"github.com/rackspace/gophercloud"
-	common "github.com/rackspace/gophercloud/openstack/common/extensions"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	common "github.com/gophercloud/gophercloud/openstack/common/extensions"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Extension is a single OpenStack extension.
diff --git a/openstack/networking/v2/extensions/delegate_test.go b/openstack/networking/v2/extensions/delegate_test.go
deleted file mode 100755
index 3d2ac78..0000000
--- a/openstack/networking/v2/extensions/delegate_test.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package extensions
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	common "github.com/rackspace/gophercloud/openstack/common/extensions"
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/extensions", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-
-		fmt.Fprintf(w, `
-{
-    "extensions": [
-        {
-            "updated": "2013-01-20T00:00:00-00:00",
-            "name": "Neutron Service Type Management",
-            "links": [],
-            "namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
-            "alias": "service-type",
-            "description": "API for retrieving service providers for Neutron advanced services"
-        }
-    ]
-}
-      `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractExtensions(page)
-		if err != nil {
-			t.Errorf("Failed to extract extensions: %v", err)
-		}
-
-		expected := []Extension{
-			Extension{
-				common.Extension{
-					Updated:     "2013-01-20T00:00:00-00:00",
-					Name:        "Neutron Service Type Management",
-					Links:       []interface{}{},
-					Namespace:   "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
-					Alias:       "service-type",
-					Description: "API for retrieving service providers for Neutron advanced services",
-				},
-			},
-		}
-
-		th.AssertDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/extensions/agent", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "extension": {
-        "updated": "2013-02-03T10:00:00-00:00",
-        "name": "agent",
-        "links": [],
-        "namespace": "http://docs.openstack.org/ext/agent/api/v2.0",
-        "alias": "agent",
-        "description": "The agent management extension."
-    }
-}
-    `)
-	})
-
-	ext, err := Get(fake.ServiceClient(), "agent").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, ext.Updated, "2013-02-03T10:00:00-00:00")
-	th.AssertEquals(t, ext.Name, "agent")
-	th.AssertEquals(t, ext.Namespace, "http://docs.openstack.org/ext/agent/api/v2.0")
-	th.AssertEquals(t, ext.Alias, "agent")
-	th.AssertEquals(t, ext.Description, "The agent management extension.")
-}
diff --git a/openstack/networking/v2/extensions/external/requests.go b/openstack/networking/v2/extensions/external/requests.go
index 097ae37..1ee39d2 100644
--- a/openstack/networking/v2/extensions/external/requests.go
+++ b/openstack/networking/v2/extensions/external/requests.go
@@ -1,69 +1,32 @@
 package external
 
 import (
-	"time"
-
-	"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-)
-
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
 )
 
 // CreateOpts is the structure used when creating new external network
 // resources. It embeds networks.CreateOpts and so inherits all of its required
 // and optional fields, with the addition of the External field.
 type CreateOpts struct {
-	Parent   networks.CreateOpts
-	External bool
+	networks.CreateOpts
+	External *bool `json:"router:external,omitempty"`
 }
 
 // ToNetworkCreateMap casts a CreateOpts struct to a map.
-func (o CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
-
-	// DO NOT REMOVE. Though this line seemingly does nothing of value, it is a
-	// splint to prevent the unit test from failing on Go Tip. We suspect it is a
-	// compiler issue that will hopefully be worked out prior to our next release.
-	// Again, for all the unit tests to pass, this line is necessary and sufficient
-	// at the moment. We should reassess after the Go 1.5 release to determine
-	// if this line is still needed.
-	time.Sleep(0 * time.Millisecond)
-
-	outer, err := o.Parent.ToNetworkCreateMap()
-	if err != nil {
-		return nil, err
-	}
-
-	outer["network"].(map[string]interface{})["router:external"] = o.External
-
-	return outer, nil
+func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "network")
 }
 
 // UpdateOpts is the structure used when updating existing external network
 // resources. It embeds networks.UpdateOpts and so inherits all of its required
 // and optional fields, with the addition of the External field.
 type UpdateOpts struct {
-	Parent   networks.UpdateOpts
-	External bool
+	networks.UpdateOpts
+	External *bool `json:"router:external,omitempty"`
 }
 
 // ToNetworkUpdateMap casts an UpdateOpts struct to a map.
-func (o UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) {
-	outer, err := o.Parent.ToNetworkUpdateMap()
-	if err != nil {
-		return nil, err
-	}
-
-	outer["network"].(map[string]interface{})["router:external"] = o.External
-
-	return outer, nil
+func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "network")
 }
diff --git a/openstack/networking/v2/extensions/external/results.go b/openstack/networking/v2/extensions/external/results.go
index 54dbf4b..7e10c6d 100644
--- a/openstack/networking/v2/extensions/external/results.go
+++ b/openstack/networking/v2/extensions/external/results.go
@@ -1,81 +1,76 @@
 package external
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // NetworkExternal represents a decorated form of a Network with based on the
 // "external-net" extension.
 type NetworkExternal struct {
 	// UUID for the network
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 
 	// Human-readable name for the network. Might not be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 
 	// The administrative state of network. If false (down), the network does not forward packets.
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Indicates whether network is currently operational. Possible values include
 	// `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values.
-	Status string `mapstructure:"status" json:"status"`
+	Status string `json:"status"`
 
 	// Subnets associated with this network.
-	Subnets []string `mapstructure:"subnets" json:"subnets"`
+	Subnets []string `json:"subnets"`
 
 	// Owner of network. Only admin users can specify a tenant_id other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// Specifies whether the network resource can be accessed by any tenant or not.
-	Shared bool `mapstructure:"shared" json:"shared"`
+	Shared bool `json:"shared"`
 
 	// Specifies whether the network is an external network or not.
-	External bool `mapstructure:"router:external" json:"router:external"`
-}
-
-func commonExtract(e error, response interface{}) (*NetworkExternal, error) {
-	if e != nil {
-		return nil, e
-	}
-
-	var res struct {
-		Network *NetworkExternal `json:"network"`
-	}
-
-	err := mapstructure.Decode(response, &res)
-
-	return res.Network, err
+	External bool `json:"router:external"`
 }
 
 // ExtractGet decorates a GetResult struct returned from a networks.Get()
 // function with extended attributes.
 func ExtractGet(r networks.GetResult) (*NetworkExternal, error) {
-	return commonExtract(r.Err, r.Body)
+	var s struct {
+		Network *NetworkExternal `json:"network"`
+	}
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractCreate decorates a CreateResult struct returned from a networks.Create()
 // function with extended attributes.
 func ExtractCreate(r networks.CreateResult) (*NetworkExternal, error) {
-	return commonExtract(r.Err, r.Body)
+	var s struct {
+		Network *NetworkExternal `json:"network"`
+	}
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractUpdate decorates a UpdateResult struct returned from a
 // networks.Update() function with extended attributes.
 func ExtractUpdate(r networks.UpdateResult) (*NetworkExternal, error) {
-	return commonExtract(r.Err, r.Body)
+	var s struct {
+		Network *NetworkExternal `json:"network"`
+	}
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractList accepts a Page struct, specifically a NetworkPage struct, and
 // extracts the elements into a slice of NetworkExternal structs. In other
 // words, a generic collection is mapped into a relevant slice.
-func ExtractList(page pagination.Page) ([]NetworkExternal, error) {
-	var resp struct {
-		Networks []NetworkExternal `mapstructure:"networks" json:"networks"`
+func ExtractList(r pagination.Page) ([]NetworkExternal, error) {
+	var s struct {
+		Networks []NetworkExternal `json:"networks" json:"networks"`
 	}
-
-	err := mapstructure.Decode(page.(networks.NetworkPage).Body, &resp)
-
-	return resp.Networks, err
+	err := (r.(networks.NetworkPage)).ExtractInto(&s)
+	return s.Networks, err
 }
diff --git a/openstack/networking/v2/extensions/external/results_test.go b/openstack/networking/v2/extensions/external/results_test.go
deleted file mode 100644
index 916cd2c..0000000
--- a/openstack/networking/v2/extensions/external/results_test.go
+++ /dev/null
@@ -1,254 +0,0 @@
-package external
-
-import (
-	"errors"
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "networks": [
-        {
-            "admin_state_up": true,
-            "id": "0f38d5ad-10a6-428f-a5fc-825cfe0f1970",
-            "name": "net1",
-            "router:external": false,
-            "shared": false,
-            "status": "ACTIVE",
-            "subnets": [
-                "25778974-48a8-46e7-8998-9dc8c70d2f06"
-            ],
-            "tenant_id": "b575417a6c444a6eb5cc3a58eb4f714a"
-        },
-        {
-            "admin_state_up": true,
-            "id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
-            "name": "ext_net",
-            "router:external": true,
-            "shared": false,
-            "status": "ACTIVE",
-            "subnets": [
-                "2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
-            ],
-            "tenant_id": "5eb8995cf717462c9df8d1edfa498010"
-        }
-    ]
-}
-			`)
-	})
-
-	count := 0
-
-	networks.List(fake.ServiceClient(), networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractList(page)
-		if err != nil {
-			t.Errorf("Failed to extract networks: %v", err)
-			return false, err
-		}
-
-		expected := []NetworkExternal{
-			NetworkExternal{
-				Status:       "ACTIVE",
-				Subnets:      []string{"25778974-48a8-46e7-8998-9dc8c70d2f06"},
-				Name:         "net1",
-				AdminStateUp: true,
-				TenantID:     "b575417a6c444a6eb5cc3a58eb4f714a",
-				Shared:       false,
-				ID:           "0f38d5ad-10a6-428f-a5fc-825cfe0f1970",
-				External:     false,
-			},
-			NetworkExternal{
-				Status:       "ACTIVE",
-				Subnets:      []string{"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"},
-				Name:         "ext_net",
-				AdminStateUp: true,
-				TenantID:     "5eb8995cf717462c9df8d1edfa498010",
-				Shared:       false,
-				ID:           "8d05a1b1-297a-46ca-8974-17debf51ca3c",
-				External:     true,
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "network": {
-        "admin_state_up": true,
-        "id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
-        "name": "ext_net",
-        "router:external": true,
-        "shared": false,
-        "status": "ACTIVE",
-        "subnets": [
-            "2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
-        ],
-        "tenant_id": "5eb8995cf717462c9df8d1edfa498010"
-    }
-}
-			`)
-	})
-
-	res := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	n, err := ExtractGet(res)
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, true, n.External)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "network": {
-        "admin_state_up": true,
-        "name": "ext_net",
-        "router:external": true
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-	"network": {
-			"admin_state_up": true,
-			"id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
-			"name": "ext_net",
-			"router:external": true,
-			"shared": false,
-			"status": "ACTIVE",
-			"subnets": [
-					"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
-			],
-			"tenant_id": "5eb8995cf717462c9df8d1edfa498010"
-	}
-}
-		`)
-	})
-
-	options := CreateOpts{networks.CreateOpts{Name: "ext_net", AdminStateUp: Up}, true}
-	res := networks.Create(fake.ServiceClient(), options)
-
-	n, err := ExtractCreate(res)
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, true, n.External)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-		"network": {
-				"router:external": true,
-				"name": "new_name"
-		}
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-	"network": {
-			"admin_state_up": true,
-			"id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
-			"name": "new_name",
-			"router:external": true,
-			"shared": false,
-			"status": "ACTIVE",
-			"subnets": [
-					"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
-			],
-			"tenant_id": "5eb8995cf717462c9df8d1edfa498010"
-	}
-}
-		`)
-	})
-
-	options := UpdateOpts{networks.UpdateOpts{Name: "new_name"}, true}
-	res := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options)
-	n, err := ExtractUpdate(res)
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, true, n.External)
-}
-
-func TestExtractFnsReturnsErrWhenResultContainsErr(t *testing.T) {
-	gr := networks.GetResult{}
-	gr.Err = errors.New("")
-
-	if _, err := ExtractGet(gr); err == nil {
-		t.Fatalf("Expected error, got one")
-	}
-
-	ur := networks.UpdateResult{}
-	ur.Err = errors.New("")
-
-	if _, err := ExtractUpdate(ur); err == nil {
-		t.Fatalf("Expected error, got one")
-	}
-
-	cr := networks.CreateResult{}
-	cr.Err = errors.New("")
-
-	if _, err := ExtractCreate(cr); err == nil {
-		t.Fatalf("Expected error, got one")
-	}
-}
diff --git a/openstack/networking/v2/extensions/external/testing/doc.go b/openstack/networking/v2/extensions/external/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/external/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/external/testing/results_test.go b/openstack/networking/v2/extensions/external/testing/results_test.go
new file mode 100644
index 0000000..2ddc3ac
--- /dev/null
+++ b/openstack/networking/v2/extensions/external/testing/results_test.go
@@ -0,0 +1,256 @@
+package testing
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "networks": [
+        {
+            "admin_state_up": true,
+            "id": "0f38d5ad-10a6-428f-a5fc-825cfe0f1970",
+            "name": "net1",
+            "router:external": false,
+            "shared": false,
+            "status": "ACTIVE",
+            "subnets": [
+                "25778974-48a8-46e7-8998-9dc8c70d2f06"
+            ],
+            "tenant_id": "b575417a6c444a6eb5cc3a58eb4f714a"
+        },
+        {
+            "admin_state_up": true,
+            "id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
+            "name": "ext_net",
+            "router:external": true,
+            "shared": false,
+            "status": "ACTIVE",
+            "subnets": [
+                "2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
+            ],
+            "tenant_id": "5eb8995cf717462c9df8d1edfa498010"
+        }
+    ]
+}
+			`)
+	})
+
+	count := 0
+
+	networks.List(fake.ServiceClient(), networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := external.ExtractList(page)
+		if err != nil {
+			t.Errorf("Failed to extract networks: %v", err)
+			return false, err
+		}
+
+		expected := []external.NetworkExternal{
+			{
+				Status:       "ACTIVE",
+				Subnets:      []string{"25778974-48a8-46e7-8998-9dc8c70d2f06"},
+				Name:         "net1",
+				AdminStateUp: true,
+				TenantID:     "b575417a6c444a6eb5cc3a58eb4f714a",
+				Shared:       false,
+				ID:           "0f38d5ad-10a6-428f-a5fc-825cfe0f1970",
+				External:     false,
+			},
+			{
+				Status:       "ACTIVE",
+				Subnets:      []string{"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"},
+				Name:         "ext_net",
+				AdminStateUp: true,
+				TenantID:     "5eb8995cf717462c9df8d1edfa498010",
+				Shared:       false,
+				ID:           "8d05a1b1-297a-46ca-8974-17debf51ca3c",
+				External:     true,
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "network": {
+        "admin_state_up": true,
+        "id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
+        "name": "ext_net",
+        "router:external": true,
+        "shared": false,
+        "status": "ACTIVE",
+        "subnets": [
+            "2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
+        ],
+        "tenant_id": "5eb8995cf717462c9df8d1edfa498010"
+    }
+}
+			`)
+	})
+
+	res := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	n, err := external.ExtractGet(res)
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, true, n.External)
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "network": {
+        "admin_state_up": true,
+        "name": "ext_net",
+        "router:external": true
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+	"network": {
+			"admin_state_up": true,
+			"id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
+			"name": "ext_net",
+			"router:external": true,
+			"shared": false,
+			"status": "ACTIVE",
+			"subnets": [
+					"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
+			],
+			"tenant_id": "5eb8995cf717462c9df8d1edfa498010"
+	}
+}
+		`)
+	})
+
+	options := external.CreateOpts{networks.CreateOpts{Name: "ext_net", AdminStateUp: gophercloud.Enabled}, gophercloud.Enabled}
+	res := networks.Create(fake.ServiceClient(), options)
+
+	n, err := external.ExtractCreate(res)
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, true, n.External)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+		"network": {
+				"router:external": true,
+				"name": "new_name"
+		}
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+	"network": {
+			"admin_state_up": true,
+			"id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
+			"name": "new_name",
+			"router:external": true,
+			"shared": false,
+			"status": "ACTIVE",
+			"subnets": [
+					"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
+			],
+			"tenant_id": "5eb8995cf717462c9df8d1edfa498010"
+	}
+}
+		`)
+	})
+
+	options := external.UpdateOpts{networks.UpdateOpts{Name: "new_name"}, gophercloud.Enabled}
+	res := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options)
+	n, err := external.ExtractUpdate(res)
+
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, true, n.External)
+}
+
+func TestExtractFnsReturnsErrWhenResultContainsErr(t *testing.T) {
+	gr := networks.GetResult{}
+	gr.Err = errors.New("")
+
+	if _, err := external.ExtractGet(gr); err == nil {
+		t.Fatalf("Expected error, got one")
+	}
+
+	ur := networks.UpdateResult{}
+	ur.Err = errors.New("")
+
+	if _, err := external.ExtractUpdate(ur); err == nil {
+		t.Fatalf("Expected error, got one")
+	}
+
+	cr := networks.CreateResult{}
+	cr.Err = errors.New("")
+
+	if _, err := external.ExtractCreate(cr); err == nil {
+		t.Fatalf("Expected error, got one")
+	}
+}
diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go
index 12d587f..21ceb4e 100644
--- a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go
+++ b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go
@@ -1,26 +1,8 @@
 package firewalls
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Shared gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Yes` and `No` enums.
-type Shared *bool
-
-// Convenience vars for AdminStateUp and Shared values.
-var (
-	iTrue             = true
-	iFalse            = false
-	Up     AdminState = &iTrue
-	Down   AdminState = &iFalse
-	Yes    Shared     = &iTrue
-	No     Shared     = &iFalse
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -51,10 +33,7 @@
 // ToFirewallListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToFirewallListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -65,7 +44,6 @@
 // tenant who submits the request, unless an admin user submits the request.
 func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
 	url := rootURL(c)
-
 	if opts != nil {
 		query, err := opts.ToFirewallListQuery()
 		if err != nil {
@@ -73,7 +51,6 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return FirewallPage{pagination.LinkedPageBase{PageResult: r}}
 	})
@@ -89,65 +66,36 @@
 
 // CreateOpts contains all the values needed to create a new firewall.
 type CreateOpts struct {
+	PolicyID string `json:"firewall_policy_id" required:"true"`
 	// Only required if the caller has an admin role and wants to create a firewall
 	// for another tenant.
-	TenantID     string
-	Name         string
-	Description  string
-	AdminStateUp *bool
-	Shared       *bool
-	PolicyID     string
+	TenantID     string `json:"tenant_id,omitempty"`
+	Name         string `json:"name,omitempty"`
+	Description  string `json:"description,omitempty"`
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
+	Shared       *bool  `json:"shared,omitempty"`
 }
 
 // ToFirewallCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) {
-	if opts.PolicyID == "" {
-		return nil, errPolicyRequired
-	}
-
-	f := make(map[string]interface{})
-
-	if opts.TenantID != "" {
-		f["tenant_id"] = opts.TenantID
-	}
-	if opts.Name != "" {
-		f["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		f["description"] = opts.Description
-	}
-	if opts.Shared != nil {
-		f["shared"] = *opts.Shared
-	}
-	if opts.AdminStateUp != nil {
-		f["admin_state_up"] = *opts.AdminStateUp
-	}
-	if opts.PolicyID != "" {
-		f["firewall_policy_id"] = opts.PolicyID
-	}
-
-	return map[string]interface{}{"firewall": f}, nil
+	return gophercloud.BuildRequestBody(opts, "firewall")
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new firewall
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToFirewallCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToFirewallCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular firewall based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -160,57 +108,33 @@
 
 // UpdateOpts contains the values used when updating a firewall.
 type UpdateOpts struct {
-	// Name of the firewall.
-	Name         string
-	Description  string
-	AdminStateUp *bool
-	Shared       *bool
-	PolicyID     string
+	PolicyID     string `json:"firewall_policy_id" required:"true"`
+	Name         string `json:"name,omitempty"`
+	Description  string `json:"description,omitempty"`
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
+	Shared       *bool  `json:"shared,omitempty"`
 }
 
 // ToFirewallUpdateMap casts a CreateOpts struct to a map.
 func (opts UpdateOpts) ToFirewallUpdateMap() (map[string]interface{}, error) {
-	f := make(map[string]interface{})
-
-	if opts.Name != "" {
-		f["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		f["description"] = opts.Description
-	}
-	if opts.Shared != nil {
-		f["shared"] = *opts.Shared
-	}
-	if opts.AdminStateUp != nil {
-		f["admin_state_up"] = *opts.AdminStateUp
-	}
-	if opts.PolicyID != "" {
-		f["firewall_policy_id"] = opts.PolicyID
-	}
-
-	return map[string]interface{}{"firewall": f}, nil
+	return gophercloud.BuildRequestBody(opts, "firewall")
 }
 
 // Update allows firewalls to be updated.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToFirewallUpdateMap()
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToFirewallUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // Delete will permanently delete a particular firewall based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/requests_test.go b/openstack/networking/v2/extensions/fwaas/firewalls/requests_test.go
deleted file mode 100644
index 19d32c5..0000000
--- a/openstack/networking/v2/extensions/fwaas/firewalls/requests_test.go
+++ /dev/null
@@ -1,246 +0,0 @@
-package firewalls
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/fw/firewalls", rootURL(fake.ServiceClient()))
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "firewalls":[
-        {
-           "status": "ACTIVE",
-           "name": "fw1",
-           "admin_state_up": false,
-           "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
-           "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
-           "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
-           "description": "OpenStack firewall 1"
-        },
-        {
-           "status": "PENDING_UPDATE",
-           "name": "fw2",
-           "admin_state_up": true,
-           "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
-           "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e299",
-           "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f99",
-           "description": "OpenStack firewall 2"
-        }
-   ]
-}
-      `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractFirewalls(page)
-		if err != nil {
-			t.Errorf("Failed to extract members: %v", err)
-			return false, err
-		}
-
-		expected := []Firewall{
-			Firewall{
-				Status:       "ACTIVE",
-				Name:         "fw1",
-				AdminStateUp: false,
-				TenantID:     "b4eedccc6fb74fa8a7ad6b08382b852b",
-				PolicyID:     "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
-				ID:           "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
-				Description:  "OpenStack firewall 1",
-			},
-			Firewall{
-				Status:       "PENDING_UPDATE",
-				Name:         "fw2",
-				AdminStateUp: true,
-				TenantID:     "b4eedccc6fb74fa8a7ad6b08382b852b",
-				PolicyID:     "34be8c83-4d42-4dca-a74e-b77fffb8e299",
-				ID:           "fb5b5315-64f6-4ea3-8e58-981cc37c6f99",
-				Description:  "OpenStack firewall 2",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "firewall":{
-        "name": "fw",
-        "description": "OpenStack firewall",
-        "admin_state_up": true,
-        "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
-        "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b"
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "firewall":{
-        "status": "PENDING_CREATE",
-        "name": "fw",
-        "description": "OpenStack firewall",
-        "admin_state_up": true,
-        "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
-        "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
-    }
-}
-    `)
-	})
-
-	options := CreateOpts{
-		TenantID:     "b4eedccc6fb74fa8a7ad6b08382b852b",
-		Name:         "fw",
-		Description:  "OpenStack firewall",
-		AdminStateUp: Up,
-		PolicyID:     "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
-	}
-	_, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewalls/fb5b5315-64f6-4ea3-8e58-981cc37c6f61", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "firewall": {
-        "status": "ACTIVE",
-        "name": "fw",
-        "admin_state_up": true,
-        "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
-        "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
-        "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
-        "description": "OpenStack firewall"
-    }
-}
-        `)
-	})
-
-	fw, err := Get(fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "ACTIVE", fw.Status)
-	th.AssertEquals(t, "fw", fw.Name)
-	th.AssertEquals(t, "OpenStack firewall", fw.Description)
-	th.AssertEquals(t, true, fw.AdminStateUp)
-	th.AssertEquals(t, "34be8c83-4d42-4dca-a74e-b77fffb8e28a", fw.PolicyID)
-	th.AssertEquals(t, "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", fw.ID)
-	th.AssertEquals(t, "b4eedccc6fb74fa8a7ad6b08382b852b", fw.TenantID)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewalls/ea5b5315-64f6-4ea3-8e58-981cc37c6576", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "firewall":{
-        "name": "fw",
-        "description": "updated fw",
-        "admin_state_up":false,
-        "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "firewall": {
-        "status": "ACTIVE",
-        "name": "fw",
-        "admin_state_up": false,
-        "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
-        "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
-        "id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576",
-        "description": "OpenStack firewall"
-    }
-}
-    `)
-	})
-
-	options := UpdateOpts{
-		Name:         "fw",
-		Description:  "updated fw",
-		AdminStateUp: Down,
-		PolicyID:     "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
-	}
-
-	_, err := Update(fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", options).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewalls/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/results.go b/openstack/networking/v2/extensions/fwaas/firewalls/results.go
index a8c76ee..ab6cf8e 100644
--- a/openstack/networking/v2/extensions/fwaas/firewalls/results.go
+++ b/openstack/networking/v2/extensions/fwaas/firewalls/results.go
@@ -1,19 +1,19 @@
 package firewalls
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
+// Firewall is an OpenStack firewall.
 type Firewall struct {
-	ID           string `json:"id" mapstructure:"id"`
-	Name         string `json:"name" mapstructure:"name"`
-	Description  string `json:"description" mapstructure:"description"`
-	AdminStateUp bool   `json:"admin_state_up" mapstructure:"admin_state_up"`
-	Status       string `json:"status" mapstructure:"status"`
-	PolicyID     string `json:"firewall_policy_id" mapstructure:"firewall_policy_id"`
-	TenantID     string `json:"tenant_id" mapstructure:"tenant_id"`
+	ID           string `json:"id"`
+	Name         string `json:"name"`
+	Description  string `json:"description"`
+	AdminStateUp bool   `json:"admin_state_up"`
+	Status       string `json:"status"`
+	PolicyID     string `json:"firewall_policy_id"`
+	TenantID     string `json:"tenant_id"`
 }
 
 type commonResult struct {
@@ -22,17 +22,11 @@
 
 // Extract is a function that accepts a result and extracts a firewall.
 func (r commonResult) Extract() (*Firewall, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Firewall *Firewall `json:"firewall"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Firewall, err
+	err := r.ExtractInto(&s)
+	return s.Firewall, err
 }
 
 // FirewallPage is the page returned by a pager when traversing over a
@@ -44,40 +38,32 @@
 // NextPageURL is invoked when a paginated collection of firewalls has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p FirewallPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"firewalls_links"`
+func (r FirewallPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"firewalls_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a FirewallPage struct is empty.
-func (p FirewallPage) IsEmpty() (bool, error) {
-	is, err := ExtractFirewalls(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r FirewallPage) IsEmpty() (bool, error) {
+	is, err := ExtractFirewalls(r)
+	return len(is) == 0, err
 }
 
 // ExtractFirewalls accepts a Page struct, specifically a RouterPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractFirewalls(page pagination.Page) ([]Firewall, error) {
-	var resp struct {
-		Firewalls []Firewall `mapstructure:"firewalls" json:"firewalls"`
+func ExtractFirewalls(r pagination.Page) ([]Firewall, error) {
+	var s struct {
+		Firewalls []Firewall `json:"firewalls" json:"firewalls"`
 	}
-
-	err := mapstructure.Decode(page.(FirewallPage).Body, &resp)
-
-	return resp.Firewalls, err
+	err := (r.(FirewallPage)).ExtractInto(&s)
+	return s.Firewalls, err
 }
 
 // GetResult represents the result of a get operation.
diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/testing/doc.go b/openstack/networking/v2/extensions/fwaas/firewalls/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/firewalls/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go
new file mode 100644
index 0000000..01ab732
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go
@@ -0,0 +1,241 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+   "firewalls":[
+        {
+           "status": "ACTIVE",
+           "name": "fw1",
+           "admin_state_up": false,
+           "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
+           "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
+           "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
+           "description": "OpenStack firewall 1"
+        },
+        {
+           "status": "PENDING_UPDATE",
+           "name": "fw2",
+           "admin_state_up": true,
+           "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
+           "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e299",
+           "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f99",
+           "description": "OpenStack firewall 2"
+        }
+   ]
+}
+      `)
+	})
+
+	count := 0
+
+	firewalls.List(fake.ServiceClient(), firewalls.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := firewalls.ExtractFirewalls(page)
+		if err != nil {
+			t.Errorf("Failed to extract members: %v", err)
+			return false, err
+		}
+
+		expected := []firewalls.Firewall{
+			{
+				Status:       "ACTIVE",
+				Name:         "fw1",
+				AdminStateUp: false,
+				TenantID:     "b4eedccc6fb74fa8a7ad6b08382b852b",
+				PolicyID:     "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
+				ID:           "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
+				Description:  "OpenStack firewall 1",
+			},
+			{
+				Status:       "PENDING_UPDATE",
+				Name:         "fw2",
+				AdminStateUp: true,
+				TenantID:     "b4eedccc6fb74fa8a7ad6b08382b852b",
+				PolicyID:     "34be8c83-4d42-4dca-a74e-b77fffb8e299",
+				ID:           "fb5b5315-64f6-4ea3-8e58-981cc37c6f99",
+				Description:  "OpenStack firewall 2",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "firewall":{
+        "name": "fw",
+        "description": "OpenStack firewall",
+        "admin_state_up": true,
+        "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
+        "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b"
+    }
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "firewall":{
+        "status": "PENDING_CREATE",
+        "name": "fw",
+        "description": "OpenStack firewall",
+        "admin_state_up": true,
+        "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
+        "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
+    }
+}
+    `)
+	})
+
+	options := firewalls.CreateOpts{
+		TenantID:     "b4eedccc6fb74fa8a7ad6b08382b852b",
+		Name:         "fw",
+		Description:  "OpenStack firewall",
+		AdminStateUp: gophercloud.Enabled,
+		PolicyID:     "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
+	}
+	_, err := firewalls.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewalls/fb5b5315-64f6-4ea3-8e58-981cc37c6f61", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "firewall": {
+        "status": "ACTIVE",
+        "name": "fw",
+        "admin_state_up": true,
+        "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
+        "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
+        "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
+        "description": "OpenStack firewall"
+    }
+}
+        `)
+	})
+
+	fw, err := firewalls.Get(fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "ACTIVE", fw.Status)
+	th.AssertEquals(t, "fw", fw.Name)
+	th.AssertEquals(t, "OpenStack firewall", fw.Description)
+	th.AssertEquals(t, true, fw.AdminStateUp)
+	th.AssertEquals(t, "34be8c83-4d42-4dca-a74e-b77fffb8e28a", fw.PolicyID)
+	th.AssertEquals(t, "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", fw.ID)
+	th.AssertEquals(t, "b4eedccc6fb74fa8a7ad6b08382b852b", fw.TenantID)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewalls/ea5b5315-64f6-4ea3-8e58-981cc37c6576", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "firewall":{
+        "name": "fw",
+        "description": "updated fw",
+        "admin_state_up":false,
+        "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
+    }
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "firewall": {
+        "status": "ACTIVE",
+        "name": "fw",
+        "admin_state_up": false,
+        "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
+        "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
+        "id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576",
+        "description": "OpenStack firewall"
+    }
+}
+    `)
+	})
+
+	options := firewalls.UpdateOpts{
+		Name:         "fw",
+		Description:  "updated fw",
+		AdminStateUp: gophercloud.Disabled,
+		PolicyID:     "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
+	}
+
+	_, err := firewalls.Update(fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", options).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewalls/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := firewalls.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/urls.go b/openstack/networking/v2/extensions/fwaas/firewalls/urls.go
index 4dde530..807ea1a 100644
--- a/openstack/networking/v2/extensions/fwaas/firewalls/urls.go
+++ b/openstack/networking/v2/extensions/fwaas/firewalls/urls.go
@@ -1,6 +1,6 @@
 package firewalls
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	rootPath     = "fw"
diff --git a/openstack/networking/v2/extensions/fwaas/policies/requests.go b/openstack/networking/v2/extensions/fwaas/policies/requests.go
index fe07d9a..437d124 100644
--- a/openstack/networking/v2/extensions/fwaas/policies/requests.go
+++ b/openstack/networking/v2/extensions/fwaas/policies/requests.go
@@ -1,20 +1,8 @@
 package policies
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Binary gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Yes` and `No` enums
-type Binary *bool
-
-// Convenience vars for Audited and Shared values.
-var (
-	iTrue         = true
-	iFalse        = false
-	Yes    Binary = &iTrue
-	No     Binary = &iFalse
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -32,8 +20,8 @@
 	TenantID    string `q:"tenant_id"`
 	Name        string `q:"name"`
 	Description string `q:"description"`
-	Shared      bool   `q:"shared"`
-	Audited     bool   `q:"audited"`
+	Shared      *bool  `q:"shared"`
+	Audited     *bool  `q:"audited"`
 	ID          string `q:"id"`
 	Limit       int    `q:"limit"`
 	Marker      string `q:"marker"`
@@ -44,10 +32,7 @@
 // ToPolicyListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToPolicyListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -58,7 +43,6 @@
 // tenant who submits the request, unless an admin user submits the request.
 func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
 	url := rootURL(c)
-
 	if opts != nil {
 		query, err := opts.ToPolicyListQuery()
 		if err != nil {
@@ -66,7 +50,6 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return PolicyPage{pagination.LinkedPageBase{PageResult: r}}
 	})
@@ -77,66 +60,41 @@
 // extensions decorate or modify the common logic, it is useful for them to
 // satisfy a basic interface in order for them to be used.
 type CreateOptsBuilder interface {
-	ToPolicyCreateMap() (map[string]interface{}, error)
+	ToFirewallPolicyCreateMap() (map[string]interface{}, error)
 }
 
 // CreateOpts contains all the values needed to create a new firewall policy.
 type CreateOpts struct {
 	// Only required if the caller has an admin role and wants to create a firewall policy
 	// for another tenant.
-	TenantID    string
-	Name        string
-	Description string
-	Shared      *bool
-	Audited     *bool
-	Rules       []string
+	TenantID    string   `json:"tenant_id,omitempty"`
+	Name        string   `json:"name,omitempty"`
+	Description string   `json:"description,omitempty"`
+	Shared      *bool    `json:"shared,omitempty"`
+	Audited     *bool    `json:"audited,omitempty"`
+	Rules       []string `json:"firewall_rules,omitempty"`
 }
 
-// ToPolicyCreateMap casts a CreateOpts struct to a map.
-func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) {
-	p := make(map[string]interface{})
-
-	if opts.TenantID != "" {
-		p["tenant_id"] = opts.TenantID
-	}
-	if opts.Name != "" {
-		p["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		p["description"] = opts.Description
-	}
-	if opts.Shared != nil {
-		p["shared"] = *opts.Shared
-	}
-	if opts.Audited != nil {
-		p["audited"] = *opts.Audited
-	}
-	if opts.Rules != nil {
-		p["firewall_rules"] = opts.Rules
-	}
-
-	return map[string]interface{}{"firewall_policy": p}, nil
+// ToFirewallPolicyCreateMap casts a CreateOpts struct to a map.
+func (opts CreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "firewall_policy")
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new firewall policy
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToPolicyCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToFirewallPolicyCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular firewall policy based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -144,100 +102,72 @@
 // extensions decorate or modify the common logic, it is useful for them to
 // satisfy a basic interface in order for them to be used.
 type UpdateOptsBuilder interface {
-	ToPolicyUpdateMap() (map[string]interface{}, error)
+	ToFirewallPolicyUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains the values used when updating a firewall policy.
 type UpdateOpts struct {
-	// Name of the firewall policy.
-	Name        string
-	Description string
-	Shared      *bool
-	Audited     *bool
-	Rules       []string
+	Name        string   `json:"name,omitempty"`
+	Description string   `json:"description,omitempty"`
+	Shared      *bool    `json:"shared,omitempty"`
+	Audited     *bool    `json:"audited,omitempty"`
+	Rules       []string `json:"firewall_rules,omitempty"`
 }
 
-// ToPolicyUpdateMap casts a CreateOpts struct to a map.
-func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) {
-	p := make(map[string]interface{})
-
-	if opts.Name != "" {
-		p["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		p["description"] = opts.Description
-	}
-	if opts.Shared != nil {
-		p["shared"] = *opts.Shared
-	}
-	if opts.Audited != nil {
-		p["audited"] = *opts.Audited
-	}
-	if opts.Rules != nil {
-		p["firewall_rules"] = opts.Rules
-	}
-
-	return map[string]interface{}{"firewall_policy": p}, nil
+// ToFirewallPolicyUpdateMap casts a CreateOpts struct to a map.
+func (opts UpdateOpts) ToFirewallPolicyUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "firewall_policy")
 }
 
 // Update allows firewall policies to be updated.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToPolicyUpdateMap()
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToFirewallPolicyUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // Delete will permanently delete a particular firewall policy based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
 
-func InsertRule(c *gophercloud.ServiceClient, policyID, ruleID, beforeID, afterID string) error {
-	type request struct {
-		RuleId string `json:"firewall_rule_id"`
-		Before string `json:"insert_before,omitempty"`
-		After  string `json:"insert_after,omitempty"`
-	}
-
-	reqBody := request{
-		RuleId: ruleID,
-		Before: beforeID,
-		After:  afterID,
-	}
-
-	// Send request to API
-	var res commonResult
-	_, res.Err = c.Put(insertURL(c, policyID), reqBody, &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-	return res.Err
+type InsertRuleOptsBuilder interface {
+	ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error)
 }
 
-func RemoveRule(c *gophercloud.ServiceClient, policyID, ruleID string) error {
-	type request struct {
-		RuleId string `json:"firewall_rule_id"`
-	}
+type InsertRuleOpts struct {
+	ID           string `json:"firewall_rule_id" required:"true"`
+	BeforeRuleID string `json:"insert_before,omitempty"`
+	AfterRuleID  string `json:"insert_after,omitempty"`
+}
 
-	reqBody := request{
-		RuleId: ruleID,
-	}
+func (opts InsertRuleOpts) ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "")
+}
 
-	// Send request to API
-	var res commonResult
-	_, res.Err = c.Put(removeURL(c, policyID), reqBody, &res.Body, &gophercloud.RequestOpts{
+func AddRule(c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder) (r InsertRuleResult) {
+	b, err := opts.ToFirewallPolicyInsertRuleMap()
+	if err != nil {
+		r.Err = err
+		return
+	}
+	_, r.Err = c.Put(insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res.Err
+	return
+}
+
+func RemoveRule(c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) {
+	b := map[string]interface{}{"firewall_rule_id": ruleID}
+	_, r.Err = c.Put(removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
+		OkCodes: []int{200},
+	})
+	return
 }
diff --git a/openstack/networking/v2/extensions/fwaas/policies/requests_test.go b/openstack/networking/v2/extensions/fwaas/policies/requests_test.go
deleted file mode 100644
index b9d7865..0000000
--- a/openstack/networking/v2/extensions/fwaas/policies/requests_test.go
+++ /dev/null
@@ -1,279 +0,0 @@
-package policies
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/fw/firewall_policies", rootURL(fake.ServiceClient()))
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewall_policies", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "firewall_policies": [
-        {
-            "name": "policy1",
-            "firewall_rules": [
-                "75452b36-268e-4e75-aaf4-f0e7ed50bc97",
-                "c9e77ca0-1bc8-497d-904d-948107873dc6"
-            ],
-            "tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
-            "audited": true,
-			"shared": false,
-            "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
-            "description": "Firewall policy 1"
-        },
-        {
-            "name": "policy2",
-            "firewall_rules": [
-                "03d2a6ad-633f-431a-8463-4370d06a22c8"
-            ],
-            "tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
-            "audited": false,
-			"shared": true,
-            "id": "c854fab5-bdaf-4a86-9359-78de93e5df01",
-            "description": "Firewall policy 2"
-        }
-    ]
-}
-        `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractPolicies(page)
-		if err != nil {
-			t.Errorf("Failed to extract members: %v", err)
-			return false, err
-		}
-
-		expected := []Policy{
-			Policy{
-				Name: "policy1",
-				Rules: []string{
-					"75452b36-268e-4e75-aaf4-f0e7ed50bc97",
-					"c9e77ca0-1bc8-497d-904d-948107873dc6",
-				},
-				TenantID:    "9145d91459d248b1b02fdaca97c6a75d",
-				Audited:     true,
-				Shared:      false,
-				ID:          "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
-				Description: "Firewall policy 1",
-			},
-			Policy{
-				Name: "policy2",
-				Rules: []string{
-					"03d2a6ad-633f-431a-8463-4370d06a22c8",
-				},
-				TenantID:    "9145d91459d248b1b02fdaca97c6a75d",
-				Audited:     false,
-				Shared:      true,
-				ID:          "c854fab5-bdaf-4a86-9359-78de93e5df01",
-				Description: "Firewall policy 2",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewall_policies", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "firewall_policy":{
-        "name": "policy",
-        "firewall_rules": [
-            "98a58c87-76be-ae7c-a74e-b77fffb88d95",
-            "11a58c87-76be-ae7c-a74e-b77fffb88a32"
-        ],
-        "description": "Firewall policy",
-		"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
-		"audited": true,
-		"shared": false
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "firewall_policy":{
-        "name": "policy",
-        "firewall_rules": [
-            "98a58c87-76be-ae7c-a74e-b77fffb88d95",
-            "11a58c87-76be-ae7c-a74e-b77fffb88a32"
-        ],
-        "tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
-        "audited": false,
-        "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
-        "description": "Firewall policy"
-    }
-}
-        `)
-	})
-
-	options := CreateOpts{
-		TenantID:    "9145d91459d248b1b02fdaca97c6a75d",
-		Name:        "policy",
-		Description: "Firewall policy",
-		Shared:      No,
-		Audited:     Yes,
-		Rules: []string{
-			"98a58c87-76be-ae7c-a74e-b77fffb88d95",
-			"11a58c87-76be-ae7c-a74e-b77fffb88a32",
-		},
-	}
-
-	_, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewall_policies/bcab5315-64f6-4ea3-8e58-981cc37c6f61", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "firewall_policy":{
-        "name": "www",
-        "firewall_rules": [
-            "75452b36-268e-4e75-aaf4-f0e7ed50bc97",
-            "c9e77ca0-1bc8-497d-904d-948107873dc6",
-            "03d2a6ad-633f-431a-8463-4370d06a22c8"
-        ],
-        "tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
-        "audited": false,
-        "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
-        "description": "Firewall policy web"
-    }
-}
-        `)
-	})
-
-	policy, err := Get(fake.ServiceClient(), "bcab5315-64f6-4ea3-8e58-981cc37c6f61").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "www", policy.Name)
-	th.AssertEquals(t, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", policy.ID)
-	th.AssertEquals(t, "Firewall policy web", policy.Description)
-	th.AssertEquals(t, 3, len(policy.Rules))
-	th.AssertEquals(t, "75452b36-268e-4e75-aaf4-f0e7ed50bc97", policy.Rules[0])
-	th.AssertEquals(t, "c9e77ca0-1bc8-497d-904d-948107873dc6", policy.Rules[1])
-	th.AssertEquals(t, "03d2a6ad-633f-431a-8463-4370d06a22c8", policy.Rules[2])
-	th.AssertEquals(t, "9145d91459d248b1b02fdaca97c6a75d", policy.TenantID)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewall_policies/f2b08c1e-aa81-4668-8ae1-1401bcb0576c", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "firewall_policy":{
-        "name": "policy",
-        "firewall_rules": [
-            "98a58c87-76be-ae7c-a74e-b77fffb88d95",
-            "11a58c87-76be-ae7c-a74e-b77fffb88a32"
-        ],
-        "description": "Firewall policy"
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "firewall_policy":{
-        "name": "policy",
-        "firewall_rules": [
-            "75452b36-268e-4e75-aaf4-f0e7ed50bc97",
-            "c9e77ca0-1bc8-497d-904d-948107873dc6",
-            "03d2a6ad-633f-431a-8463-4370d06a22c8"
-        ],
-        "tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
-        "audited": false,
-        "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
-        "description": "Firewall policy"
-    }
-}
-    `)
-	})
-
-	options := UpdateOpts{
-		Name:        "policy",
-		Description: "Firewall policy",
-		Rules: []string{
-			"98a58c87-76be-ae7c-a74e-b77fffb88d95",
-			"11a58c87-76be-ae7c-a74e-b77fffb88a32",
-		},
-	}
-
-	_, err := Update(fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", options).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewall_policies/4ec89077-d057-4a2b-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/extensions/fwaas/policies/results.go b/openstack/networking/v2/extensions/fwaas/policies/results.go
index a9a0c35..9c5b186 100644
--- a/openstack/networking/v2/extensions/fwaas/policies/results.go
+++ b/openstack/networking/v2/extensions/fwaas/policies/results.go
@@ -1,19 +1,19 @@
 package policies
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
+// Policy is a firewall policy.
 type Policy struct {
-	ID          string   `json:"id" mapstructure:"id"`
-	Name        string   `json:"name" mapstructure:"name"`
-	Description string   `json:"description" mapstructure:"description"`
-	TenantID    string   `json:"tenant_id" mapstructure:"tenant_id"`
-	Audited     bool     `json:"audited" mapstructure:"audited"`
-	Shared      bool     `json:"shared" mapstructure:"shared"`
-	Rules       []string `json:"firewall_rules,omitempty" mapstructure:"firewall_rules"`
+	ID          string   `json:"id"`
+	Name        string   `json:"name"`
+	Description string   `json:"description"`
+	TenantID    string   `json:"tenant_id"`
+	Audited     bool     `json:"audited"`
+	Shared      bool     `json:"shared"`
+	Rules       []string `json:"firewall_rules,omitempty"`
 }
 
 type commonResult struct {
@@ -22,17 +22,11 @@
 
 // Extract is a function that accepts a result and extracts a firewall policy.
 func (r commonResult) Extract() (*Policy, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Policy *Policy `json:"firewall_policy"`
 	}
-
-	var res struct {
-		Policy *Policy `json:"firewall_policy" mapstructure:"firewall_policy"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Policy, err
+	err := r.ExtractInto(&s)
+	return s.Policy, err
 }
 
 // PolicyPage is the page returned by a pager when traversing over a
@@ -44,40 +38,32 @@
 // NextPageURL is invoked when a paginated collection of firewall policies has
 // reached the end of a page and the pager seeks to traverse over a new one.
 // In order to do this, it needs to construct the next page's URL.
-func (p PolicyPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"firewall_policies_links"`
+func (r PolicyPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"firewall_policies_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a PolicyPage struct is empty.
-func (p PolicyPage) IsEmpty() (bool, error) {
-	is, err := ExtractPolicies(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r PolicyPage) IsEmpty() (bool, error) {
+	is, err := ExtractPolicies(r)
+	return len(is) == 0, err
 }
 
 // ExtractPolicies accepts a Page struct, specifically a RouterPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractPolicies(page pagination.Page) ([]Policy, error) {
-	var resp struct {
-		Policies []Policy `mapstructure:"firewall_policies" json:"firewall_policies"`
+func ExtractPolicies(r pagination.Page) ([]Policy, error) {
+	var s struct {
+		Policies []Policy `json:"firewall_policies"`
 	}
-
-	err := mapstructure.Decode(page.(PolicyPage).Body, &resp)
-
-	return resp.Policies, err
+	err := (r.(PolicyPage)).ExtractInto(&s)
+	return s.Policies, err
 }
 
 // GetResult represents the result of a get operation.
@@ -99,3 +85,13 @@
 type CreateResult struct {
 	commonResult
 }
+
+// InsertRuleResult represents the result of an InsertRule operation.
+type InsertRuleResult struct {
+	commonResult
+}
+
+// RemoveRuleResult represents the result of a RemoveRule operation.
+type RemoveRuleResult struct {
+	commonResult
+}
diff --git a/openstack/networking/v2/extensions/fwaas/policies/testing/doc.go b/openstack/networking/v2/extensions/fwaas/policies/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/policies/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go
new file mode 100644
index 0000000..11b9848
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go
@@ -0,0 +1,274 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewall_policies", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "firewall_policies": [
+        {
+            "name": "policy1",
+            "firewall_rules": [
+                "75452b36-268e-4e75-aaf4-f0e7ed50bc97",
+                "c9e77ca0-1bc8-497d-904d-948107873dc6"
+            ],
+            "tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
+            "audited": true,
+			"shared": false,
+            "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
+            "description": "Firewall policy 1"
+        },
+        {
+            "name": "policy2",
+            "firewall_rules": [
+                "03d2a6ad-633f-431a-8463-4370d06a22c8"
+            ],
+            "tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
+            "audited": false,
+			"shared": true,
+            "id": "c854fab5-bdaf-4a86-9359-78de93e5df01",
+            "description": "Firewall policy 2"
+        }
+    ]
+}
+        `)
+	})
+
+	count := 0
+
+	policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := policies.ExtractPolicies(page)
+		if err != nil {
+			t.Errorf("Failed to extract members: %v", err)
+			return false, err
+		}
+
+		expected := []policies.Policy{
+			{
+				Name: "policy1",
+				Rules: []string{
+					"75452b36-268e-4e75-aaf4-f0e7ed50bc97",
+					"c9e77ca0-1bc8-497d-904d-948107873dc6",
+				},
+				TenantID:    "9145d91459d248b1b02fdaca97c6a75d",
+				Audited:     true,
+				Shared:      false,
+				ID:          "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
+				Description: "Firewall policy 1",
+			},
+			{
+				Name: "policy2",
+				Rules: []string{
+					"03d2a6ad-633f-431a-8463-4370d06a22c8",
+				},
+				TenantID:    "9145d91459d248b1b02fdaca97c6a75d",
+				Audited:     false,
+				Shared:      true,
+				ID:          "c854fab5-bdaf-4a86-9359-78de93e5df01",
+				Description: "Firewall policy 2",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewall_policies", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "firewall_policy":{
+        "name": "policy",
+        "firewall_rules": [
+            "98a58c87-76be-ae7c-a74e-b77fffb88d95",
+            "11a58c87-76be-ae7c-a74e-b77fffb88a32"
+        ],
+        "description": "Firewall policy",
+		"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
+		"audited": true,
+		"shared": false
+    }
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "firewall_policy":{
+        "name": "policy",
+        "firewall_rules": [
+            "98a58c87-76be-ae7c-a74e-b77fffb88d95",
+            "11a58c87-76be-ae7c-a74e-b77fffb88a32"
+        ],
+        "tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
+        "audited": false,
+        "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
+        "description": "Firewall policy"
+    }
+}
+        `)
+	})
+
+	options := policies.CreateOpts{
+		TenantID:    "9145d91459d248b1b02fdaca97c6a75d",
+		Name:        "policy",
+		Description: "Firewall policy",
+		Shared:      gophercloud.Disabled,
+		Audited:     gophercloud.Enabled,
+		Rules: []string{
+			"98a58c87-76be-ae7c-a74e-b77fffb88d95",
+			"11a58c87-76be-ae7c-a74e-b77fffb88a32",
+		},
+	}
+
+	_, err := policies.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewall_policies/bcab5315-64f6-4ea3-8e58-981cc37c6f61", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "firewall_policy":{
+        "name": "www",
+        "firewall_rules": [
+            "75452b36-268e-4e75-aaf4-f0e7ed50bc97",
+            "c9e77ca0-1bc8-497d-904d-948107873dc6",
+            "03d2a6ad-633f-431a-8463-4370d06a22c8"
+        ],
+        "tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
+        "audited": false,
+        "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
+        "description": "Firewall policy web"
+    }
+}
+        `)
+	})
+
+	policy, err := policies.Get(fake.ServiceClient(), "bcab5315-64f6-4ea3-8e58-981cc37c6f61").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "www", policy.Name)
+	th.AssertEquals(t, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", policy.ID)
+	th.AssertEquals(t, "Firewall policy web", policy.Description)
+	th.AssertEquals(t, 3, len(policy.Rules))
+	th.AssertEquals(t, "75452b36-268e-4e75-aaf4-f0e7ed50bc97", policy.Rules[0])
+	th.AssertEquals(t, "c9e77ca0-1bc8-497d-904d-948107873dc6", policy.Rules[1])
+	th.AssertEquals(t, "03d2a6ad-633f-431a-8463-4370d06a22c8", policy.Rules[2])
+	th.AssertEquals(t, "9145d91459d248b1b02fdaca97c6a75d", policy.TenantID)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewall_policies/f2b08c1e-aa81-4668-8ae1-1401bcb0576c", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "firewall_policy":{
+        "name": "policy",
+        "firewall_rules": [
+            "98a58c87-76be-ae7c-a74e-b77fffb88d95",
+            "11a58c87-76be-ae7c-a74e-b77fffb88a32"
+        ],
+        "description": "Firewall policy"
+    }
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "firewall_policy":{
+        "name": "policy",
+        "firewall_rules": [
+            "75452b36-268e-4e75-aaf4-f0e7ed50bc97",
+            "c9e77ca0-1bc8-497d-904d-948107873dc6",
+            "03d2a6ad-633f-431a-8463-4370d06a22c8"
+        ],
+        "tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
+        "audited": false,
+        "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
+        "description": "Firewall policy"
+    }
+}
+    `)
+	})
+
+	options := policies.UpdateOpts{
+		Name:        "policy",
+		Description: "Firewall policy",
+		Rules: []string{
+			"98a58c87-76be-ae7c-a74e-b77fffb88d95",
+			"11a58c87-76be-ae7c-a74e-b77fffb88a32",
+		},
+	}
+
+	_, err := policies.Update(fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", options).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewall_policies/4ec89077-d057-4a2b-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := policies.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/extensions/fwaas/policies/urls.go b/openstack/networking/v2/extensions/fwaas/policies/urls.go
index 27ea9ae..c252b79 100644
--- a/openstack/networking/v2/extensions/fwaas/policies/urls.go
+++ b/openstack/networking/v2/extensions/fwaas/policies/urls.go
@@ -1,6 +1,6 @@
 package policies
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	rootPath     = "fw"
diff --git a/openstack/networking/v2/extensions/fwaas/rules/requests.go b/openstack/networking/v2/extensions/fwaas/rules/requests.go
index 57a0e8b..6b0814c 100644
--- a/openstack/networking/v2/extensions/fwaas/rules/requests.go
+++ b/openstack/networking/v2/extensions/fwaas/rules/requests.go
@@ -1,20 +1,8 @@
 package rules
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Binary gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Yes` and `No` enums
-type Binary *bool
-
-// Convenience vars for Enabled and Shared values.
-var (
-	iTrue         = true
-	iFalse        = false
-	Yes    Binary = &iTrue
-	No     Binary = &iFalse
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -88,90 +76,40 @@
 
 // CreateOpts contains all the values needed to create a new firewall rule.
 type CreateOpts struct {
-	// Mandatory for create
-	Protocol string
-	Action   string
-	// Optional
-	TenantID             string
-	Name                 string
-	Description          string
-	IPVersion            int
-	SourceIPAddress      string
-	DestinationIPAddress string
-	SourcePort           string
-	DestinationPort      string
-	Shared               *bool
-	Enabled              *bool
+	Protocol             string                `json:"protocol" required:"true"`
+	Action               string                `json:"action" required:"true"`
+	TenantID             string                `json:"tenant_id,omitempty"`
+	Name                 string                `json:"name,omitempty"`
+	Description          string                `json:"description,omitempty"`
+	IPVersion            gophercloud.IPVersion `json:"ip_version,omitempty"`
+	SourceIPAddress      string                `json:"source_ip_address,omitempty"`
+	DestinationIPAddress string                `json:"destination_ip_address,omitempty"`
+	SourcePort           string                `json:"source_port,omitempty"`
+	DestinationPort      string                `json:"destination_port,omitempty"`
+	Shared               *bool                 `json:"shared,omitempty"`
+	Enabled              *bool                 `json:"enabled,omitempty"`
 }
 
 // ToRuleCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
-	if opts.Protocol == "" {
-		return nil, errProtocolRequired
-	}
-
-	if opts.Action == "" {
-		return nil, errActionRequired
-	}
-
-	r := make(map[string]interface{})
-
-	r["protocol"] = opts.Protocol
-	r["action"] = opts.Action
-
-	if opts.TenantID != "" {
-		r["tenant_id"] = opts.TenantID
-	}
-	if opts.Name != "" {
-		r["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		r["description"] = opts.Description
-	}
-	if opts.IPVersion != 0 {
-		r["ip_version"] = opts.IPVersion
-	}
-	if opts.SourceIPAddress != "" {
-		r["source_ip_address"] = opts.SourceIPAddress
-	}
-	if opts.DestinationIPAddress != "" {
-		r["destination_ip_address"] = opts.DestinationIPAddress
-	}
-	if opts.SourcePort != "" {
-		r["source_port"] = opts.SourcePort
-	}
-	if opts.DestinationPort != "" {
-		r["destination_port"] = opts.DestinationPort
-	}
-	if opts.Shared != nil {
-		r["shared"] = *opts.Shared
-	}
-	if opts.Enabled != nil {
-		r["enabled"] = *opts.Enabled
-	}
-
-	return map[string]interface{}{"firewall_rule": r}, nil
+	return gophercloud.BuildRequestBody(opts, "firewall_rule")
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new firewall rule
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToRuleCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToRuleCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular firewall rule based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -183,103 +121,40 @@
 }
 
 // UpdateOpts contains the values used when updating a firewall rule.
-// Optional
 type UpdateOpts struct {
-	Protocol             string
-	Action               string
-	Name                 string
-	Description          string
-	IPVersion            int
-	SourceIPAddress      *string
-	DestinationIPAddress *string
-	SourcePort           *string
-	DestinationPort      *string
-	Shared               *bool
-	Enabled              *bool
+	Protocol             *string                `json:"protocol,omitempty"`
+	Action               *string                `json:"action,omitempty"`
+	Name                 *string                `json:"name,omitempty"`
+	Description          *string                `json:"description,omitempty"`
+	IPVersion            *gophercloud.IPVersion `json:"ip_version,omitempty"`
+	SourceIPAddress      *string                `json:"source_ip_address,omitempty"`
+	DestinationIPAddress *string                `json:"destination_ip_address,omitempty"`
+	SourcePort           *string                `json:"source_port,omitempty"`
+	DestinationPort      *string                `json:"destination_port,omitempty"`
+	Shared               *bool                  `json:"shared,omitempty"`
+	Enabled              *bool                  `json:"enabled,omitempty"`
 }
 
 // ToRuleUpdateMap casts a UpdateOpts struct to a map.
 func (opts UpdateOpts) ToRuleUpdateMap() (map[string]interface{}, error) {
-	r := make(map[string]interface{})
-
-	if opts.Protocol != "" {
-		r["protocol"] = opts.Protocol
-	}
-	if opts.Action != "" {
-		r["action"] = opts.Action
-	}
-	if opts.Name != "" {
-		r["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		r["description"] = opts.Description
-	}
-	if opts.IPVersion != 0 {
-		r["ip_version"] = opts.IPVersion
-	}
-	if opts.SourceIPAddress != nil {
-		s := *opts.SourceIPAddress
-		if s == "" {
-			r["source_ip_address"] = nil
-		} else {
-			r["source_ip_address"] = s
-		}
-	}
-	if opts.DestinationIPAddress != nil {
-		s := *opts.DestinationIPAddress
-		if s == "" {
-			r["destination_ip_address"] = nil
-		} else {
-			r["destination_ip_address"] = s
-		}
-	}
-	if opts.SourcePort != nil {
-		s := *opts.SourcePort
-		if s == "" {
-			r["source_port"] = nil
-		} else {
-			r["source_port"] = s
-		}
-	}
-	if opts.DestinationPort != nil {
-		s := *opts.DestinationPort
-		if s == "" {
-			r["destination_port"] = nil
-		} else {
-			r["destination_port"] = s
-		}
-	}
-	if opts.Shared != nil {
-		r["shared"] = *opts.Shared
-	}
-	if opts.Enabled != nil {
-		r["enabled"] = *opts.Enabled
-	}
-
-	return map[string]interface{}{"firewall_rule": r}, nil
+	return gophercloud.BuildRequestBody(opts, "firewall_rule")
 }
 
 // Update allows firewall policies to be updated.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToRuleUpdateMap()
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToRuleUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return res
+	return
 }
 
 // Delete will permanently delete a particular firewall rule based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/fwaas/rules/requests_test.go b/openstack/networking/v2/extensions/fwaas/rules/requests_test.go
deleted file mode 100644
index 36f89fa..0000000
--- a/openstack/networking/v2/extensions/fwaas/rules/requests_test.go
+++ /dev/null
@@ -1,328 +0,0 @@
-package rules
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/fw/firewall_rules", rootURL(fake.ServiceClient()))
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "firewall_rules": [
-        {
-            "protocol": "tcp",
-            "description": "ssh rule",
-            "source_port": null,
-            "source_ip_address": null,
-            "destination_ip_address": "192.168.1.0/24",
-            "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
-            "position": 2,
-            "destination_port": "22",
-            "id": "f03bd950-6c56-4f5e-a307-45967078f507",
-            "name": "ssh_form_any",
-            "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
-            "enabled": true,
-            "action": "allow",
-            "ip_version": 4,
-            "shared": false
-        },
-        {
-            "protocol": "udp",
-            "description": "udp rule",
-            "source_port": null,
-            "source_ip_address": null,
-            "destination_ip_address": null,
-            "firewall_policy_id": "98d7fb51-698c-4123-87e8-f1eee6b5ab7e",
-            "position": 1,
-            "destination_port": null,
-            "id": "ab7bd950-6c56-4f5e-a307-45967078f890",
-            "name": "deny_all_udp",
-            "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
-            "enabled": true,
-            "action": "deny",
-            "ip_version": 4,
-            "shared": false
-        }
-    ]
-}
-        `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractRules(page)
-		if err != nil {
-			t.Errorf("Failed to extract members: %v", err)
-			return false, err
-		}
-
-		expected := []Rule{
-			Rule{
-				Protocol:             "tcp",
-				Description:          "ssh rule",
-				SourcePort:           "",
-				SourceIPAddress:      "",
-				DestinationIPAddress: "192.168.1.0/24",
-				PolicyID:             "e2a5fb51-698c-4898-87e8-f1eee6b50919",
-				Position:             2,
-				DestinationPort:      "22",
-				ID:                   "f03bd950-6c56-4f5e-a307-45967078f507",
-				Name:                 "ssh_form_any",
-				TenantID:             "80cf934d6ffb4ef5b244f1c512ad1e61",
-				Enabled:              true,
-				Action:               "allow",
-				IPVersion:            4,
-				Shared:               false,
-			},
-			Rule{
-				Protocol:             "udp",
-				Description:          "udp rule",
-				SourcePort:           "",
-				SourceIPAddress:      "",
-				DestinationIPAddress: "",
-				PolicyID:             "98d7fb51-698c-4123-87e8-f1eee6b5ab7e",
-				Position:             1,
-				DestinationPort:      "",
-				ID:                   "ab7bd950-6c56-4f5e-a307-45967078f890",
-				Name:                 "deny_all_udp",
-				TenantID:             "80cf934d6ffb4ef5b244f1c512ad1e61",
-				Enabled:              true,
-				Action:               "deny",
-				IPVersion:            4,
-				Shared:               false,
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-	"firewall_rule": {
-		"protocol": "tcp",
-		"description": "ssh rule",
-		"destination_ip_address": "192.168.1.0/24",
-		"destination_port": "22",
-		"name": "ssh_form_any",
-		"action": "allow",
-		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61"
-	}
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-	"firewall_rule":{
-		"protocol": "tcp",
-		"description": "ssh rule",
-		"source_port": null,
-		"source_ip_address": null,
-		"destination_ip_address": "192.168.1.0/24",
-		"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
-		"position": 2,
-		"destination_port": "22",
-		"id": "f03bd950-6c56-4f5e-a307-45967078f507",
-		"name": "ssh_form_any",
-		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
-		"enabled": true,
-		"action": "allow",
-		"ip_version": 4,
-		"shared": false
-	}
-}
-        `)
-	})
-
-	options := CreateOpts{
-		TenantID:             "80cf934d6ffb4ef5b244f1c512ad1e61",
-		Protocol:             "tcp",
-		Description:          "ssh rule",
-		DestinationIPAddress: "192.168.1.0/24",
-		DestinationPort:      "22",
-		Name:                 "ssh_form_any",
-		Action:               "allow",
-	}
-
-	_, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-	"firewall_rule":{
-		"protocol": "tcp",
-		"description": "ssh rule",
-		"source_port": null,
-		"source_ip_address": null,
-		"destination_ip_address": "192.168.1.0/24",
-		"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
-		"position": 2,
-		"destination_port": "22",
-		"id": "f03bd950-6c56-4f5e-a307-45967078f507",
-		"name": "ssh_form_any",
-		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
-		"enabled": true,
-		"action": "allow",
-		"ip_version": 4,
-		"shared": false
-	}
-}
-        `)
-	})
-
-	rule, err := Get(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "tcp", rule.Protocol)
-	th.AssertEquals(t, "ssh rule", rule.Description)
-	th.AssertEquals(t, "192.168.1.0/24", rule.DestinationIPAddress)
-	th.AssertEquals(t, "e2a5fb51-698c-4898-87e8-f1eee6b50919", rule.PolicyID)
-	th.AssertEquals(t, 2, rule.Position)
-	th.AssertEquals(t, "22", rule.DestinationPort)
-	th.AssertEquals(t, "f03bd950-6c56-4f5e-a307-45967078f507", rule.ID)
-	th.AssertEquals(t, "ssh_form_any", rule.Name)
-	th.AssertEquals(t, "80cf934d6ffb4ef5b244f1c512ad1e61", rule.TenantID)
-	th.AssertEquals(t, true, rule.Enabled)
-	th.AssertEquals(t, "allow", rule.Action)
-	th.AssertEquals(t, 4, rule.IPVersion)
-	th.AssertEquals(t, false, rule.Shared)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-	"firewall_rule":{
-		"protocol": "tcp",
-		"description": "ssh rule",
-		"destination_ip_address": "192.168.1.0/24",
-		"destination_port": "22",
-		"source_ip_address": null,
-		"source_port": null,
-		"name": "ssh_form_any",
-		"action": "allow",
-		"enabled": false
-	}
-}
-	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-	"firewall_rule":{
-		"protocol": "tcp",
-		"description": "ssh rule",
-		"source_port": null,
-		"source_ip_address": null,
-		"destination_ip_address": "192.168.1.0/24",
-		"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
-		"position": 2,
-		"destination_port": "22",
-		"id": "f03bd950-6c56-4f5e-a307-45967078f507",
-		"name": "ssh_form_any",
-		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
-		"enabled": false,
-		"action": "allow",
-		"ip_version": 4,
-		"shared": false
-	}
-}
-		`)
-	})
-
-	destinationIPAddress := "192.168.1.0/24"
-	destinationPort := "22"
-	empty := ""
-
-	options := UpdateOpts{
-		Protocol:             "tcp",
-		Description:          "ssh rule",
-		DestinationIPAddress: &destinationIPAddress,
-		DestinationPort:      &destinationPort,
-		Name:                 "ssh_form_any",
-		SourceIPAddress:      &empty,
-		SourcePort:           &empty,
-		Action:               "allow",
-		Enabled:              No,
-	}
-
-	_, err := Update(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/fw/firewall_rules/4ec89077-d057-4a2b-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/extensions/fwaas/rules/results.go b/openstack/networking/v2/extensions/fwaas/rules/results.go
index d772024..c44e5a9 100644
--- a/openstack/networking/v2/extensions/fwaas/rules/results.go
+++ b/openstack/networking/v2/extensions/fwaas/rules/results.go
@@ -1,28 +1,27 @@
 package rules
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Rule represents a firewall rule
 type Rule struct {
-	ID                   string `json:"id" mapstructure:"id"`
-	Name                 string `json:"name,omitempty" mapstructure:"name"`
-	Description          string `json:"description,omitempty" mapstructure:"description"`
-	Protocol             string `json:"protocol" mapstructure:"protocol"`
-	Action               string `json:"action" mapstructure:"action"`
-	IPVersion            int    `json:"ip_version,omitempty" mapstructure:"ip_version"`
-	SourceIPAddress      string `json:"source_ip_address,omitempty" mapstructure:"source_ip_address"`
-	DestinationIPAddress string `json:"destination_ip_address,omitempty" mapstructure:"destination_ip_address"`
-	SourcePort           string `json:"source_port,omitempty" mapstructure:"source_port"`
-	DestinationPort      string `json:"destination_port,omitempty" mapstructure:"destination_port"`
-	Shared               bool   `json:"shared,omitempty" mapstructure:"shared"`
-	Enabled              bool   `json:"enabled,omitempty" mapstructure:"enabled"`
-	PolicyID             string `json:"firewall_policy_id" mapstructure:"firewall_policy_id"`
-	Position             int    `json:"position" mapstructure:"position"`
-	TenantID             string `json:"tenant_id" mapstructure:"tenant_id"`
+	ID                   string `json:"id"`
+	Name                 string `json:"name,omitempty"`
+	Description          string `json:"description,omitempty"`
+	Protocol             string `json:"protocol"`
+	Action               string `json:"action"`
+	IPVersion            int    `json:"ip_version,omitempty"`
+	SourceIPAddress      string `json:"source_ip_address,omitempty"`
+	DestinationIPAddress string `json:"destination_ip_address,omitempty"`
+	SourcePort           string `json:"source_port,omitempty"`
+	DestinationPort      string `json:"destination_port,omitempty"`
+	Shared               bool   `json:"shared,omitempty"`
+	Enabled              bool   `json:"enabled,omitempty"`
+	PolicyID             string `json:"firewall_policy_id"`
+	Position             int    `json:"position"`
+	TenantID             string `json:"tenant_id"`
 }
 
 // RulePage is the page returned by a pager when traversing over a
@@ -34,40 +33,32 @@
 // NextPageURL is invoked when a paginated collection of firewall rules has
 // reached the end of a page and the pager seeks to traverse over a new one.
 // In order to do this, it needs to construct the next page's URL.
-func (p RulePage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"firewall_rules_links"`
+func (r RulePage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"firewall_rules_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a RulePage struct is empty.
-func (p RulePage) IsEmpty() (bool, error) {
-	is, err := ExtractRules(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r RulePage) IsEmpty() (bool, error) {
+	is, err := ExtractRules(r)
+	return len(is) == 0, err
 }
 
 // ExtractRules accepts a Page struct, specifically a RouterPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractRules(page pagination.Page) ([]Rule, error) {
-	var resp struct {
-		Rules []Rule `mapstructure:"firewall_rules" json:"firewall_rules"`
+func ExtractRules(r pagination.Page) ([]Rule, error) {
+	var s struct {
+		Rules []Rule `json:"firewall_rules"`
 	}
-
-	err := mapstructure.Decode(page.(RulePage).Body, &resp)
-
-	return resp.Rules, err
+	err := (r.(RulePage)).ExtractInto(&s)
+	return s.Rules, err
 }
 
 type commonResult struct {
@@ -76,17 +67,11 @@
 
 // Extract is a function that accepts a result and extracts a firewall rule.
 func (r commonResult) Extract() (*Rule, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Rule *Rule `json:"firewall_rule"`
 	}
-
-	var res struct {
-		Rule *Rule `json:"firewall_rule" mapstructure:"firewall_rule"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Rule, err
+	err := r.ExtractInto(&s)
+	return s.Rule, err
 }
 
 // GetResult represents the result of a get operation.
diff --git a/openstack/networking/v2/extensions/fwaas/rules/testing/doc.go b/openstack/networking/v2/extensions/fwaas/rules/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/rules/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go
new file mode 100644
index 0000000..dbf377a
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go
@@ -0,0 +1,320 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "firewall_rules": [
+        {
+            "protocol": "tcp",
+            "description": "ssh rule",
+            "source_port": null,
+            "source_ip_address": null,
+            "destination_ip_address": "192.168.1.0/24",
+            "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
+            "position": 2,
+            "destination_port": "22",
+            "id": "f03bd950-6c56-4f5e-a307-45967078f507",
+            "name": "ssh_form_any",
+            "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
+            "enabled": true,
+            "action": "allow",
+            "ip_version": 4,
+            "shared": false
+        },
+        {
+            "protocol": "udp",
+            "description": "udp rule",
+            "source_port": null,
+            "source_ip_address": null,
+            "destination_ip_address": null,
+            "firewall_policy_id": "98d7fb51-698c-4123-87e8-f1eee6b5ab7e",
+            "position": 1,
+            "destination_port": null,
+            "id": "ab7bd950-6c56-4f5e-a307-45967078f890",
+            "name": "deny_all_udp",
+            "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
+            "enabled": true,
+            "action": "deny",
+            "ip_version": 4,
+            "shared": false
+        }
+    ]
+}
+        `)
+	})
+
+	count := 0
+
+	rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := rules.ExtractRules(page)
+		if err != nil {
+			t.Errorf("Failed to extract members: %v", err)
+			return false, err
+		}
+
+		expected := []rules.Rule{
+			{
+				Protocol:             "tcp",
+				Description:          "ssh rule",
+				SourcePort:           "",
+				SourceIPAddress:      "",
+				DestinationIPAddress: "192.168.1.0/24",
+				PolicyID:             "e2a5fb51-698c-4898-87e8-f1eee6b50919",
+				Position:             2,
+				DestinationPort:      "22",
+				ID:                   "f03bd950-6c56-4f5e-a307-45967078f507",
+				Name:                 "ssh_form_any",
+				TenantID:             "80cf934d6ffb4ef5b244f1c512ad1e61",
+				Enabled:              true,
+				Action:               "allow",
+				IPVersion:            4,
+				Shared:               false,
+			},
+			{
+				Protocol:             "udp",
+				Description:          "udp rule",
+				SourcePort:           "",
+				SourceIPAddress:      "",
+				DestinationIPAddress: "",
+				PolicyID:             "98d7fb51-698c-4123-87e8-f1eee6b5ab7e",
+				Position:             1,
+				DestinationPort:      "",
+				ID:                   "ab7bd950-6c56-4f5e-a307-45967078f890",
+				Name:                 "deny_all_udp",
+				TenantID:             "80cf934d6ffb4ef5b244f1c512ad1e61",
+				Enabled:              true,
+				Action:               "deny",
+				IPVersion:            4,
+				Shared:               false,
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+	"firewall_rule": {
+		"protocol": "tcp",
+		"description": "ssh rule",
+		"destination_ip_address": "192.168.1.0/24",
+		"destination_port": "22",
+		"name": "ssh_form_any",
+		"action": "allow",
+		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61"
+	}
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+	"firewall_rule":{
+		"protocol": "tcp",
+		"description": "ssh rule",
+		"source_port": null,
+		"source_ip_address": null,
+		"destination_ip_address": "192.168.1.0/24",
+		"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
+		"position": 2,
+		"destination_port": "22",
+		"id": "f03bd950-6c56-4f5e-a307-45967078f507",
+		"name": "ssh_form_any",
+		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
+		"enabled": true,
+		"action": "allow",
+		"ip_version": 4,
+		"shared": false
+	}
+}
+        `)
+	})
+
+	options := rules.CreateOpts{
+		TenantID:             "80cf934d6ffb4ef5b244f1c512ad1e61",
+		Protocol:             "tcp",
+		Description:          "ssh rule",
+		DestinationIPAddress: "192.168.1.0/24",
+		DestinationPort:      "22",
+		Name:                 "ssh_form_any",
+		Action:               "allow",
+	}
+
+	_, err := rules.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+	"firewall_rule":{
+		"protocol": "tcp",
+		"description": "ssh rule",
+		"source_port": null,
+		"source_ip_address": null,
+		"destination_ip_address": "192.168.1.0/24",
+		"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
+		"position": 2,
+		"destination_port": "22",
+		"id": "f03bd950-6c56-4f5e-a307-45967078f507",
+		"name": "ssh_form_any",
+		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
+		"enabled": true,
+		"action": "allow",
+		"ip_version": 4,
+		"shared": false
+	}
+}
+        `)
+	})
+
+	rule, err := rules.Get(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "tcp", rule.Protocol)
+	th.AssertEquals(t, "ssh rule", rule.Description)
+	th.AssertEquals(t, "192.168.1.0/24", rule.DestinationIPAddress)
+	th.AssertEquals(t, "e2a5fb51-698c-4898-87e8-f1eee6b50919", rule.PolicyID)
+	th.AssertEquals(t, 2, rule.Position)
+	th.AssertEquals(t, "22", rule.DestinationPort)
+	th.AssertEquals(t, "f03bd950-6c56-4f5e-a307-45967078f507", rule.ID)
+	th.AssertEquals(t, "ssh_form_any", rule.Name)
+	th.AssertEquals(t, "80cf934d6ffb4ef5b244f1c512ad1e61", rule.TenantID)
+	th.AssertEquals(t, true, rule.Enabled)
+	th.AssertEquals(t, "allow", rule.Action)
+	th.AssertEquals(t, 4, rule.IPVersion)
+	th.AssertEquals(t, false, rule.Shared)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+	"firewall_rule":{
+		"protocol": "tcp",
+		"description": "ssh rule",
+		"destination_ip_address": "192.168.1.0/24",
+		"destination_port": "22",
+		"name": "ssh_form_any",
+		"action": "allow",
+		"enabled": false
+	}
+}
+	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+	"firewall_rule":{
+		"protocol": "tcp",
+		"description": "ssh rule",
+		"destination_ip_address": "192.168.1.0/24",
+		"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
+		"position": 2,
+		"destination_port": "22",
+		"id": "f03bd950-6c56-4f5e-a307-45967078f507",
+		"name": "ssh_form_any",
+		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
+		"enabled": false,
+		"action": "allow",
+		"ip_version": 4,
+		"shared": false
+	}
+}
+		`)
+	})
+
+	newProtocol := "tcp"
+	newDescription := "ssh rule"
+	newDestinationIP := "192.168.1.0/24"
+	newDestintionPort := "22"
+	newName := "ssh_form_any"
+	newAction := "allow"
+
+	options := rules.UpdateOpts{
+		Protocol:             &newProtocol,
+		Description:          &newDescription,
+		DestinationIPAddress: &newDestinationIP,
+		DestinationPort:      &newDestintionPort,
+		Name:                 &newName,
+		Action:               &newAction,
+		Enabled:              gophercloud.Disabled,
+	}
+
+	_, err := rules.Update(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/fw/firewall_rules/4ec89077-d057-4a2b-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := rules.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/extensions/fwaas/rules/urls.go b/openstack/networking/v2/extensions/fwaas/rules/urls.go
index 20b0879..79654be 100644
--- a/openstack/networking/v2/extensions/fwaas/rules/urls.go
+++ b/openstack/networking/v2/extensions/fwaas/rules/urls.go
@@ -1,6 +1,6 @@
 package rules
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	rootPath     = "fw"
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go
index 29f752a..ed6b263 100644
--- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go
+++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go
@@ -1,10 +1,8 @@
 package floatingips
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOpts allows the filtering and sorting of paginated collections through
@@ -39,20 +37,28 @@
 	})
 }
 
+// CreateOptsBuilder is the interface type must satisfy to be used as Create
+// options.
+type CreateOptsBuilder interface {
+	ToFloatingIPCreateMap() (map[string]interface{}, error)
+}
+
 // CreateOpts contains all the values needed to create a new floating IP
 // resource. The only required fields are FloatingNetworkID and PortID which
 // refer to the external network and internal port respectively.
 type CreateOpts struct {
-	FloatingNetworkID string
-	FloatingIP        string
-	PortID            string
-	FixedIP           string
-	TenantID          string
+	FloatingNetworkID string `json:"floating_network_id" required:"true"`
+	FloatingIP        string `json:"floating_ip_address,omitempty"`
+	PortID            string `json:"port_id,omitempty"`
+	FixedIP           string `json:"fixed_ip_address,omitempty"`
+	TenantID          string `json:"tenant_id,omitempty"`
 }
 
-var (
-	errFloatingNetworkIDRequired = fmt.Errorf("A NetworkID is required")
-)
+// ToFloatingIPCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
+// interface
+func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "floatingip")
+}
 
 // Create accepts a CreateOpts struct and uses the values provided to create a
 // new floating IP resource. You can create floating IPs on external networks
@@ -78,45 +84,26 @@
 // operation will fail and return a 400 error code. If the PortID and FixedIP
 // are already associated with another resource, the operation will fail and
 // returns a 409 error code.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	var res CreateResult
-
-	// Validate
-	if opts.FloatingNetworkID == "" {
-		res.Err = errFloatingNetworkIDRequired
-		return res
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToFloatingIPCreateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-
-	// Define structures
-	type floatingIP struct {
-		FloatingNetworkID string `json:"floating_network_id"`
-		FloatingIP        string `json:"floating_ip_address,omitempty"`
-		PortID            string `json:"port_id,omitempty"`
-		FixedIP           string `json:"fixed_ip_address,omitempty"`
-		TenantID          string `json:"tenant_id,omitempty"`
-	}
-	type request struct {
-		FloatingIP floatingIP `json:"floatingip"`
-	}
-
-	// Populate request body
-	reqBody := request{FloatingIP: floatingIP{
-		FloatingNetworkID: opts.FloatingNetworkID,
-		FloatingIP:        opts.FloatingIP,
-		PortID:            opts.PortID,
-		FixedIP:           opts.FixedIP,
-		TenantID:          opts.TenantID,
-	}}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular floating IP resource based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
+}
+
+// UpdateOptsBuilder is the interface type must satisfy to be used as Update
+// options.
+type UpdateOptsBuilder interface {
+	ToFloatingIPUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains the values used when updating a floating IP resource. The
@@ -124,45 +111,35 @@
 // linked to. To associate the floating IP with a new internal port, provide its
 // ID. To disassociate the floating IP from all ports, provide an empty string.
 type UpdateOpts struct {
-	PortID string
+	PortID string `json:"port_id"`
+}
+
+// ToFloatingIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder
+// interface
+func (opts UpdateOpts) ToFloatingIPUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "floatingip")
 }
 
 // Update allows floating IP resources to be updated. Currently, the only way to
 // "update" a floating IP is to associate it with a new internal port, or
 // disassociated it from all ports. See UpdateOpts for instructions of how to
 // do this.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type floatingIP struct {
-		PortID *string `json:"port_id"`
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToFloatingIPUpdateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-
-	type request struct {
-		FloatingIP floatingIP `json:"floatingip"`
-	}
-
-	var portID *string
-	if opts.PortID == "" {
-		portID = nil
-	} else {
-		portID = &opts.PortID
-	}
-
-	reqBody := request{FloatingIP: floatingIP{PortID: portID}}
-
-	// Send request to API
-	var res UpdateResult
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return res
+	return
 }
 
 // Delete will permanently delete a particular floating IP resource. Please
 // ensure this is what you want - you can also disassociate the IP from existing
 // internal ports.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/requests_test.go
deleted file mode 100644
index d914a79..0000000
--- a/openstack/networking/v2/extensions/layer3/floatingips/requests_test.go
+++ /dev/null
@@ -1,355 +0,0 @@
-package floatingips
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "floatingips": [
-        {
-            "floating_network_id": "6d67c30a-ddb4-49a1-bec3-a65b286b4170",
-            "router_id": null,
-            "fixed_ip_address": null,
-            "floating_ip_address": "192.0.0.4",
-            "tenant_id": "017d8de156df4177889f31a9bd6edc00",
-            "status": "DOWN",
-            "port_id": null,
-            "id": "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e"
-        },
-        {
-            "floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64",
-            "router_id": "0a24cb83-faf5-4d7f-b723-3144ed8a2167",
-            "fixed_ip_address": "192.0.0.2",
-            "floating_ip_address": "10.0.0.3",
-            "tenant_id": "017d8de156df4177889f31a9bd6edc00",
-            "status": "DOWN",
-            "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25",
-            "id": "ada25a95-f321-4f59-b0e0-f3a970dd3d63"
-        }
-    ]
-}
-			`)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractFloatingIPs(page)
-		if err != nil {
-			t.Errorf("Failed to extract floating IPs: %v", err)
-			return false, err
-		}
-
-		expected := []FloatingIP{
-			FloatingIP{
-				FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170",
-				FixedIP:           "",
-				FloatingIP:        "192.0.0.4",
-				TenantID:          "017d8de156df4177889f31a9bd6edc00",
-				Status:            "DOWN",
-				PortID:            "",
-				ID:                "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e",
-			},
-			FloatingIP{
-				FloatingNetworkID: "90f742b1-6d17-487b-ba95-71881dbc0b64",
-				FixedIP:           "192.0.0.2",
-				FloatingIP:        "10.0.0.3",
-				TenantID:          "017d8de156df4177889f31a9bd6edc00",
-				Status:            "DOWN",
-				PortID:            "74a342ce-8e07-4e91-880c-9f834b68fa25",
-				ID:                "ada25a95-f321-4f59-b0e0-f3a970dd3d63",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestInvalidNextPageURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `{"floatingips": [{}], "floatingips_links": {}}`)
-	})
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		ExtractFloatingIPs(page)
-		return true, nil
-	})
-}
-
-func TestRequiredFieldsForCreate(t *testing.T) {
-	res1 := Create(fake.ServiceClient(), CreateOpts{FloatingNetworkID: ""})
-	if res1.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-
-	res2 := Create(fake.ServiceClient(), CreateOpts{FloatingNetworkID: "foo", PortID: ""})
-	if res2.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "floatingip": {
-        "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
-        "port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab"
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "floatingip": {
-        "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
-        "tenant_id": "4969c491a3c74ee4af974e6d800c62de",
-        "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
-        "fixed_ip_address": "10.0.0.3",
-        "floating_ip_address": "",
-        "port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab",
-        "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
-    }
-}
-		`)
-	})
-
-	options := CreateOpts{
-		FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57",
-		PortID:            "ce705c24-c1ef-408a-bda3-7bbd946164ab",
-	}
-
-	ip, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID)
-	th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID)
-	th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID)
-	th.AssertEquals(t, "", ip.FloatingIP)
-	th.AssertEquals(t, "ce705c24-c1ef-408a-bda3-7bbd946164ab", ip.PortID)
-	th.AssertEquals(t, "10.0.0.3", ip.FixedIP)
-}
-
-func TestCreateEmptyPort(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-			{
-				"floatingip": {
-					"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57"
-				}
-			}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-				{
-					"floatingip": {
-						"router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
-						"tenant_id": "4969c491a3c74ee4af974e6d800c62de",
-						"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
-						"fixed_ip_address": "10.0.0.3",
-						"floating_ip_address": "",
-						"id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
-					}
-				}
-				`)
-	})
-
-	options := CreateOpts{
-		FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57",
-	}
-
-	ip, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID)
-	th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID)
-	th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID)
-	th.AssertEquals(t, "", ip.FloatingIP)
-	th.AssertEquals(t, "", ip.PortID)
-	th.AssertEquals(t, "10.0.0.3", ip.FixedIP)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "floatingip": {
-        "floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64",
-        "fixed_ip_address": "192.0.0.2",
-        "floating_ip_address": "10.0.0.3",
-        "tenant_id": "017d8de156df4177889f31a9bd6edc00",
-        "status": "DOWN",
-        "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25",
-        "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
-    }
-}
-      `)
-	})
-
-	ip, err := Get(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "90f742b1-6d17-487b-ba95-71881dbc0b64", ip.FloatingNetworkID)
-	th.AssertEquals(t, "10.0.0.3", ip.FloatingIP)
-	th.AssertEquals(t, "74a342ce-8e07-4e91-880c-9f834b68fa25", ip.PortID)
-	th.AssertEquals(t, "192.0.0.2", ip.FixedIP)
-	th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", ip.TenantID)
-	th.AssertEquals(t, "DOWN", ip.Status)
-	th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID)
-}
-
-func TestAssociate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-	"floatingip": {
-		"port_id": "423abc8d-2991-4a55-ba98-2aaea84cc72e"
-	}
-}
-		`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-	"floatingip": {
-			"router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
-			"tenant_id": "4969c491a3c74ee4af974e6d800c62de",
-			"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
-			"fixed_ip_address": null,
-			"floating_ip_address": "172.24.4.228",
-			"port_id": "423abc8d-2991-4a55-ba98-2aaea84cc72e",
-			"id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
-	}
-}
-	`)
-	})
-
-	ip, err := Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", UpdateOpts{PortID: "423abc8d-2991-4a55-ba98-2aaea84cc72e"}).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertDeepEquals(t, "423abc8d-2991-4a55-ba98-2aaea84cc72e", ip.PortID)
-}
-
-func TestDisassociate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "floatingip": {
-      "port_id": null
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "floatingip": {
-        "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
-        "tenant_id": "4969c491a3c74ee4af974e6d800c62de",
-        "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
-        "fixed_ip_address": null,
-        "floating_ip_address": "172.24.4.228",
-        "port_id": null,
-        "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
-    }
-}
-    `)
-	})
-
-	ip, err := Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", UpdateOpts{}).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertDeepEquals(t, "", ip.FixedIP)
-	th.AssertDeepEquals(t, "", ip.PortID)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go
index a1c7afe..838ca2c 100644
--- a/openstack/networking/v2/extensions/layer3/floatingips/results.go
+++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go
@@ -1,11 +1,8 @@
 package floatingips
 
 import (
-	"fmt"
-
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // FloatingIP represents a floating IP resource. A floating IP is an external
@@ -16,27 +13,27 @@
 // attribute (provided by the external network extension) is set to True.
 type FloatingIP struct {
 	// Unique identifier for the floating IP instance.
-	ID string `json:"id" mapstructure:"id"`
+	ID string `json:"id"`
 
 	// UUID of the external network where the floating IP is to be created.
-	FloatingNetworkID string `json:"floating_network_id" mapstructure:"floating_network_id"`
+	FloatingNetworkID string `json:"floating_network_id"`
 
 	// Address of the floating IP on the external network.
-	FloatingIP string `json:"floating_ip_address" mapstructure:"floating_ip_address"`
+	FloatingIP string `json:"floating_ip_address"`
 
 	// UUID of the port on an internal network that is associated with the floating IP.
-	PortID string `json:"port_id" mapstructure:"port_id"`
+	PortID string `json:"port_id"`
 
 	// The specific IP address of the internal port which should be associated
 	// with the floating IP.
-	FixedIP string `json:"fixed_ip_address" mapstructure:"fixed_ip_address"`
+	FixedIP string `json:"fixed_ip_address"`
 
 	// Owner of the floating IP. Only admin users can specify a tenant identifier
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// The condition of the API resource.
-	Status string `json:"status" mapstructure:"status"`
+	Status string `json:"status"`
 }
 
 type commonResult struct {
@@ -45,20 +42,11 @@
 
 // Extract a result and extracts a FloatingIP resource.
 func (r commonResult) Extract() (*FloatingIP, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		FloatingIP *FloatingIP `json:"floatingip"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-	if err != nil {
-		return nil, fmt.Errorf("Error decoding Neutron floating IP: %v", err)
-	}
-
-	return res.FloatingIP, nil
+	err := r.ExtractInto(&s)
+	return s.FloatingIP, err
 }
 
 // CreateResult represents the result of a create operation.
@@ -90,38 +78,30 @@
 // NextPageURL is invoked when a paginated collection of floating IPs has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p FloatingIPPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"floatingips_links"`
+func (r FloatingIPPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"floatingips_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a NetworkPage struct is empty.
-func (p FloatingIPPage) IsEmpty() (bool, error) {
-	is, err := ExtractFloatingIPs(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r FloatingIPPage) IsEmpty() (bool, error) {
+	is, err := ExtractFloatingIPs(r)
+	return len(is) == 0, err
 }
 
 // ExtractFloatingIPs accepts a Page struct, specifically a FloatingIPPage struct,
 // and extracts the elements into a slice of FloatingIP structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractFloatingIPs(page pagination.Page) ([]FloatingIP, error) {
-	var resp struct {
-		FloatingIPs []FloatingIP `mapstructure:"floatingips" json:"floatingips"`
+func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) {
+	var s struct {
+		FloatingIPs []FloatingIP `json:"floatingips"`
 	}
-
-	err := mapstructure.Decode(page.(FloatingIPPage).Body, &resp)
-
-	return resp.FloatingIPs, err
+	err := (r.(FloatingIPPage)).ExtractInto(&s)
+	return s.FloatingIPs, err
 }
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/doc.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go
new file mode 100644
index 0000000..45f27eb
--- /dev/null
+++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go
@@ -0,0 +1,356 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "floatingips": [
+        {
+            "floating_network_id": "6d67c30a-ddb4-49a1-bec3-a65b286b4170",
+            "router_id": null,
+            "fixed_ip_address": null,
+            "floating_ip_address": "192.0.0.4",
+            "tenant_id": "017d8de156df4177889f31a9bd6edc00",
+            "status": "DOWN",
+            "port_id": null,
+            "id": "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e"
+        },
+        {
+            "floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64",
+            "router_id": "0a24cb83-faf5-4d7f-b723-3144ed8a2167",
+            "fixed_ip_address": "192.0.0.2",
+            "floating_ip_address": "10.0.0.3",
+            "tenant_id": "017d8de156df4177889f31a9bd6edc00",
+            "status": "DOWN",
+            "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25",
+            "id": "ada25a95-f321-4f59-b0e0-f3a970dd3d63"
+        }
+    ]
+}
+			`)
+	})
+
+	count := 0
+
+	floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := floatingips.ExtractFloatingIPs(page)
+		if err != nil {
+			t.Errorf("Failed to extract floating IPs: %v", err)
+			return false, err
+		}
+
+		expected := []floatingips.FloatingIP{
+			{
+				FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170",
+				FixedIP:           "",
+				FloatingIP:        "192.0.0.4",
+				TenantID:          "017d8de156df4177889f31a9bd6edc00",
+				Status:            "DOWN",
+				PortID:            "",
+				ID:                "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e",
+			},
+			{
+				FloatingNetworkID: "90f742b1-6d17-487b-ba95-71881dbc0b64",
+				FixedIP:           "192.0.0.2",
+				FloatingIP:        "10.0.0.3",
+				TenantID:          "017d8de156df4177889f31a9bd6edc00",
+				Status:            "DOWN",
+				PortID:            "74a342ce-8e07-4e91-880c-9f834b68fa25",
+				ID:                "ada25a95-f321-4f59-b0e0-f3a970dd3d63",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestInvalidNextPageURLs(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `{"floatingips": [{}], "floatingips_links": {}}`)
+	})
+
+	floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		floatingips.ExtractFloatingIPs(page)
+		return true, nil
+	})
+}
+
+func TestRequiredFieldsForCreate(t *testing.T) {
+	res1 := floatingips.Create(fake.ServiceClient(), floatingips.CreateOpts{FloatingNetworkID: ""})
+	if res1.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+
+	res2 := floatingips.Create(fake.ServiceClient(), floatingips.CreateOpts{FloatingNetworkID: "foo", PortID: ""})
+	if res2.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "floatingip": {
+        "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
+        "port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab"
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "floatingip": {
+        "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
+        "tenant_id": "4969c491a3c74ee4af974e6d800c62de",
+        "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
+        "fixed_ip_address": "10.0.0.3",
+        "floating_ip_address": "",
+        "port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab",
+        "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
+    }
+}
+		`)
+	})
+
+	options := floatingips.CreateOpts{
+		FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57",
+		PortID:            "ce705c24-c1ef-408a-bda3-7bbd946164ab",
+	}
+
+	ip, err := floatingips.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID)
+	th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID)
+	th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID)
+	th.AssertEquals(t, "", ip.FloatingIP)
+	th.AssertEquals(t, "ce705c24-c1ef-408a-bda3-7bbd946164ab", ip.PortID)
+	th.AssertEquals(t, "10.0.0.3", ip.FixedIP)
+}
+
+func TestCreateEmptyPort(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+			{
+				"floatingip": {
+					"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57"
+				}
+			}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+				{
+					"floatingip": {
+						"router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
+						"tenant_id": "4969c491a3c74ee4af974e6d800c62de",
+						"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
+						"fixed_ip_address": "10.0.0.3",
+						"floating_ip_address": "",
+						"id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
+					}
+				}
+				`)
+	})
+
+	options := floatingips.CreateOpts{
+		FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57",
+	}
+
+	ip, err := floatingips.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID)
+	th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID)
+	th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID)
+	th.AssertEquals(t, "", ip.FloatingIP)
+	th.AssertEquals(t, "", ip.PortID)
+	th.AssertEquals(t, "10.0.0.3", ip.FixedIP)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "floatingip": {
+        "floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64",
+        "fixed_ip_address": "192.0.0.2",
+        "floating_ip_address": "10.0.0.3",
+        "tenant_id": "017d8de156df4177889f31a9bd6edc00",
+        "status": "DOWN",
+        "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25",
+        "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
+    }
+}
+      `)
+	})
+
+	ip, err := floatingips.Get(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "90f742b1-6d17-487b-ba95-71881dbc0b64", ip.FloatingNetworkID)
+	th.AssertEquals(t, "10.0.0.3", ip.FloatingIP)
+	th.AssertEquals(t, "74a342ce-8e07-4e91-880c-9f834b68fa25", ip.PortID)
+	th.AssertEquals(t, "192.0.0.2", ip.FixedIP)
+	th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", ip.TenantID)
+	th.AssertEquals(t, "DOWN", ip.Status)
+	th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID)
+}
+
+func TestAssociate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+	"floatingip": {
+		"port_id": "423abc8d-2991-4a55-ba98-2aaea84cc72e"
+	}
+}
+		`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+	"floatingip": {
+			"router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
+			"tenant_id": "4969c491a3c74ee4af974e6d800c62de",
+			"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
+			"fixed_ip_address": null,
+			"floating_ip_address": "172.24.4.228",
+			"port_id": "423abc8d-2991-4a55-ba98-2aaea84cc72e",
+			"id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
+	}
+}
+	`)
+	})
+
+	ip, err := floatingips.Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", floatingips.UpdateOpts{PortID: "423abc8d-2991-4a55-ba98-2aaea84cc72e"}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertDeepEquals(t, "423abc8d-2991-4a55-ba98-2aaea84cc72e", ip.PortID)
+}
+
+func TestDisassociate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "floatingip": {
+      "port_id": ""
+    }
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "floatingip": {
+        "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
+        "tenant_id": "4969c491a3c74ee4af974e6d800c62de",
+        "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
+        "fixed_ip_address": null,
+        "floating_ip_address": "172.24.4.228",
+        "port_id": null,
+        "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
+    }
+}
+    `)
+	})
+
+	ip, err := floatingips.Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", floatingips.UpdateOpts{}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertDeepEquals(t, "", ip.FixedIP)
+	th.AssertDeepEquals(t, "", ip.PortID)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := floatingips.Delete(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/urls.go b/openstack/networking/v2/extensions/layer3/floatingips/urls.go
index 355f20d..1318a18 100644
--- a/openstack/networking/v2/extensions/layer3/floatingips/urls.go
+++ b/openstack/networking/v2/extensions/layer3/floatingips/urls.go
@@ -1,6 +1,6 @@
 package floatingips
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const resourcePath = "floatingips"
 
diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go
index 5fde236..71b2f62 100644
--- a/openstack/networking/v2/extensions/layer3/routers/requests.go
+++ b/openstack/networking/v2/extensions/layer3/routers/requests.go
@@ -1,10 +1,8 @@
 package routers
 
 import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOpts allows the filtering and sorting of paginated collections through
@@ -53,38 +51,15 @@
 // CreateOpts contains all the values needed to create a new router. There are
 // no required values.
 type CreateOpts struct {
-	Name         string
-	AdminStateUp *bool
-	Distributed  *bool
-	TenantID     string
-	GatewayInfo  *GatewayInfo
+	Name         string       `json:"name,omitempty"`
+	AdminStateUp *bool        `json:"admin_state_up,omitempty"`
+	Distributed  *bool        `json:"distributed,omitempty"`
+	TenantID     string       `json:"tenant_id,omitempty"`
+	GatewayInfo  *GatewayInfo `json:"external_gateway_info,omitempty"`
 }
 
-// ToRouterCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToRouterCreateMap() (map[string]interface{}, error) {
-	r := make(map[string]interface{})
-
-	if gophercloud.MaybeString(opts.Name) != nil {
-		r["name"] = opts.Name
-	}
-
-	if opts.AdminStateUp != nil {
-		r["admin_state_up"] = opts.AdminStateUp
-	}
-
-	if opts.Distributed != nil {
-		r["distributed"] = opts.Distributed
-	}
-
-	if gophercloud.MaybeString(opts.TenantID) != nil {
-		r["tenant_id"] = opts.TenantID
-	}
-
-	if opts.GatewayInfo != nil {
-		r["external_gateway_info"] = opts.GatewayInfo
-	}
-
-	return map[string]interface{}{"router": r}, nil
+	return gophercloud.BuildRequestBody(opts, "router")
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new
@@ -95,33 +70,37 @@
 // GatewayInfo struct. The external gateway for the router must be plugged into
 // an external network (it is external if its `router:external' field is set to
 // true).
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToRouterCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToRouterCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular router based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
+}
+
+type UpdateOptsBuilder interface {
+	ToRouterUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains the values used when updating a router.
 type UpdateOpts struct {
-	Name         string
-	AdminStateUp *bool
-	Distributed  *bool
-	GatewayInfo  *GatewayInfo
-	Routes       []Route
+	Name         string       `json:"name,omitempty"`
+	AdminStateUp *bool        `json:"admin_state_up,omitempty"`
+	Distributed  *bool        `json:"distributed,omitempty"`
+	GatewayInfo  *GatewayInfo `json:"external_gateway_info,omitempty"`
+	Routes       []Route      `json:"routes"`
+}
+
+func (opts UpdateOpts) ToRouterUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "router")
 }
 
 // Update allows routers to be updated. You can update the name, administrative
@@ -129,56 +108,41 @@
 // external gateway for a router, see Create. This operation does not enable
 // the update of router interfaces. To do this, use the AddInterface and
 // RemoveInterface functions.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type router struct {
-		Name         *string      `json:"name,omitempty"`
-		AdminStateUp *bool        `json:"admin_state_up,omitempty"`
-		Distributed  *bool        `json:"distributed,omitempty"`
-		GatewayInfo  *GatewayInfo `json:"external_gateway_info,omitempty"`
-		Routes       []Route      `json:"routes"`
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToRouterUpdateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-
-	type request struct {
-		Router router `json:"router"`
-	}
-
-	reqBody := request{Router: router{
-		Name:         gophercloud.MaybeString(opts.Name),
-		AdminStateUp: opts.AdminStateUp,
-		Distributed:  opts.Distributed,
-	}}
-
-	if opts.GatewayInfo != nil {
-		reqBody.Router.GatewayInfo = opts.GatewayInfo
-	}
-
-	if opts.Routes != nil {
-		reqBody.Router.Routes = opts.Routes
-	}
-
-	// Send request to API
-	var res UpdateResult
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return res
+	return
 }
 
 // Delete will permanently delete a particular router based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
 
-var errInvalidInterfaceOpts = errors.New("When adding a router interface you must provide either a subnet ID or a port ID")
+// AddInterfaceOptsBuilder is what types must satisfy to be used as AddInterface
+// options.
+type AddInterfaceOptsBuilder interface {
+	ToRouterAddInterfaceMap() (map[string]interface{}, error)
+}
 
-// InterfaceOpts allow you to work with operations that either add or remote
+// AddInterfaceOpts allow you to work with operations that either add
 // an internal interface from a router.
-type InterfaceOpts struct {
-	SubnetID string
-	PortID   string
+type AddInterfaceOpts struct {
+	SubnetID string `json:"subnet_id,omitempty" xor:"PortID"`
+	PortID   string `json:"port_id,omitempty" xor:"SubnetID"`
+}
+
+// ToRouterAddInterfaceMap allows InterfaceOpts to satisfy the InterfaceOptsBuilder
+// interface
+func (opts AddInterfaceOpts) ToRouterAddInterfaceMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "")
 }
 
 // AddInterface attaches a subnet to an internal router interface. You must
@@ -202,27 +166,35 @@
 // identifier of a new port created by this operation. After the operation
 // completes, the device ID of the port is set to the router ID, and the
 // device owner attribute is set to `network:router_interface'.
-func AddInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOpts) InterfaceResult {
-	var res InterfaceResult
-
-	// Validate
-	if (opts.SubnetID == "" && opts.PortID == "") || (opts.SubnetID != "" && opts.PortID != "") {
-		res.Err = errInvalidInterfaceOpts
-		return res
+func AddInterface(c *gophercloud.ServiceClient, id string, opts AddInterfaceOptsBuilder) (r InterfaceResult) {
+	b, err := opts.ToRouterAddInterfaceMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-
-	type request struct {
-		SubnetID string `json:"subnet_id,omitempty"`
-		PortID   string `json:"port_id,omitempty"`
-	}
-
-	body := request{SubnetID: opts.SubnetID, PortID: opts.PortID}
-
-	_, res.Err = c.Put(addInterfaceURL(c, id), body, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(addInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
+	return
+}
 
-	return res
+// RemoveInterfaceOptsBuilder is what types must satisfy to be used as RemoveInterface
+// options.
+type RemoveInterfaceOptsBuilder interface {
+	ToRouterRemoveInterfaceMap() (map[string]interface{}, error)
+}
+
+// RemoveInterfaceOpts allow you to work with operations that either add or remote
+// an internal interface from a router.
+type RemoveInterfaceOpts struct {
+	SubnetID string `json:"subnet_id,omitempty" or:"PortID"`
+	PortID   string `json:"port_id,omitempty" or:"SubnetID"`
+}
+
+// ToRouterRemoveInterfaceMap allows RemoveInterfaceOpts to satisfy the RemoveInterfaceOptsBuilder
+// interface
+func (opts RemoveInterfaceOpts) ToRouterRemoveInterfaceMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "")
 }
 
 // RemoveInterface removes an internal router interface, which detaches a
@@ -238,19 +210,14 @@
 // visible to you, the operation will fail and a 404 Not Found error will be
 // returned. After this operation completes, the port connecting the router
 // with the subnet is removed from the subnet for the network.
-func RemoveInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOpts) InterfaceResult {
-	var res InterfaceResult
-
-	type request struct {
-		SubnetID string `json:"subnet_id,omitempty"`
-		PortID   string `json:"port_id,omitempty"`
+func RemoveInterface(c *gophercloud.ServiceClient, id string, opts RemoveInterfaceOptsBuilder) (r InterfaceResult) {
+	b, err := opts.ToRouterRemoveInterfaceMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-
-	body := request{SubnetID: opts.SubnetID, PortID: opts.PortID}
-
-	_, res.Err = c.Put(removeInterfaceURL(c, id), body, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(removeInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return res
+	return
 }
diff --git a/openstack/networking/v2/extensions/layer3/routers/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/requests_test.go
deleted file mode 100644
index dbdc6fa..0000000
--- a/openstack/networking/v2/extensions/layer3/routers/requests_test.go
+++ /dev/null
@@ -1,413 +0,0 @@
-package routers
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/routers", rootURL(fake.ServiceClient()))
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/routers", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "routers": [
-        {
-            "status": "ACTIVE",
-            "external_gateway_info": null,
-            "name": "second_routers",
-            "admin_state_up": true,
-            "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
-            "distributed": false,
-            "id": "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b"
-        },
-        {
-            "status": "ACTIVE",
-            "external_gateway_info": {
-                "network_id": "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8"
-            },
-            "name": "router1",
-            "admin_state_up": true,
-            "tenant_id": "33a40233088643acb66ff6eb0ebea679",
-            "distributed": false,
-            "id": "a9254bdb-2613-4a13-ac4c-adc581fba50d"
-        }
-    ]
-}
-			`)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractRouters(page)
-		if err != nil {
-			t.Errorf("Failed to extract routers: %v", err)
-			return false, err
-		}
-
-		expected := []Router{
-			Router{
-				Status:       "ACTIVE",
-				GatewayInfo:  GatewayInfo{NetworkID: ""},
-				AdminStateUp: true,
-				Distributed:  false,
-				Name:         "second_routers",
-				ID:           "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b",
-				TenantID:     "6b96ff0cb17a4b859e1e575d221683d3",
-			},
-			Router{
-				Status:       "ACTIVE",
-				GatewayInfo:  GatewayInfo{NetworkID: "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8"},
-				AdminStateUp: true,
-				Distributed:  false,
-				Name:         "router1",
-				ID:           "a9254bdb-2613-4a13-ac4c-adc581fba50d",
-				TenantID:     "33a40233088643acb66ff6eb0ebea679",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/routers", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-   "router":{
-      "name": "foo_router",
-      "admin_state_up": false,
-      "external_gateway_info":{
-         "network_id":"8ca37218-28ff-41cb-9b10-039601ea7e6b"
-      }
-   }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "router": {
-        "status": "ACTIVE",
-        "external_gateway_info": {
-            "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
-        },
-        "name": "foo_router",
-        "admin_state_up": false,
-        "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
-        "distributed": false,
-        "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e"
-    }
-}
-		`)
-	})
-
-	asu := false
-	gwi := GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"}
-
-	options := CreateOpts{
-		Name:         "foo_router",
-		AdminStateUp: &asu,
-		GatewayInfo:  &gwi,
-	}
-	r, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "foo_router", r.Name)
-	th.AssertEquals(t, false, r.AdminStateUp)
-	th.AssertDeepEquals(t, GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"}, r.GatewayInfo)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/routers/a07eea83-7710-4860-931b-5fe220fae533", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "router": {
-        "status": "ACTIVE",
-        "external_gateway_info": {
-            "network_id": "85d76829-6415-48ff-9c63-5c5ca8c61ac6"
-        },
-        "routes": [
-            {
-                "nexthop": "10.1.0.10",
-                "destination": "40.0.1.0/24"
-            }
-        ],
-        "name": "router1",
-        "admin_state_up": true,
-        "tenant_id": "d6554fe62e2f41efbb6e026fad5c1542",
-        "distributed": false,
-        "id": "a07eea83-7710-4860-931b-5fe220fae533"
-    }
-}
-			`)
-	})
-
-	n, err := Get(fake.ServiceClient(), "a07eea83-7710-4860-931b-5fe220fae533").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertDeepEquals(t, n.GatewayInfo, GatewayInfo{NetworkID: "85d76829-6415-48ff-9c63-5c5ca8c61ac6"})
-	th.AssertEquals(t, n.Name, "router1")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.TenantID, "d6554fe62e2f41efbb6e026fad5c1542")
-	th.AssertEquals(t, n.ID, "a07eea83-7710-4860-931b-5fe220fae533")
-	th.AssertDeepEquals(t, n.Routes, []Route{Route{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}})
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "router": {
-			"name": "new_name",
-        "external_gateway_info": {
-            "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
-		},
-        "routes": [
-            {
-                "nexthop": "10.1.0.10",
-                "destination": "40.0.1.0/24"
-            }
-        ]
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "router": {
-        "status": "ACTIVE",
-        "external_gateway_info": {
-            "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
-        },
-        "name": "new_name",
-        "admin_state_up": true,
-        "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
-        "distributed": false,
-        "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e",
-        "routes": [
-            {
-                "nexthop": "10.1.0.10",
-                "destination": "40.0.1.0/24"
-            }
-        ]
-    }
-}
-		`)
-	})
-
-	gwi := GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"}
-	r := []Route{Route{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}}
-	options := UpdateOpts{Name: "new_name", GatewayInfo: &gwi, Routes: r}
-
-	n, err := Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Name, "new_name")
-	th.AssertDeepEquals(t, n.GatewayInfo, GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"})
-	th.AssertDeepEquals(t, n.Routes, []Route{Route{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}})
-}
-
-func TestAllRoutesRemoved(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "router": {
-        "routes": []
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "router": {
-        "status": "ACTIVE",
-        "external_gateway_info": {
-            "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
-        },
-        "name": "name",
-        "admin_state_up": true,
-        "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
-        "distributed": false,
-        "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e",
-        "routes": []
-    }
-}
-		`)
-	})
-
-	r := []Route{}
-	options := UpdateOpts{Routes: r}
-
-	n, err := Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertDeepEquals(t, n.Routes, []Route{})
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestAddInterface(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/add_router_interface", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1"
-}
-	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188",
-    "tenant_id": "017d8de156df4177889f31a9bd6edc00",
-    "port_id": "3f990102-4485-4df1-97a0-2c35bdb85b31",
-    "id": "9a83fa11-8da5-436e-9afe-3d3ac5ce7770"
-}
-`)
-	})
-
-	opts := InterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"}
-	res, err := AddInterface(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID)
-	th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", res.TenantID)
-	th.AssertEquals(t, "3f990102-4485-4df1-97a0-2c35bdb85b31", res.PortID)
-	th.AssertEquals(t, "9a83fa11-8da5-436e-9afe-3d3ac5ce7770", res.ID)
-}
-
-func TestAddInterfaceRequiredOpts(t *testing.T) {
-	_, err := AddInterface(fake.ServiceClient(), "foo", InterfaceOpts{}).Extract()
-	if err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	_, err = AddInterface(fake.ServiceClient(), "foo", InterfaceOpts{SubnetID: "bar", PortID: "baz"}).Extract()
-	if err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestRemoveInterface(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/remove_router_interface", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-		"subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1"
-}
-	`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-		"subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188",
-		"tenant_id": "017d8de156df4177889f31a9bd6edc00",
-		"port_id": "3f990102-4485-4df1-97a0-2c35bdb85b31",
-		"id": "9a83fa11-8da5-436e-9afe-3d3ac5ce7770"
-}
-`)
-	})
-
-	opts := InterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"}
-	res, err := RemoveInterface(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID)
-	th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", res.TenantID)
-	th.AssertEquals(t, "3f990102-4485-4df1-97a0-2c35bdb85b31", res.PortID)
-	th.AssertEquals(t, "9a83fa11-8da5-436e-9afe-3d3ac5ce7770", res.ID)
-}
diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go
index 4534123..d849d45 100644
--- a/openstack/networking/v2/extensions/layer3/routers/results.go
+++ b/openstack/networking/v2/extensions/layer3/routers/results.go
@@ -1,20 +1,20 @@
 package routers
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // GatewayInfo represents the information of an external gateway for any
 // particular network router.
 type GatewayInfo struct {
-	NetworkID string `json:"network_id" mapstructure:"network_id"`
+	NetworkID string `json:"network_id"`
 }
 
+// Route is a possible route in a router.
 type Route struct {
-	NextHop         string `mapstructure:"nexthop" json:"nexthop"`
-	DestinationCIDR string `mapstructure:"destination" json:"destination"`
+	NextHop         string `json:"nexthop"`
+	DestinationCIDR string `json:"destination"`
 }
 
 // Router represents a Neutron router. A router is a logical entity that
@@ -27,28 +27,28 @@
 // interface is added to the subnet's network.
 type Router struct {
 	// Indicates whether or not a router is currently operational.
-	Status string `json:"status" mapstructure:"status"`
+	Status string `json:"status"`
 
 	// Information on external gateway for the router.
-	GatewayInfo GatewayInfo `json:"external_gateway_info" mapstructure:"external_gateway_info"`
+	GatewayInfo GatewayInfo `json:"external_gateway_info"`
 
 	// Administrative state of the router.
-	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Whether router is disitrubted or not..
-	Distributed bool `json:"distributed" mapstructure:"distributed"`
+	Distributed bool `json:"distributed"`
 
 	// Human readable name for the router. Does not have to be unique.
-	Name string `json:"name" mapstructure:"name"`
+	Name string `json:"name"`
 
 	// Unique identifier for the router.
-	ID string `json:"id" mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Owner of the router. Only admin users can specify a tenant identifier
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
-	Routes []Route `json:"routes" mapstructure:"routes"`
+	Routes []Route `json:"routes"`
 }
 
 // RouterPage is the page returned by a pager when traversing over a
@@ -60,40 +60,32 @@
 // NextPageURL is invoked when a paginated collection of routers has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p RouterPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"routers_links"`
+func (r RouterPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"routers_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a RouterPage struct is empty.
-func (p RouterPage) IsEmpty() (bool, error) {
-	is, err := ExtractRouters(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r RouterPage) IsEmpty() (bool, error) {
+	is, err := ExtractRouters(r)
+	return len(is) == 0, err
 }
 
 // ExtractRouters accepts a Page struct, specifically a RouterPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractRouters(page pagination.Page) ([]Router, error) {
-	var resp struct {
-		Routers []Router `mapstructure:"routers" json:"routers"`
+func ExtractRouters(r pagination.Page) ([]Router, error) {
+	var s struct {
+		Routers []Router `json:"routers"`
 	}
-
-	err := mapstructure.Decode(page.(RouterPage).Body, &resp)
-
-	return resp.Routers, err
+	err := (r.(RouterPage)).ExtractInto(&s)
+	return s.Routers, err
 }
 
 type commonResult struct {
@@ -102,17 +94,11 @@
 
 // Extract is a function that accepts a result and extracts a router.
 func (r commonResult) Extract() (*Router, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Router *Router `json:"router"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Router, err
+	err := r.ExtractInto(&s)
+	return s.Router, err
 }
 
 // CreateResult represents the result of a create operation.
@@ -140,16 +126,16 @@
 // interface.
 type InterfaceInfo struct {
 	// The ID of the subnet which this interface is associated with.
-	SubnetID string `json:"subnet_id" mapstructure:"subnet_id"`
+	SubnetID string `json:"subnet_id"`
 
 	// The ID of the port that is a part of the subnet.
-	PortID string `json:"port_id" mapstructure:"port_id"`
+	PortID string `json:"port_id"`
 
 	// The UUID of the interface.
-	ID string `json:"id" mapstructure:"id"`
+	ID string `json:"id"`
 
 	// Owner of the interface.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 }
 
 // InterfaceResult represents the result of interface operations, such as
@@ -160,12 +146,7 @@
 
 // Extract is a function that accepts a result and extracts an information struct.
 func (r InterfaceResult) Extract() (*InterfaceInfo, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res *InterfaceInfo
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res, err
+	var s InterfaceInfo
+	err := r.ExtractInto(&s)
+	return &s, err
 }
diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/doc.go b/openstack/networking/v2/extensions/layer3/routers/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/layer3/routers/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go
new file mode 100644
index 0000000..bf7f35e
--- /dev/null
+++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go
@@ -0,0 +1,407 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/routers", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "routers": [
+        {
+            "status": "ACTIVE",
+            "external_gateway_info": null,
+            "name": "second_routers",
+            "admin_state_up": true,
+            "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
+            "distributed": false,
+            "id": "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b"
+        },
+        {
+            "status": "ACTIVE",
+            "external_gateway_info": {
+                "network_id": "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8"
+            },
+            "name": "router1",
+            "admin_state_up": true,
+            "tenant_id": "33a40233088643acb66ff6eb0ebea679",
+            "distributed": false,
+            "id": "a9254bdb-2613-4a13-ac4c-adc581fba50d"
+        }
+    ]
+}
+			`)
+	})
+
+	count := 0
+
+	routers.List(fake.ServiceClient(), routers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := routers.ExtractRouters(page)
+		if err != nil {
+			t.Errorf("Failed to extract routers: %v", err)
+			return false, err
+		}
+
+		expected := []routers.Router{
+			{
+				Status:       "ACTIVE",
+				GatewayInfo:  routers.GatewayInfo{NetworkID: ""},
+				AdminStateUp: true,
+				Distributed:  false,
+				Name:         "second_routers",
+				ID:           "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b",
+				TenantID:     "6b96ff0cb17a4b859e1e575d221683d3",
+			},
+			{
+				Status:       "ACTIVE",
+				GatewayInfo:  routers.GatewayInfo{NetworkID: "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8"},
+				AdminStateUp: true,
+				Distributed:  false,
+				Name:         "router1",
+				ID:           "a9254bdb-2613-4a13-ac4c-adc581fba50d",
+				TenantID:     "33a40233088643acb66ff6eb0ebea679",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/routers", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+   "router":{
+      "name": "foo_router",
+      "admin_state_up": false,
+      "external_gateway_info":{
+         "network_id":"8ca37218-28ff-41cb-9b10-039601ea7e6b"
+      }
+   }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "router": {
+        "status": "ACTIVE",
+        "external_gateway_info": {
+            "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
+        },
+        "name": "foo_router",
+        "admin_state_up": false,
+        "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
+        "distributed": false,
+        "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e"
+    }
+}
+		`)
+	})
+
+	asu := false
+	gwi := routers.GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"}
+
+	options := routers.CreateOpts{
+		Name:         "foo_router",
+		AdminStateUp: &asu,
+		GatewayInfo:  &gwi,
+	}
+	r, err := routers.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "foo_router", r.Name)
+	th.AssertEquals(t, false, r.AdminStateUp)
+	th.AssertDeepEquals(t, routers.GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"}, r.GatewayInfo)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/routers/a07eea83-7710-4860-931b-5fe220fae533", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "router": {
+        "status": "ACTIVE",
+        "external_gateway_info": {
+            "network_id": "85d76829-6415-48ff-9c63-5c5ca8c61ac6"
+        },
+        "routes": [
+            {
+                "nexthop": "10.1.0.10",
+                "destination": "40.0.1.0/24"
+            }
+        ],
+        "name": "router1",
+        "admin_state_up": true,
+        "tenant_id": "d6554fe62e2f41efbb6e026fad5c1542",
+        "distributed": false,
+        "id": "a07eea83-7710-4860-931b-5fe220fae533"
+    }
+}
+			`)
+	})
+
+	n, err := routers.Get(fake.ServiceClient(), "a07eea83-7710-4860-931b-5fe220fae533").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Status, "ACTIVE")
+	th.AssertDeepEquals(t, n.GatewayInfo, routers.GatewayInfo{NetworkID: "85d76829-6415-48ff-9c63-5c5ca8c61ac6"})
+	th.AssertEquals(t, n.Name, "router1")
+	th.AssertEquals(t, n.AdminStateUp, true)
+	th.AssertEquals(t, n.TenantID, "d6554fe62e2f41efbb6e026fad5c1542")
+	th.AssertEquals(t, n.ID, "a07eea83-7710-4860-931b-5fe220fae533")
+	th.AssertDeepEquals(t, n.Routes, []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}})
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "router": {
+			"name": "new_name",
+        "external_gateway_info": {
+            "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
+		},
+        "routes": [
+            {
+                "nexthop": "10.1.0.10",
+                "destination": "40.0.1.0/24"
+            }
+        ]
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "router": {
+        "status": "ACTIVE",
+        "external_gateway_info": {
+            "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
+        },
+        "name": "new_name",
+        "admin_state_up": true,
+        "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
+        "distributed": false,
+        "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e",
+        "routes": [
+            {
+                "nexthop": "10.1.0.10",
+                "destination": "40.0.1.0/24"
+            }
+        ]
+    }
+}
+		`)
+	})
+
+	gwi := routers.GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"}
+	r := []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}}
+	options := routers.UpdateOpts{Name: "new_name", GatewayInfo: &gwi, Routes: r}
+
+	n, err := routers.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Name, "new_name")
+	th.AssertDeepEquals(t, n.GatewayInfo, routers.GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"})
+	th.AssertDeepEquals(t, n.Routes, []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}})
+}
+
+func TestAllRoutesRemoved(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "router": {
+        "routes": []
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "router": {
+        "status": "ACTIVE",
+        "external_gateway_info": {
+            "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
+        },
+        "name": "name",
+        "admin_state_up": true,
+        "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
+        "distributed": false,
+        "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e",
+        "routes": []
+    }
+}
+		`)
+	})
+
+	r := []routers.Route{}
+	options := routers.UpdateOpts{Routes: r}
+
+	n, err := routers.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertDeepEquals(t, n.Routes, []routers.Route{})
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := routers.Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestAddInterface(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/add_router_interface", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1"
+}
+	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188",
+    "tenant_id": "017d8de156df4177889f31a9bd6edc00",
+    "port_id": "3f990102-4485-4df1-97a0-2c35bdb85b31",
+    "id": "9a83fa11-8da5-436e-9afe-3d3ac5ce7770"
+}
+`)
+	})
+
+	opts := routers.AddInterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"}
+	res, err := routers.AddInterface(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID)
+	th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", res.TenantID)
+	th.AssertEquals(t, "3f990102-4485-4df1-97a0-2c35bdb85b31", res.PortID)
+	th.AssertEquals(t, "9a83fa11-8da5-436e-9afe-3d3ac5ce7770", res.ID)
+}
+
+func TestAddInterfaceRequiredOpts(t *testing.T) {
+	_, err := routers.AddInterface(fake.ServiceClient(), "foo", routers.AddInterfaceOpts{}).Extract()
+	if err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	_, err = routers.AddInterface(fake.ServiceClient(), "foo", routers.AddInterfaceOpts{SubnetID: "bar", PortID: "baz"}).Extract()
+	if err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestRemoveInterface(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/remove_router_interface", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+		"subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1"
+}
+	`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+		"subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188",
+		"tenant_id": "017d8de156df4177889f31a9bd6edc00",
+		"port_id": "3f990102-4485-4df1-97a0-2c35bdb85b31",
+		"id": "9a83fa11-8da5-436e-9afe-3d3ac5ce7770"
+}
+`)
+	})
+
+	opts := routers.RemoveInterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"}
+	res, err := routers.RemoveInterface(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID)
+	th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", res.TenantID)
+	th.AssertEquals(t, "3f990102-4485-4df1-97a0-2c35bdb85b31", res.PortID)
+	th.AssertEquals(t, "9a83fa11-8da5-436e-9afe-3d3ac5ce7770", res.ID)
+}
diff --git a/openstack/networking/v2/extensions/layer3/routers/urls.go b/openstack/networking/v2/extensions/layer3/routers/urls.go
index bc22c2a..f9e9da3 100644
--- a/openstack/networking/v2/extensions/layer3/routers/urls.go
+++ b/openstack/networking/v2/extensions/layer3/routers/urls.go
@@ -1,6 +1,6 @@
 package routers
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const resourcePath = "routers"
 
diff --git a/openstack/networking/v2/extensions/lbaas/members/requests.go b/openstack/networking/v2/extensions/lbaas/members/requests.go
index 848938f..f74eb82 100644
--- a/openstack/networking/v2/extensions/lbaas/members/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/members/requests.go
@@ -1,8 +1,8 @@
 package members
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOpts allows the filtering and sorting of paginated collections through
@@ -42,82 +42,74 @@
 	})
 }
 
+type CreateOptsBuilder interface {
+	ToLBMemberCreateMap() (map[string]interface{}, error)
+}
+
 // CreateOpts contains all the values needed to create a new pool member.
 type CreateOpts struct {
+	// The IP address of the member.
+	Address string `json:"address" required:"true"`
+	// The port on which the application is hosted.
+	ProtocolPort int `json:"protocol_port" required:"true"`
+	// The pool to which this member will belong.
+	PoolID string `json:"pool_id" required:"true"`
 	// Only required if the caller has an admin role and wants to create a pool
 	// for another tenant.
-	TenantID string
+	TenantID string `json:"tenant_id,omitempty" required:"true"`
+}
 
-	// Required. The IP address of the member.
-	Address string
-
-	// Required. The port on which the application is hosted.
-	ProtocolPort int
-
-	// Required. The pool to which this member will belong.
-	PoolID string
+func (opts CreateOpts) ToLBMemberCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "member")
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new
 // load balancer pool member.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	type member struct {
-		TenantID     string `json:"tenant_id,omitempty"`
-		ProtocolPort int    `json:"protocol_port"`
-		Address      string `json:"address"`
-		PoolID       string `json:"pool_id"`
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToLBMemberCreateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-	type request struct {
-		Member member `json:"member"`
-	}
-
-	reqBody := request{Member: member{
-		Address:      opts.Address,
-		TenantID:     opts.TenantID,
-		ProtocolPort: opts.ProtocolPort,
-		PoolID:       opts.PoolID,
-	}}
-
-	var res CreateResult
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular pool member based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
+}
+
+type UpdateOptsBuilder interface {
+	ToLBMemberUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains the values used when updating a pool member.
 type UpdateOpts struct {
 	// The administrative state of the member, which is up (true) or down (false).
-	AdminStateUp bool
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
+}
+
+func (opts UpdateOpts) ToLBMemberUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "member")
 }
 
 // Update allows members to be updated.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type member struct {
-		AdminStateUp bool `json:"admin_state_up"`
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToLBMemberUpdateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-	type request struct {
-		Member member `json:"member"`
-	}
-
-	reqBody := request{Member: member{AdminStateUp: opts.AdminStateUp}}
-
-	// Send request to API
-	var res UpdateResult
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201, 202},
 	})
-	return res
+	return
 }
 
 // Delete will permanently delete a particular member based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/lbaas/members/requests_test.go b/openstack/networking/v2/extensions/lbaas/members/requests_test.go
deleted file mode 100644
index dc1ece3..0000000
--- a/openstack/networking/v2/extensions/lbaas/members/requests_test.go
+++ /dev/null
@@ -1,243 +0,0 @@
-package members
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lb/members", rootURL(fake.ServiceClient()))
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/members", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "members":[
-      {
-         "status":"ACTIVE",
-         "weight":1,
-         "admin_state_up":true,
-         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-         "pool_id":"72741b06-df4d-4715-b142-276b6bce75ab",
-         "address":"10.0.0.4",
-         "protocol_port":80,
-         "id":"701b531b-111a-4f21-ad85-4795b7b12af6"
-      },
-      {
-         "status":"ACTIVE",
-         "weight":1,
-         "admin_state_up":true,
-         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-         "pool_id":"72741b06-df4d-4715-b142-276b6bce75ab",
-         "address":"10.0.0.3",
-         "protocol_port":80,
-         "id":"beb53b4d-230b-4abd-8118-575b8fa006ef"
-      }
-   ]
-}
-      `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractMembers(page)
-		if err != nil {
-			t.Errorf("Failed to extract members: %v", err)
-			return false, err
-		}
-
-		expected := []Member{
-			Member{
-				Status:       "ACTIVE",
-				Weight:       1,
-				AdminStateUp: true,
-				TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
-				PoolID:       "72741b06-df4d-4715-b142-276b6bce75ab",
-				Address:      "10.0.0.4",
-				ProtocolPort: 80,
-				ID:           "701b531b-111a-4f21-ad85-4795b7b12af6",
-			},
-			Member{
-				Status:       "ACTIVE",
-				Weight:       1,
-				AdminStateUp: true,
-				TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
-				PoolID:       "72741b06-df4d-4715-b142-276b6bce75ab",
-				Address:      "10.0.0.3",
-				ProtocolPort: 80,
-				ID:           "beb53b4d-230b-4abd-8118-575b8fa006ef",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/members", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-  "member": {
-    "tenant_id": "453105b9-1754-413f-aab1-55f1af620750",
-		"pool_id": "foo",
-    "address": "192.0.2.14",
-    "protocol_port":8080
-  }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-  "member": {
-    "id": "975592ca-e308-48ad-8298-731935ee9f45",
-    "address": "192.0.2.14",
-    "protocol_port": 8080,
-    "tenant_id": "453105b9-1754-413f-aab1-55f1af620750",
-    "admin_state_up":true,
-    "weight": 1,
-    "status": "DOWN"
-  }
-}
-    `)
-	})
-
-	options := CreateOpts{
-		TenantID:     "453105b9-1754-413f-aab1-55f1af620750",
-		Address:      "192.0.2.14",
-		ProtocolPort: 8080,
-		PoolID:       "foo",
-	}
-	_, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/members/975592ca-e308-48ad-8298-731935ee9f45", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "member":{
-      "id":"975592ca-e308-48ad-8298-731935ee9f45",
-      "address":"192.0.2.14",
-      "protocol_port":8080,
-      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
-      "admin_state_up":true,
-      "weight":1,
-      "status":"DOWN"
-   }
-}
-      `)
-	})
-
-	m, err := Get(fake.ServiceClient(), "975592ca-e308-48ad-8298-731935ee9f45").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "975592ca-e308-48ad-8298-731935ee9f45", m.ID)
-	th.AssertEquals(t, "192.0.2.14", m.Address)
-	th.AssertEquals(t, 8080, m.ProtocolPort)
-	th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", m.TenantID)
-	th.AssertEquals(t, true, m.AdminStateUp)
-	th.AssertEquals(t, 1, m.Weight)
-	th.AssertEquals(t, "DOWN", m.Status)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/members/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-   "member":{
-      "admin_state_up":false
-   }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "member":{
-      "status":"PENDING_UPDATE",
-      "protocol_port":8080,
-      "weight":1,
-      "admin_state_up":false,
-      "tenant_id":"4fd44f30292945e481c7b8a0c8908869",
-      "pool_id":"7803631d-f181-4500-b3a2-1b68ba2a75fd",
-      "address":"10.0.0.5",
-      "status_description":null,
-      "id":"48a471ea-64f1-4eb6-9be7-dae6bbe40a0f"
-   }
-}
-    `)
-	})
-
-	options := UpdateOpts{AdminStateUp: false}
-
-	_, err := Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/members/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/extensions/lbaas/members/results.go b/openstack/networking/v2/extensions/lbaas/members/results.go
index 3cad339..933e1ae 100644
--- a/openstack/networking/v2/extensions/lbaas/members/results.go
+++ b/openstack/networking/v2/extensions/lbaas/members/results.go
@@ -1,9 +1,8 @@
 package members
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Member represents the application running on a backend server.
@@ -15,20 +14,20 @@
 	Weight int
 
 	// The administrative state of the member, which is up (true) or down (false).
-	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Owner of the member. Only an administrative user can specify a tenant ID
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// The pool to which the member belongs.
-	PoolID string `json:"pool_id" mapstructure:"pool_id"`
+	PoolID string `json:"pool_id"`
 
 	// The IP address of the member.
 	Address string
 
 	// The port on which the application is hosted.
-	ProtocolPort int `json:"protocol_port" mapstructure:"protocol_port"`
+	ProtocolPort int `json:"protocol_port"`
 
 	// The unique ID for the member.
 	ID string
@@ -43,43 +42,32 @@
 // NextPageURL is invoked when a paginated collection of members has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p MemberPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"members_links"`
+func (r MemberPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"members_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a MemberPage struct is empty.
-func (p MemberPage) IsEmpty() (bool, error) {
-	is, err := ExtractMembers(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r MemberPage) IsEmpty() (bool, error) {
+	is, err := ExtractMembers(r)
+	return len(is) == 0, err
 }
 
 // ExtractMembers accepts a Page struct, specifically a MemberPage struct,
 // and extracts the elements into a slice of Member structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractMembers(page pagination.Page) ([]Member, error) {
-	var resp struct {
-		Members []Member `mapstructure:"members" json:"members"`
+func ExtractMembers(r pagination.Page) ([]Member, error) {
+	var s struct {
+		Members []Member `json:"members"`
 	}
-
-	err := mapstructure.Decode(page.(MemberPage).Body, &resp)
-	if err != nil {
-		return nil, err
-	}
-
-	return resp.Members, nil
+	err := (r.(MemberPage)).ExtractInto(&s)
+	return s.Members, err
 }
 
 type commonResult struct {
@@ -88,17 +76,11 @@
 
 // Extract is a function that accepts a result and extracts a router.
 func (r commonResult) Extract() (*Member, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Member *Member `json:"member"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Member, err
+	err := r.ExtractInto(&s)
+	return s.Member, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/lbaas/members/testing/doc.go b/openstack/networking/v2/extensions/lbaas/members/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas/members/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go
new file mode 100644
index 0000000..3e4f1d4
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go
@@ -0,0 +1,238 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/members", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+   "members":[
+      {
+         "status":"ACTIVE",
+         "weight":1,
+         "admin_state_up":true,
+         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+         "pool_id":"72741b06-df4d-4715-b142-276b6bce75ab",
+         "address":"10.0.0.4",
+         "protocol_port":80,
+         "id":"701b531b-111a-4f21-ad85-4795b7b12af6"
+      },
+      {
+         "status":"ACTIVE",
+         "weight":1,
+         "admin_state_up":true,
+         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+         "pool_id":"72741b06-df4d-4715-b142-276b6bce75ab",
+         "address":"10.0.0.3",
+         "protocol_port":80,
+         "id":"beb53b4d-230b-4abd-8118-575b8fa006ef"
+      }
+   ]
+}
+      `)
+	})
+
+	count := 0
+
+	members.List(fake.ServiceClient(), members.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := members.ExtractMembers(page)
+		if err != nil {
+			t.Errorf("Failed to extract members: %v", err)
+			return false, err
+		}
+
+		expected := []members.Member{
+			{
+				Status:       "ACTIVE",
+				Weight:       1,
+				AdminStateUp: true,
+				TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
+				PoolID:       "72741b06-df4d-4715-b142-276b6bce75ab",
+				Address:      "10.0.0.4",
+				ProtocolPort: 80,
+				ID:           "701b531b-111a-4f21-ad85-4795b7b12af6",
+			},
+			{
+				Status:       "ACTIVE",
+				Weight:       1,
+				AdminStateUp: true,
+				TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
+				PoolID:       "72741b06-df4d-4715-b142-276b6bce75ab",
+				Address:      "10.0.0.3",
+				ProtocolPort: 80,
+				ID:           "beb53b4d-230b-4abd-8118-575b8fa006ef",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/members", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+  "member": {
+    "tenant_id": "453105b9-1754-413f-aab1-55f1af620750",
+		"pool_id": "foo",
+    "address": "192.0.2.14",
+    "protocol_port":8080
+  }
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+  "member": {
+    "id": "975592ca-e308-48ad-8298-731935ee9f45",
+    "address": "192.0.2.14",
+    "protocol_port": 8080,
+    "tenant_id": "453105b9-1754-413f-aab1-55f1af620750",
+    "admin_state_up":true,
+    "weight": 1,
+    "status": "DOWN"
+  }
+}
+    `)
+	})
+
+	options := members.CreateOpts{
+		TenantID:     "453105b9-1754-413f-aab1-55f1af620750",
+		Address:      "192.0.2.14",
+		ProtocolPort: 8080,
+		PoolID:       "foo",
+	}
+	_, err := members.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/members/975592ca-e308-48ad-8298-731935ee9f45", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+   "member":{
+      "id":"975592ca-e308-48ad-8298-731935ee9f45",
+      "address":"192.0.2.14",
+      "protocol_port":8080,
+      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
+      "admin_state_up":true,
+      "weight":1,
+      "status":"DOWN"
+   }
+}
+      `)
+	})
+
+	m, err := members.Get(fake.ServiceClient(), "975592ca-e308-48ad-8298-731935ee9f45").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "975592ca-e308-48ad-8298-731935ee9f45", m.ID)
+	th.AssertEquals(t, "192.0.2.14", m.Address)
+	th.AssertEquals(t, 8080, m.ProtocolPort)
+	th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", m.TenantID)
+	th.AssertEquals(t, true, m.AdminStateUp)
+	th.AssertEquals(t, 1, m.Weight)
+	th.AssertEquals(t, "DOWN", m.Status)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/members/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+   "member":{
+      "admin_state_up":false
+   }
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+   "member":{
+      "status":"PENDING_UPDATE",
+      "protocol_port":8080,
+      "weight":1,
+      "admin_state_up":false,
+      "tenant_id":"4fd44f30292945e481c7b8a0c8908869",
+      "pool_id":"7803631d-f181-4500-b3a2-1b68ba2a75fd",
+      "address":"10.0.0.5",
+      "status_description":null,
+      "id":"48a471ea-64f1-4eb6-9be7-dae6bbe40a0f"
+   }
+}
+    `)
+	})
+
+	options := members.UpdateOpts{AdminStateUp: gophercloud.Disabled}
+
+	_, err := members.Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/members/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := members.Delete(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/extensions/lbaas/members/urls.go b/openstack/networking/v2/extensions/lbaas/members/urls.go
index 94b57e4..e2248f8 100644
--- a/openstack/networking/v2/extensions/lbaas/members/urls.go
+++ b/openstack/networking/v2/extensions/lbaas/members/urls.go
@@ -1,6 +1,6 @@
 package members
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	rootPath     = "lb"
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests.go b/openstack/networking/v2/extensions/lbaas/monitors/requests.go
index 71b21ef..f1b964b 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/requests.go
@@ -3,8 +3,8 @@
 import (
 	"fmt"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOpts allows the filtering and sorting of paginated collections through
@@ -42,64 +42,78 @@
 		return pagination.Pager{Err: err}
 	}
 	u := rootURL(c) + q.String()
-
 	return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
 		return MonitorPage{pagination.LinkedPageBase{PageResult: r}}
 	})
 }
 
+// MonitorType is the type for all the types of LB monitors
+type MonitorType string
+
 // Constants that represent approved monitoring types.
 const (
-	TypePING  = "PING"
-	TypeTCP   = "TCP"
-	TypeHTTP  = "HTTP"
-	TypeHTTPS = "HTTPS"
+	TypePING  MonitorType = "PING"
+	TypeTCP   MonitorType = "TCP"
+	TypeHTTP  MonitorType = "HTTP"
+	TypeHTTPS MonitorType = "HTTPS"
 )
 
-var (
-	errValidTypeRequired     = fmt.Errorf("A valid Type is required. Supported values are PING, TCP, HTTP and HTTPS")
-	errDelayRequired         = fmt.Errorf("Delay is required")
-	errTimeoutRequired       = fmt.Errorf("Timeout is required")
-	errMaxRetriesRequired    = fmt.Errorf("MaxRetries is required")
-	errURLPathRequired       = fmt.Errorf("URL path is required")
-	errExpectedCodesRequired = fmt.Errorf("ExpectedCodes is required")
-	errDelayMustGETimeout    = fmt.Errorf("Delay must be greater than or equal to timeout")
-)
+// CreateOptsBuilder is what types must satisfy to be used as Create
+// options.
+type CreateOptsBuilder interface {
+	ToLBMonitorCreateMap() (map[string]interface{}, error)
+}
 
 // CreateOpts contains all the values needed to create a new health monitor.
 type CreateOpts struct {
-	// Required for admins. Indicates the owner of the VIP.
-	TenantID string
-
 	// Required. The type of probe, which is PING, TCP, HTTP, or HTTPS, that is
 	// sent by the load balancer to verify the member state.
-	Type string
-
+	Type MonitorType `json:"type" required:"true"`
 	// Required. The time, in seconds, between sending probes to members.
-	Delay int
-
+	Delay int `json:"delay" required:"true"`
 	// Required. Maximum number of seconds for a monitor to wait for a ping reply
 	// before it times out. The value must be less than the delay value.
-	Timeout int
-
+	Timeout int `json:"timeout" required:"true"`
 	// Required. Number of permissible ping failures before changing the member's
 	// status to INACTIVE. Must be a number between 1 and 10.
-	MaxRetries int
-
+	MaxRetries int `json:"max_retries" required:"true"`
 	// Required for HTTP(S) types. URI path that will be accessed if monitor type
 	// is HTTP or HTTPS.
-	URLPath string
-
+	URLPath string `json:"url_path,omitempty"`
 	// Required for HTTP(S) types. The HTTP method used for requests by the
 	// monitor. If this attribute is not specified, it defaults to "GET".
-	HTTPMethod string
-
+	HTTPMethod string `json:"http_method,omitempty"`
 	// Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S)
 	// monitor. You can either specify a single status like "200", or a range
 	// like "200-202".
-	ExpectedCodes string
+	ExpectedCodes string `json:"expected_codes,omitempty"`
+	// Required for admins. Indicates the owner of the VIP.
+	TenantID     string `json:"tenant_id,omitempty"`
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
+}
 
-	AdminStateUp *bool
+// ToLBMonitorCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
+// interface
+func (opts CreateOpts) ToLBMonitorCreateMap() (map[string]interface{}, error) {
+	if opts.Type == TypeHTTP || opts.Type == TypeHTTPS {
+		if opts.URLPath == "" {
+			err := gophercloud.ErrMissingInput{}
+			err.Argument = "monitors.CreateOpts.URLPath"
+			return nil, err
+		}
+		if opts.ExpectedCodes == "" {
+			err := gophercloud.ErrMissingInput{}
+			err.Argument = "monitors.CreateOpts.ExpectedCodes"
+			return nil, err
+		}
+	}
+	if opts.Delay < opts.Timeout {
+		err := gophercloud.ErrInvalidInput{}
+		err.Argument = "monitors.CreateOpts.Delay/monitors.CreateOpts.Timeout"
+		err.Info = "Delay must be greater than or equal to timeout"
+		return nil, err
+	}
+	return gophercloud.BuildRequestBody(opts, "health_monitor")
 }
 
 // Create is an operation which provisions a new health monitor. There are
@@ -116,150 +130,81 @@
 // CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3,
 //  HttpMethod: "HEAD", ExpectedCodes: "200"}
 //
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	var res CreateResult
-
-	// Validate inputs
-	allowed := map[string]bool{TypeHTTP: true, TypeHTTPS: true, TypeTCP: true, TypePING: true}
-	if opts.Type == "" || allowed[opts.Type] == false {
-		res.Err = errValidTypeRequired
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToLBMonitorCreateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-	if opts.Delay == 0 {
-		res.Err = errDelayRequired
-	}
-	if opts.Timeout == 0 {
-		res.Err = errTimeoutRequired
-	}
-	if opts.MaxRetries == 0 {
-		res.Err = errMaxRetriesRequired
-	}
-	if opts.Type == TypeHTTP || opts.Type == TypeHTTPS {
-		if opts.URLPath == "" {
-			res.Err = errURLPathRequired
-		}
-		if opts.ExpectedCodes == "" {
-			res.Err = errExpectedCodesRequired
-		}
-	}
-	if opts.Delay < opts.Timeout {
-		res.Err = errDelayMustGETimeout
-	}
-	if res.Err != nil {
-		return res
-	}
-
-	type monitor struct {
-		Type          string  `json:"type"`
-		Delay         int     `json:"delay"`
-		Timeout       int     `json:"timeout"`
-		MaxRetries    int     `json:"max_retries"`
-		TenantID      *string `json:"tenant_id,omitempty"`
-		URLPath       *string `json:"url_path,omitempty"`
-		ExpectedCodes *string `json:"expected_codes,omitempty"`
-		HTTPMethod    *string `json:"http_method,omitempty"`
-		AdminStateUp  *bool   `json:"admin_state_up,omitempty"`
-	}
-
-	type request struct {
-		Monitor monitor `json:"health_monitor"`
-	}
-
-	reqBody := request{Monitor: monitor{
-		Type:          opts.Type,
-		Delay:         opts.Delay,
-		Timeout:       opts.Timeout,
-		MaxRetries:    opts.MaxRetries,
-		TenantID:      gophercloud.MaybeString(opts.TenantID),
-		URLPath:       gophercloud.MaybeString(opts.URLPath),
-		ExpectedCodes: gophercloud.MaybeString(opts.ExpectedCodes),
-		HTTPMethod:    gophercloud.MaybeString(opts.HTTPMethod),
-		AdminStateUp:  opts.AdminStateUp,
-	}}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular health monitor based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
+}
+
+// UpdateOptsBuilder is what types must satisfy to be used as Update
+// options.
+type UpdateOptsBuilder interface {
+	ToLBMonitorUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains all the values needed to update an existing virtual IP.
 // Attributes not listed here but appear in CreateOpts are immutable and cannot
 // be updated.
 type UpdateOpts struct {
-	// Required. The time, in seconds, between sending probes to members.
-	Delay int
-
-	// Required. Maximum number of seconds for a monitor to wait for a ping reply
+	// The time, in seconds, between sending probes to members.
+	Delay int `json:"delay,omitempty"`
+	// Maximum number of seconds for a monitor to wait for a ping reply
 	// before it times out. The value must be less than the delay value.
-	Timeout int
-
-	// Required. Number of permissible ping failures before changing the member's
+	Timeout int `json:"timeout,omitempty"`
+	// Number of permissible ping failures before changing the member's
 	// status to INACTIVE. Must be a number between 1 and 10.
-	MaxRetries int
-
-	// Required for HTTP(S) types. URI path that will be accessed if monitor type
+	MaxRetries int `json:"max_retries,omitempty"`
+	// URI path that will be accessed if monitor type
 	// is HTTP or HTTPS.
-	URLPath string
-
-	// Required for HTTP(S) types. The HTTP method used for requests by the
+	URLPath string `json:"url_path,omitempty"`
+	// The HTTP method used for requests by the
 	// monitor. If this attribute is not specified, it defaults to "GET".
-	HTTPMethod string
-
-	// Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S)
+	HTTPMethod string `json:"http_method,omitempty"`
+	// Expected HTTP codes for a passing HTTP(S)
 	// monitor. You can either specify a single status like "200", or a range
 	// like "200-202".
-	ExpectedCodes string
+	ExpectedCodes string `json:"expected_codes,omitempty"`
+	AdminStateUp  *bool  `json:"admin_state_up,omitempty"`
+}
 
-	AdminStateUp *bool
+// ToLBMonitorUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder
+// interface
+func (opts UpdateOpts) ToLBMonitorUpdateMap() (map[string]interface{}, error) {
+	if opts.Delay > 0 && opts.Timeout > 0 && opts.Delay < opts.Timeout {
+		err := gophercloud.ErrInvalidInput{}
+		err.Argument = "monitors.CreateOpts.Delay/monitors.CreateOpts.Timeout"
+		err.Value = fmt.Sprintf("%d/%d", opts.Delay, opts.Timeout)
+		err.Info = "Delay must be greater than or equal to timeout"
+		return nil, err
+	}
+	return gophercloud.BuildRequestBody(opts, "health_monitor")
 }
 
 // Update is an operation which modifies the attributes of the specified monitor.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	var res UpdateResult
-
-	if opts.Delay > 0 && opts.Timeout > 0 && opts.Delay < opts.Timeout {
-		res.Err = errDelayMustGETimeout
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToLBMonitorUpdateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-
-	type monitor struct {
-		Delay         int     `json:"delay"`
-		Timeout       int     `json:"timeout"`
-		MaxRetries    int     `json:"max_retries"`
-		URLPath       *string `json:"url_path,omitempty"`
-		ExpectedCodes *string `json:"expected_codes,omitempty"`
-		HTTPMethod    *string `json:"http_method,omitempty"`
-		AdminStateUp  *bool   `json:"admin_state_up,omitempty"`
-	}
-
-	type request struct {
-		Monitor monitor `json:"health_monitor"`
-	}
-
-	reqBody := request{Monitor: monitor{
-		Delay:         opts.Delay,
-		Timeout:       opts.Timeout,
-		MaxRetries:    opts.MaxRetries,
-		URLPath:       gophercloud.MaybeString(opts.URLPath),
-		ExpectedCodes: gophercloud.MaybeString(opts.ExpectedCodes),
-		HTTPMethod:    gophercloud.MaybeString(opts.HTTPMethod),
-		AdminStateUp:  opts.AdminStateUp,
-	}}
-
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
-
-	return res
+	return
 }
 
 // Delete will permanently delete a particular monitor based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go b/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go
deleted file mode 100644
index 79a99bf..0000000
--- a/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go
+++ /dev/null
@@ -1,312 +0,0 @@
-package monitors
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lb/health_monitors", rootURL(fake.ServiceClient()))
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/health_monitors", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "health_monitors":[
-      {
-         "admin_state_up":true,
-         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-         "delay":10,
-         "max_retries":1,
-         "timeout":1,
-         "type":"PING",
-         "id":"466c8345-28d8-4f84-a246-e04380b0461d"
-      },
-      {
-         "admin_state_up":true,
-         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-         "delay":5,
-         "expected_codes":"200",
-         "max_retries":2,
-         "http_method":"GET",
-         "timeout":2,
-         "url_path":"/",
-         "type":"HTTP",
-         "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
-      }
-   ]
-}
-			`)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractMonitors(page)
-		if err != nil {
-			t.Errorf("Failed to extract monitors: %v", err)
-			return false, err
-		}
-
-		expected := []Monitor{
-			Monitor{
-				AdminStateUp: true,
-				TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
-				Delay:        10,
-				MaxRetries:   1,
-				Timeout:      1,
-				Type:         "PING",
-				ID:           "466c8345-28d8-4f84-a246-e04380b0461d",
-			},
-			Monitor{
-				AdminStateUp:  true,
-				TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
-				Delay:         5,
-				ExpectedCodes: "200",
-				MaxRetries:    2,
-				Timeout:       2,
-				URLPath:       "/",
-				Type:          "HTTP",
-				HTTPMethod:    "GET",
-				ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) {
-	_, err := Create(fake.ServiceClient(), CreateOpts{
-		Type:          "HTTP",
-		Delay:         1,
-		Timeout:       10,
-		MaxRetries:    5,
-		URLPath:       "/check",
-		ExpectedCodes: "200-299",
-	}).Extract()
-
-	if err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-
-	_, err = Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", UpdateOpts{
-		Delay:   1,
-		Timeout: 10,
-	}).Extract()
-
-	if err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/health_monitors", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-   "health_monitor":{
-      "type":"HTTP",
-      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
-      "delay":20,
-      "timeout":10,
-      "max_retries":5,
-      "url_path":"/check",
-      "expected_codes":"200-299"
-   }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-   "health_monitor":{
-      "id":"f3eeab00-8367-4524-b662-55e64d4cacb5",
-      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
-      "type":"HTTP",
-      "delay":20,
-      "timeout":10,
-      "max_retries":5,
-      "http_method":"GET",
-      "url_path":"/check",
-      "expected_codes":"200-299",
-      "admin_state_up":true,
-      "status":"ACTIVE"
-   }
-}
-		`)
-	})
-
-	_, err := Create(fake.ServiceClient(), CreateOpts{
-		Type:          "HTTP",
-		TenantID:      "453105b9-1754-413f-aab1-55f1af620750",
-		Delay:         20,
-		Timeout:       10,
-		MaxRetries:    5,
-		URLPath:       "/check",
-		ExpectedCodes: "200-299",
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), CreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Type: TypeHTTP})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/health_monitors/f3eeab00-8367-4524-b662-55e64d4cacb5", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "health_monitor":{
-      "id":"f3eeab00-8367-4524-b662-55e64d4cacb5",
-      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
-      "type":"HTTP",
-      "delay":20,
-      "timeout":10,
-      "max_retries":5,
-      "http_method":"GET",
-      "url_path":"/check",
-      "expected_codes":"200-299",
-      "admin_state_up":true,
-      "status":"ACTIVE"
-   }
-}
-			`)
-	})
-
-	hm, err := Get(fake.ServiceClient(), "f3eeab00-8367-4524-b662-55e64d4cacb5").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "f3eeab00-8367-4524-b662-55e64d4cacb5", hm.ID)
-	th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", hm.TenantID)
-	th.AssertEquals(t, "HTTP", hm.Type)
-	th.AssertEquals(t, 20, hm.Delay)
-	th.AssertEquals(t, 10, hm.Timeout)
-	th.AssertEquals(t, 5, hm.MaxRetries)
-	th.AssertEquals(t, "GET", hm.HTTPMethod)
-	th.AssertEquals(t, "/check", hm.URLPath)
-	th.AssertEquals(t, "200-299", hm.ExpectedCodes)
-	th.AssertEquals(t, true, hm.AdminStateUp)
-	th.AssertEquals(t, "ACTIVE", hm.Status)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-   "health_monitor":{
-      "delay": 3,
-      "timeout": 20,
-      "max_retries": 10,
-      "url_path": "/another_check",
-      "expected_codes": "301"
-   }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-
-		fmt.Fprintf(w, `
-{
-    "health_monitor": {
-        "admin_state_up": true,
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "delay": 3,
-        "max_retries": 10,
-        "http_method": "GET",
-        "timeout": 20,
-        "pools": [
-            {
-                "status": "PENDING_CREATE",
-                "status_description": null,
-                "pool_id": "6e55751f-6ad4-4e53-b8d4-02e442cd21df"
-            }
-        ],
-        "type": "PING",
-        "id": "b05e44b5-81f9-4551-b474-711a722698f7"
-    }
-}
-		`)
-	})
-
-	_, err := Update(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7", UpdateOpts{
-		Delay:         3,
-		Timeout:       20,
-		MaxRetries:    10,
-		URLPath:       "/another_check",
-		ExpectedCodes: "301",
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/results.go b/openstack/networking/v2/extensions/lbaas/monitors/results.go
index ac2801b..0385942 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/results.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/results.go
@@ -1,9 +1,8 @@
 package monitors
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Monitor represents a load balancer health monitor. A health monitor is used
@@ -30,7 +29,7 @@
 
 	// Owner of the VIP. Only an administrative user can specify a tenant ID
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// The type of probe sent by the load balancer to verify the member state,
 	// which is PING, TCP, HTTP, or HTTPS.
@@ -45,20 +44,20 @@
 
 	// Number of allowed connection failures before changing the status of the
 	// member to INACTIVE. A valid value is from 1 to 10.
-	MaxRetries int `json:"max_retries" mapstructure:"max_retries"`
+	MaxRetries int `json:"max_retries"`
 
 	// The HTTP method that the monitor uses for requests.
-	HTTPMethod string `json:"http_method" mapstructure:"http_method"`
+	HTTPMethod string `json:"http_method"`
 
 	// The HTTP path of the request sent by the monitor to test the health of a
 	// member. Must be a string beginning with a forward slash (/).
-	URLPath string `json:"url_path" mapstructure:"url_path"`
+	URLPath string `json:"url_path"`
 
 	// Expected HTTP codes for a passing HTTP(S) monitor.
-	ExpectedCodes string `json:"expected_codes" mapstructure:"expected_codes"`
+	ExpectedCodes string `json:"expected_codes"`
 
 	// The administrative state of the health monitor, which is up (true) or down (false).
-	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// The status of the health monitor. Indicates whether the health monitor is
 	// operational.
@@ -74,40 +73,33 @@
 // NextPageURL is invoked when a paginated collection of monitors has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p MonitorPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"health_monitors_links"`
+func (r MonitorPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"health_monitors_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
 
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a PoolPage struct is empty.
-func (p MonitorPage) IsEmpty() (bool, error) {
-	is, err := ExtractMonitors(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r MonitorPage) IsEmpty() (bool, error) {
+	is, err := ExtractMonitors(r)
+	return len(is) == 0, err
 }
 
 // ExtractMonitors accepts a Page struct, specifically a MonitorPage struct,
 // and extracts the elements into a slice of Monitor structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractMonitors(page pagination.Page) ([]Monitor, error) {
-	var resp struct {
-		Monitors []Monitor `mapstructure:"health_monitors" json:"health_monitors"`
+func ExtractMonitors(r pagination.Page) ([]Monitor, error) {
+	var s struct {
+		Monitors []Monitor `json:"health_monitors"`
 	}
-
-	err := mapstructure.Decode(page.(MonitorPage).Body, &resp)
-
-	return resp.Monitors, err
+	err := (r.(MonitorPage)).ExtractInto(&s)
+	return s.Monitors, err
 }
 
 type commonResult struct {
@@ -116,17 +108,11 @@
 
 // Extract is a function that accepts a result and extracts a monitor.
 func (r commonResult) Extract() (*Monitor, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Monitor *Monitor `json:"health_monitor"`
 	}
-
-	var res struct {
-		Monitor *Monitor `json:"health_monitor" mapstructure:"health_monitor"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Monitor, err
+	err := r.ExtractInto(&s)
+	return s.Monitor, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/testing/doc.go b/openstack/networking/v2/extensions/lbaas/monitors/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas/monitors/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go
new file mode 100644
index 0000000..f736074
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go
@@ -0,0 +1,310 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/health_monitors", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+   "health_monitors":[
+      {
+         "admin_state_up":true,
+         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+         "delay":10,
+         "max_retries":1,
+         "timeout":1,
+         "type":"PING",
+         "id":"466c8345-28d8-4f84-a246-e04380b0461d"
+      },
+      {
+         "admin_state_up":true,
+         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+         "delay":5,
+         "expected_codes":"200",
+         "max_retries":2,
+         "http_method":"GET",
+         "timeout":2,
+         "url_path":"/",
+         "type":"HTTP",
+         "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
+      }
+   ]
+}
+			`)
+	})
+
+	count := 0
+
+	monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := monitors.ExtractMonitors(page)
+		if err != nil {
+			t.Errorf("Failed to extract monitors: %v", err)
+			return false, err
+		}
+
+		expected := []monitors.Monitor{
+			{
+				AdminStateUp: true,
+				TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
+				Delay:        10,
+				MaxRetries:   1,
+				Timeout:      1,
+				Type:         "PING",
+				ID:           "466c8345-28d8-4f84-a246-e04380b0461d",
+			},
+			{
+				AdminStateUp:  true,
+				TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+				Delay:         5,
+				ExpectedCodes: "200",
+				MaxRetries:    2,
+				Timeout:       2,
+				URLPath:       "/",
+				Type:          "HTTP",
+				HTTPMethod:    "GET",
+				ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) {
+	_, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{
+		Type:          "HTTP",
+		Delay:         1,
+		Timeout:       10,
+		MaxRetries:    5,
+		URLPath:       "/check",
+		ExpectedCodes: "200-299",
+	}).Extract()
+
+	if err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+
+	_, err = monitors.Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{
+		Delay:   1,
+		Timeout: 10,
+	}).Extract()
+
+	if err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/health_monitors", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+   "health_monitor":{
+      "type":"HTTP",
+      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
+      "delay":20,
+      "timeout":10,
+      "max_retries":5,
+      "url_path":"/check",
+      "expected_codes":"200-299"
+   }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+   "health_monitor":{
+      "id":"f3eeab00-8367-4524-b662-55e64d4cacb5",
+      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
+      "type":"HTTP",
+      "delay":20,
+      "timeout":10,
+      "max_retries":5,
+      "http_method":"GET",
+      "url_path":"/check",
+      "expected_codes":"200-299",
+      "admin_state_up":true,
+      "status":"ACTIVE"
+   }
+}
+		`)
+	})
+
+	_, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{
+		Type:          "HTTP",
+		TenantID:      "453105b9-1754-413f-aab1-55f1af620750",
+		Delay:         20,
+		Timeout:       10,
+		MaxRetries:    5,
+		URLPath:       "/check",
+		ExpectedCodes: "200-299",
+	}).Extract()
+
+	th.AssertNoErr(t, err)
+}
+
+func TestRequiredCreateOpts(t *testing.T) {
+	res := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = monitors.Create(fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/health_monitors/f3eeab00-8367-4524-b662-55e64d4cacb5", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+   "health_monitor":{
+      "id":"f3eeab00-8367-4524-b662-55e64d4cacb5",
+      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
+      "type":"HTTP",
+      "delay":20,
+      "timeout":10,
+      "max_retries":5,
+      "http_method":"GET",
+      "url_path":"/check",
+      "expected_codes":"200-299",
+      "admin_state_up":true,
+      "status":"ACTIVE"
+   }
+}
+			`)
+	})
+
+	hm, err := monitors.Get(fake.ServiceClient(), "f3eeab00-8367-4524-b662-55e64d4cacb5").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "f3eeab00-8367-4524-b662-55e64d4cacb5", hm.ID)
+	th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", hm.TenantID)
+	th.AssertEquals(t, "HTTP", hm.Type)
+	th.AssertEquals(t, 20, hm.Delay)
+	th.AssertEquals(t, 10, hm.Timeout)
+	th.AssertEquals(t, 5, hm.MaxRetries)
+	th.AssertEquals(t, "GET", hm.HTTPMethod)
+	th.AssertEquals(t, "/check", hm.URLPath)
+	th.AssertEquals(t, "200-299", hm.ExpectedCodes)
+	th.AssertEquals(t, true, hm.AdminStateUp)
+	th.AssertEquals(t, "ACTIVE", hm.Status)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+   "health_monitor":{
+      "delay": 30,
+      "timeout": 20,
+      "max_retries": 10,
+      "url_path": "/another_check",
+      "expected_codes": "301",
+			"admin_state_up": true
+   }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusAccepted)
+
+		fmt.Fprintf(w, `
+{
+    "health_monitor": {
+        "admin_state_up": true,
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "delay": 30,
+        "max_retries": 10,
+        "http_method": "GET",
+        "timeout": 20,
+        "pools": [
+            {
+                "status": "PENDING_CREATE",
+                "status_description": null,
+                "pool_id": "6e55751f-6ad4-4e53-b8d4-02e442cd21df"
+            }
+        ],
+        "type": "PING",
+        "id": "b05e44b5-81f9-4551-b474-711a722698f7"
+    }
+}
+		`)
+	})
+
+	_, err := monitors.Update(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7", monitors.UpdateOpts{
+		Delay:         30,
+		Timeout:       20,
+		MaxRetries:    10,
+		URLPath:       "/another_check",
+		ExpectedCodes: "301",
+		AdminStateUp:  gophercloud.Enabled,
+	}).Extract()
+
+	th.AssertNoErr(t, err)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := monitors.Delete(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/urls.go b/openstack/networking/v2/extensions/lbaas/monitors/urls.go
index 46e84bb..e9d90fc 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/urls.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/urls.go
@@ -1,6 +1,6 @@
 package monitors
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	rootPath     = "lb"
diff --git a/openstack/networking/v2/extensions/lbaas/pools/requests.go b/openstack/networking/v2/extensions/lbaas/pools/requests.go
index e0002ac..2a75737 100644
--- a/openstack/networking/v2/extensions/lbaas/pools/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/pools/requests.go
@@ -1,8 +1,8 @@
 package pools
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOpts allows the filtering and sorting of paginated collections through
@@ -43,117 +43,111 @@
 	})
 }
 
+// LBMethod is a type used for possible load balancing methods
+type LBMethod string
+
+// LBProtocol is a type used for possible load balancing protocols
+type LBProtocol string
+
 // Supported attributes for create/update operations.
 const (
-	LBMethodRoundRobin       = "ROUND_ROBIN"
-	LBMethodLeastConnections = "LEAST_CONNECTIONS"
+	LBMethodRoundRobin       LBMethod = "ROUND_ROBIN"
+	LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS"
 
-	ProtocolTCP   = "TCP"
-	ProtocolHTTP  = "HTTP"
-	ProtocolHTTPS = "HTTPS"
+	ProtocolTCP   LBProtocol = "TCP"
+	ProtocolHTTP  LBProtocol = "HTTP"
+	ProtocolHTTPS LBProtocol = "HTTPS"
 )
 
+// CreateOptsBuilder is the interface types must satisfy to be used as options
+// for the Create function
+type CreateOptsBuilder interface {
+	ToLBPoolCreateMap() (map[string]interface{}, error)
+}
+
 // CreateOpts contains all the values needed to create a new pool.
 type CreateOpts struct {
+	// Name of the pool.
+	Name string `json:"name" required:"true"`
+	// The protocol used by the pool members, you can use either
+	// ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS.
+	Protocol LBProtocol `json:"protocol" required:"true"`
 	// Only required if the caller has an admin role and wants to create a pool
 	// for another tenant.
-	TenantID string
-
-	// Required. Name of the pool.
-	Name string
-
-	// Required. The protocol used by the pool members, you can use either
-	// ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS.
-	Protocol string
-
+	TenantID string `json:"tenant_id,omitempty"`
 	// The network on which the members of the pool will be located. Only members
 	// that are on this network can be added to the pool.
-	SubnetID string
-
+	SubnetID string `json:"subnet_id,omitempty"`
 	// The algorithm used to distribute load between the members of the pool. The
 	// current specification supports LBMethodRoundRobin and
 	// LBMethodLeastConnections as valid values for this attribute.
-	LBMethod string
+	LBMethod LBMethod `json:"lb_method" required:"true"`
 
 	// The provider of the pool
-	Provider string
+	Provider string `json:"provider,omitempty"`
 }
 
-// Create accepts a CreateOpts struct and uses the values to create a new
+// ToLBPoolCreateMap allows CreateOpts to satisfy the CreateOptsBuilder interface
+func (opts CreateOpts) ToLBPoolCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "pool")
+}
+
+// Create accepts a CreateOptsBuilder and uses the values to create a new
 // load balancer pool.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	type pool struct {
-		Name     string `json:"name"`
-		TenantID string `json:"tenant_id,omitempty"`
-		Protocol string `json:"protocol"`
-		SubnetID string `json:"subnet_id"`
-		LBMethod string `json:"lb_method"`
-		Provider string `json:"provider,omitempty"`
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToLBPoolCreateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-	type request struct {
-		Pool pool `json:"pool"`
-	}
-
-	reqBody := request{Pool: pool{
-		Name:     opts.Name,
-		TenantID: opts.TenantID,
-		Protocol: opts.Protocol,
-		SubnetID: opts.SubnetID,
-		LBMethod: opts.LBMethod,
-		Provider: opts.Provider,
-	}}
-
-	var res CreateResult
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular pool based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
+}
+
+// UpdateOptsBuilder is the interface types must satisfy to be used as options
+// for the Update function
+type UpdateOptsBuilder interface {
+	ToLBPoolUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains the values used when updating a pool.
 type UpdateOpts struct {
-	// Required. Name of the pool.
-	Name string
-
+	// Name of the pool.
+	Name string `json:"name,omitempty"`
 	// The algorithm used to distribute load between the members of the pool. The
 	// current specification supports LBMethodRoundRobin and
 	// LBMethodLeastConnections as valid values for this attribute.
-	LBMethod string
+	LBMethod LBMethod `json:"lb_method,omitempty"`
+}
+
+// ToLBPoolUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder interface
+func (opts UpdateOpts) ToLBPoolUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "pool")
 }
 
 // Update allows pools to be updated.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type pool struct {
-		Name     string `json:"name,"`
-		LBMethod string `json:"lb_method"`
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToLBPoolUpdateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-	type request struct {
-		Pool pool `json:"pool"`
-	}
-
-	reqBody := request{Pool: pool{
-		Name:     opts.Name,
-		LBMethod: opts.LBMethod,
-	}}
-
-	// Send request to API
-	var res UpdateResult
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // Delete will permanently delete a particular pool based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
 
 // AssociateMonitor will associate a health monitor with a particular pool.
@@ -161,26 +155,16 @@
 // pool and will deactivate these members if they are deemed unhealthy. A
 // member can be deactivated (status set to INACTIVE) if any of health monitors
 // finds it unhealthy.
-func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) AssociateResult {
-	type hm struct {
-		ID string `json:"id"`
-	}
-	type request struct {
-		Monitor hm `json:"health_monitor"`
-	}
-
-	reqBody := request{hm{ID: monitorID}}
-
-	var res AssociateResult
-	_, res.Err = c.Post(associateURL(c, poolID), reqBody, &res.Body, nil)
-	return res
+func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) {
+	b := map[string]interface{}{"health_monitor": map[string]string{"id": monitorID}}
+	_, r.Err = c.Post(associateURL(c, poolID), b, &r.Body, nil)
+	return
 }
 
 // DisassociateMonitor will disassociate a health monitor with a particular
 // pool. When dissociation is successful, the health monitor will no longer
 // check for the health of the members of the pool.
-func DisassociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) AssociateResult {
-	var res AssociateResult
-	_, res.Err = c.Delete(disassociateURL(c, poolID, monitorID), nil)
-	return res
+func DisassociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) {
+	_, r.Err = c.Delete(disassociateURL(c, poolID, monitorID), nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/lbaas/pools/requests_test.go b/openstack/networking/v2/extensions/lbaas/pools/requests_test.go
deleted file mode 100644
index 9696cb8..0000000
--- a/openstack/networking/v2/extensions/lbaas/pools/requests_test.go
+++ /dev/null
@@ -1,322 +0,0 @@
-package pools
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lb/pools", rootURL(fake.ServiceClient()))
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/pools", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "pools":[
-      {
-         "status":"ACTIVE",
-         "lb_method":"ROUND_ROBIN",
-         "protocol":"HTTP",
-         "description":"",
-         "health_monitors":[
-            "466c8345-28d8-4f84-a246-e04380b0461d",
-            "5d4b5228-33b0-4e60-b225-9b727c1a20e7"
-         ],
-         "members":[
-            "701b531b-111a-4f21-ad85-4795b7b12af6",
-            "beb53b4d-230b-4abd-8118-575b8fa006ef"
-         ],
-         "status_description": null,
-         "id":"72741b06-df4d-4715-b142-276b6bce75ab",
-         "vip_id":"4ec89087-d057-4e2c-911f-60a3b47ee304",
-         "name":"app_pool",
-         "admin_state_up":true,
-         "subnet_id":"8032909d-47a1-4715-90af-5153ffe39861",
-         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-         "health_monitors_status": [],
-         "provider": "haproxy"
-      }
-   ]
-}
-			`)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractPools(page)
-		if err != nil {
-			t.Errorf("Failed to extract pools: %v", err)
-			return false, err
-		}
-
-		expected := []Pool{
-			Pool{
-				Status:      "ACTIVE",
-				LBMethod:    "ROUND_ROBIN",
-				Protocol:    "HTTP",
-				Description: "",
-				MonitorIDs: []string{
-					"466c8345-28d8-4f84-a246-e04380b0461d",
-					"5d4b5228-33b0-4e60-b225-9b727c1a20e7",
-				},
-				SubnetID:     "8032909d-47a1-4715-90af-5153ffe39861",
-				TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
-				AdminStateUp: true,
-				Name:         "app_pool",
-				MemberIDs: []string{
-					"701b531b-111a-4f21-ad85-4795b7b12af6",
-					"beb53b4d-230b-4abd-8118-575b8fa006ef",
-				},
-				ID:       "72741b06-df4d-4715-b142-276b6bce75ab",
-				VIPID:    "4ec89087-d057-4e2c-911f-60a3b47ee304",
-				Provider: "haproxy",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/pools", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "pool": {
-        "lb_method": "ROUND_ROBIN",
-        "protocol": "HTTP",
-        "name": "Example pool",
-        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-        "provider": "haproxy"
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "pool": {
-        "status": "PENDING_CREATE",
-        "lb_method": "ROUND_ROBIN",
-        "protocol": "HTTP",
-        "description": "",
-        "health_monitors": [],
-        "members": [],
-        "status_description": null,
-        "id": "69055154-f603-4a28-8951-7cc2d9e54a9a",
-        "vip_id": null,
-        "name": "Example pool",
-        "admin_state_up": true,
-        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-        "health_monitors_status": [],
-        "provider": "haproxy"
-    }
-}
-		`)
-	})
-
-	options := CreateOpts{
-		LBMethod: LBMethodRoundRobin,
-		Protocol: "HTTP",
-		Name:     "Example pool",
-		SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9",
-		TenantID: "2ffc6e22aae24e4795f87155d24c896f",
-		Provider: "haproxy",
-	}
-	p, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "PENDING_CREATE", p.Status)
-	th.AssertEquals(t, "ROUND_ROBIN", p.LBMethod)
-	th.AssertEquals(t, "HTTP", p.Protocol)
-	th.AssertEquals(t, "", p.Description)
-	th.AssertDeepEquals(t, []string{}, p.MonitorIDs)
-	th.AssertDeepEquals(t, []string{}, p.MemberIDs)
-	th.AssertEquals(t, "69055154-f603-4a28-8951-7cc2d9e54a9a", p.ID)
-	th.AssertEquals(t, "Example pool", p.Name)
-	th.AssertEquals(t, "1981f108-3c48-48d2-b908-30f7d28532c9", p.SubnetID)
-	th.AssertEquals(t, "2ffc6e22aae24e4795f87155d24c896f", p.TenantID)
-	th.AssertEquals(t, "haproxy", p.Provider)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "pool":{
-      "id":"332abe93-f488-41ba-870b-2ac66be7f853",
-      "tenant_id":"19eaa775-cf5d-49bc-902e-2f85f668d995",
-      "name":"Example pool",
-      "description":"",
-      "protocol":"tcp",
-      "lb_algorithm":"ROUND_ROBIN",
-      "session_persistence":{
-      },
-      "healthmonitor_id":null,
-      "members":[
-      ],
-      "admin_state_up":true,
-      "status":"ACTIVE"
-   }
-}
-			`)
-	})
-
-	n, err := Get(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.ID, "332abe93-f488-41ba-870b-2ac66be7f853")
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-   "pool":{
-      "name":"SuperPool",
-      "lb_method": "LEAST_CONNECTIONS"
-   }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "pool":{
-      "status":"PENDING_UPDATE",
-      "lb_method":"LEAST_CONNECTIONS",
-      "protocol":"TCP",
-      "description":"",
-      "health_monitors":[
-
-      ],
-      "subnet_id":"8032909d-47a1-4715-90af-5153ffe39861",
-      "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-      "admin_state_up":true,
-      "name":"SuperPool",
-      "members":[
-
-      ],
-      "id":"61b1f87a-7a21-4ad3-9dda-7f81d249944f",
-      "vip_id":null
-   }
-}
-		`)
-	})
-
-	options := UpdateOpts{Name: "SuperPool", LBMethod: LBMethodLeastConnections}
-
-	n, err := Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "SuperPool", n.Name)
-	th.AssertDeepEquals(t, "LEAST_CONNECTIONS", n.LBMethod)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestAssociateHealthMonitor(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853/health_monitors", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-   "health_monitor":{
-      "id":"b624decf-d5d3-4c66-9a3d-f047e7786181"
-   }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, `{}`)
-	})
-
-	_, err := AssociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181").Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestDisassociateHealthMonitor(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853/health_monitors/b624decf-d5d3-4c66-9a3d-f047e7786181", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := DisassociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/extensions/lbaas/pools/results.go b/openstack/networking/v2/extensions/lbaas/pools/results.go
index 07ec85e..2ca1963 100644
--- a/openstack/networking/v2/extensions/lbaas/pools/results.go
+++ b/openstack/networking/v2/extensions/lbaas/pools/results.go
@@ -1,9 +1,8 @@
 package pools
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Pool represents a logical set of devices, such as web servers, that you
@@ -18,7 +17,7 @@
 	// The load-balancer algorithm, which is round-robin, least-connections, and
 	// so on. This value, which must be supported, is dependent on the provider.
 	// Round-robin must be supported.
-	LBMethod string `json:"lb_method" mapstructure:"lb_method"`
+	LBMethod string `json:"lb_method"`
 
 	// The protocol of the pool, which is TCP, HTTP, or HTTPS.
 	Protocol string
@@ -27,30 +26,30 @@
 	Description string
 
 	// The IDs of associated monitors which check the health of the pool members.
-	MonitorIDs []string `json:"health_monitors" mapstructure:"health_monitors"`
+	MonitorIDs []string `json:"health_monitors"`
 
 	// The network on which the members of the pool will be located. Only members
 	// that are on this network can be added to the pool.
-	SubnetID string `json:"subnet_id" mapstructure:"subnet_id"`
+	SubnetID string `json:"subnet_id"`
 
 	// Owner of the pool. Only an administrative user can specify a tenant ID
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// The administrative state of the pool, which is up (true) or down (false).
-	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Pool name. Does not have to be unique.
 	Name string
 
 	// List of member IDs that belong to the pool.
-	MemberIDs []string `json:"members" mapstructure:"members"`
+	MemberIDs []string `json:"members"`
 
 	// The unique ID for the pool.
 	ID string
 
 	// The ID of the virtual IP associated with this pool
-	VIPID string `json:"vip_id" mapstructure:"vip_id"`
+	VIPID string `json:"vip_id"`
 
 	// The provider
 	Provider string
@@ -65,40 +64,32 @@
 // NextPageURL is invoked when a paginated collection of pools has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p PoolPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"pools_links"`
+func (r PoolPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"pools_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a PoolPage struct is empty.
-func (p PoolPage) IsEmpty() (bool, error) {
-	is, err := ExtractPools(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r PoolPage) IsEmpty() (bool, error) {
+	is, err := ExtractPools(r)
+	return len(is) == 0, err
 }
 
 // ExtractPools accepts a Page struct, specifically a RouterPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractPools(page pagination.Page) ([]Pool, error) {
-	var resp struct {
-		Pools []Pool `mapstructure:"pools" json:"pools"`
+func ExtractPools(r pagination.Page) ([]Pool, error) {
+	var s struct {
+		Pools []Pool `json:"pools"`
 	}
-
-	err := mapstructure.Decode(page.(PoolPage).Body, &resp)
-
-	return resp.Pools, err
+	err := (r.(PoolPage)).ExtractInto(&s)
+	return s.Pools, err
 }
 
 type commonResult struct {
@@ -107,17 +98,11 @@
 
 // Extract is a function that accepts a result and extracts a router.
 func (r commonResult) Extract() (*Pool, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Pool *Pool `json:"pool"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Pool, err
+	err := r.ExtractInto(&s)
+	return s.Pool, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/lbaas/pools/testing/doc.go b/openstack/networking/v2/extensions/lbaas/pools/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas/pools/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go
new file mode 100644
index 0000000..de038cb
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go
@@ -0,0 +1,316 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/pools", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+   "pools":[
+      {
+         "status":"ACTIVE",
+         "lb_method":"ROUND_ROBIN",
+         "protocol":"HTTP",
+         "description":"",
+         "health_monitors":[
+            "466c8345-28d8-4f84-a246-e04380b0461d",
+            "5d4b5228-33b0-4e60-b225-9b727c1a20e7"
+         ],
+         "members":[
+            "701b531b-111a-4f21-ad85-4795b7b12af6",
+            "beb53b4d-230b-4abd-8118-575b8fa006ef"
+         ],
+         "status_description": null,
+         "id":"72741b06-df4d-4715-b142-276b6bce75ab",
+         "vip_id":"4ec89087-d057-4e2c-911f-60a3b47ee304",
+         "name":"app_pool",
+         "admin_state_up":true,
+         "subnet_id":"8032909d-47a1-4715-90af-5153ffe39861",
+         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+         "health_monitors_status": [],
+         "provider": "haproxy"
+      }
+   ]
+}
+			`)
+	})
+
+	count := 0
+
+	pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := pools.ExtractPools(page)
+		if err != nil {
+			t.Errorf("Failed to extract pools: %v", err)
+			return false, err
+		}
+
+		expected := []pools.Pool{
+			{
+				Status:      "ACTIVE",
+				LBMethod:    "ROUND_ROBIN",
+				Protocol:    "HTTP",
+				Description: "",
+				MonitorIDs: []string{
+					"466c8345-28d8-4f84-a246-e04380b0461d",
+					"5d4b5228-33b0-4e60-b225-9b727c1a20e7",
+				},
+				SubnetID:     "8032909d-47a1-4715-90af-5153ffe39861",
+				TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
+				AdminStateUp: true,
+				Name:         "app_pool",
+				MemberIDs: []string{
+					"701b531b-111a-4f21-ad85-4795b7b12af6",
+					"beb53b4d-230b-4abd-8118-575b8fa006ef",
+				},
+				ID:       "72741b06-df4d-4715-b142-276b6bce75ab",
+				VIPID:    "4ec89087-d057-4e2c-911f-60a3b47ee304",
+				Provider: "haproxy",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/pools", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "pool": {
+        "lb_method": "ROUND_ROBIN",
+        "protocol": "HTTP",
+        "name": "Example pool",
+        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+        "provider": "haproxy"
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "pool": {
+        "status": "PENDING_CREATE",
+        "lb_method": "ROUND_ROBIN",
+        "protocol": "HTTP",
+        "description": "",
+        "health_monitors": [],
+        "members": [],
+        "status_description": null,
+        "id": "69055154-f603-4a28-8951-7cc2d9e54a9a",
+        "vip_id": null,
+        "name": "Example pool",
+        "admin_state_up": true,
+        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+        "health_monitors_status": [],
+        "provider": "haproxy"
+    }
+}
+		`)
+	})
+
+	options := pools.CreateOpts{
+		LBMethod: pools.LBMethodRoundRobin,
+		Protocol: "HTTP",
+		Name:     "Example pool",
+		SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9",
+		TenantID: "2ffc6e22aae24e4795f87155d24c896f",
+		Provider: "haproxy",
+	}
+	p, err := pools.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "PENDING_CREATE", p.Status)
+	th.AssertEquals(t, "ROUND_ROBIN", p.LBMethod)
+	th.AssertEquals(t, "HTTP", p.Protocol)
+	th.AssertEquals(t, "", p.Description)
+	th.AssertDeepEquals(t, []string{}, p.MonitorIDs)
+	th.AssertDeepEquals(t, []string{}, p.MemberIDs)
+	th.AssertEquals(t, "69055154-f603-4a28-8951-7cc2d9e54a9a", p.ID)
+	th.AssertEquals(t, "Example pool", p.Name)
+	th.AssertEquals(t, "1981f108-3c48-48d2-b908-30f7d28532c9", p.SubnetID)
+	th.AssertEquals(t, "2ffc6e22aae24e4795f87155d24c896f", p.TenantID)
+	th.AssertEquals(t, "haproxy", p.Provider)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+   "pool":{
+      "id":"332abe93-f488-41ba-870b-2ac66be7f853",
+      "tenant_id":"19eaa775-cf5d-49bc-902e-2f85f668d995",
+      "name":"Example pool",
+      "description":"",
+      "protocol":"tcp",
+      "lb_algorithm":"ROUND_ROBIN",
+      "session_persistence":{
+      },
+      "healthmonitor_id":null,
+      "members":[
+      ],
+      "admin_state_up":true,
+      "status":"ACTIVE"
+   }
+}
+			`)
+	})
+
+	n, err := pools.Get(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.ID, "332abe93-f488-41ba-870b-2ac66be7f853")
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+   "pool":{
+      "name":"SuperPool",
+      "lb_method": "LEAST_CONNECTIONS"
+   }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+   "pool":{
+      "status":"PENDING_UPDATE",
+      "lb_method":"LEAST_CONNECTIONS",
+      "protocol":"TCP",
+      "description":"",
+      "health_monitors":[
+
+      ],
+      "subnet_id":"8032909d-47a1-4715-90af-5153ffe39861",
+      "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+      "admin_state_up":true,
+      "name":"SuperPool",
+      "members":[
+
+      ],
+      "id":"61b1f87a-7a21-4ad3-9dda-7f81d249944f",
+      "vip_id":null
+   }
+}
+		`)
+	})
+
+	options := pools.UpdateOpts{Name: "SuperPool", LBMethod: pools.LBMethodLeastConnections}
+
+	n, err := pools.Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "SuperPool", n.Name)
+	th.AssertDeepEquals(t, "LEAST_CONNECTIONS", n.LBMethod)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := pools.Delete(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestAssociateHealthMonitor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853/health_monitors", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+   "health_monitor":{
+      "id":"b624decf-d5d3-4c66-9a3d-f047e7786181"
+   }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+		fmt.Fprintf(w, `{}`)
+	})
+
+	_, err := pools.AssociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181").Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestDisassociateHealthMonitor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853/health_monitors/b624decf-d5d3-4c66-9a3d-f047e7786181", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := pools.DisassociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/extensions/lbaas/pools/urls.go b/openstack/networking/v2/extensions/lbaas/pools/urls.go
index 6cd15b0..fe3601b 100644
--- a/openstack/networking/v2/extensions/lbaas/pools/urls.go
+++ b/openstack/networking/v2/extensions/lbaas/pools/urls.go
@@ -1,6 +1,6 @@
 package pools
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	rootPath     = "lb"
diff --git a/openstack/networking/v2/extensions/lbaas/vips/requests.go b/openstack/networking/v2/extensions/lbaas/vips/requests.go
index 6216f87..f89d769 100644
--- a/openstack/networking/v2/extensions/lbaas/vips/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/vips/requests.go
@@ -1,23 +1,8 @@
 package vips
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOpts allows the filtering and sorting of paginated collections through
@@ -60,51 +45,45 @@
 	})
 }
 
-var (
-	errNameRequired         = fmt.Errorf("Name is required")
-	errSubnetIDRequried     = fmt.Errorf("SubnetID is required")
-	errProtocolRequired     = fmt.Errorf("Protocol is required")
-	errProtocolPortRequired = fmt.Errorf("Protocol port is required")
-	errPoolIDRequired       = fmt.Errorf("PoolID is required")
-)
+// CreateOptsBuilder is what types must satisfy to be used as Create
+// options.
+type CreateOptsBuilder interface {
+	ToVIPCreateMap() (map[string]interface{}, error)
+}
 
 // CreateOpts contains all the values needed to create a new virtual IP.
 type CreateOpts struct {
-	// Required. Human-readable name for the VIP. Does not have to be unique.
-	Name string
-
-	// Required. The network on which to allocate the VIP's address. A tenant can
+	// Human-readable name for the VIP. Does not have to be unique.
+	Name string `json:"name" required:"true"`
+	// The network on which to allocate the VIP's address. A tenant can
 	// only create VIPs on networks authorized by policy (e.g. networks that
 	// belong to them or networks that are shared).
-	SubnetID string
-
-	// Required. The protocol - can either be TCP, HTTP or HTTPS.
-	Protocol string
-
-	// Required. The port on which to listen for client traffic.
-	ProtocolPort int
-
-	// Required. The ID of the pool with which the VIP is associated.
-	PoolID string
-
+	SubnetID string `json:"subnet_id" required:"true"`
+	// The protocol - can either be TCP, HTTP or HTTPS.
+	Protocol string `json:"protocol" required:"true"`
+	// The port on which to listen for client traffic.
+	ProtocolPort int `json:"protocol_port" required:"true"`
+	// The ID of the pool with which the VIP is associated.
+	PoolID string `json:"pool_id" required:"true"`
 	// Required for admins. Indicates the owner of the VIP.
-	TenantID string
-
-	// Optional. The IP address of the VIP.
-	Address string
-
-	// Optional. Human-readable description for the VIP.
-	Description string
-
-	// Optional. Omit this field to prevent session persistence.
-	Persistence *SessionPersistence
-
-	// Optional. The maximum number of connections allowed for the VIP.
-	ConnLimit *int
-
-	// Optional. The administrative state of the VIP. A valid value is true (UP)
+	TenantID string `json:"tenant_id,omitempty"`
+	// The IP address of the VIP.
+	Address string `json:"address,omitempty"`
+	// Human-readable description for the VIP.
+	Description string `json:"description,omitempty"`
+	// Omit this field to prevent session persistence.
+	Persistence *SessionPersistence `json:"session_persistence,omitempty"`
+	// The maximum number of connections allowed for the VIP.
+	ConnLimit *int `json:"connection_limit,omitempty"`
+	// The administrative state of the VIP. A valid value is true (UP)
 	// or false (DOWN).
-	AdminStateUp *bool
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
+}
+
+// ToVIPCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
+// interface
+func (opts CreateOpts) ToVIPCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "vip")
 }
 
 // Create is an operation which provisions a new virtual IP based on the
@@ -118,75 +97,26 @@
 //
 // Users with an admin role can create VIPs on behalf of other tenants by
 // specifying a TenantID attribute different than their own.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	var res CreateResult
-
-	// Validate required opts
-	if opts.Name == "" {
-		res.Err = errNameRequired
-		return res
+func Create(c *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) {
+	b, err := opts.ToVIPCreateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-	if opts.SubnetID == "" {
-		res.Err = errSubnetIDRequried
-		return res
-	}
-	if opts.Protocol == "" {
-		res.Err = errProtocolRequired
-		return res
-	}
-	if opts.ProtocolPort == 0 {
-		res.Err = errProtocolPortRequired
-		return res
-	}
-	if opts.PoolID == "" {
-		res.Err = errPoolIDRequired
-		return res
-	}
-
-	type vip struct {
-		Name         string              `json:"name"`
-		SubnetID     string              `json:"subnet_id"`
-		Protocol     string              `json:"protocol"`
-		ProtocolPort int                 `json:"protocol_port"`
-		PoolID       string              `json:"pool_id"`
-		Description  *string             `json:"description,omitempty"`
-		TenantID     *string             `json:"tenant_id,omitempty"`
-		Address      *string             `json:"address,omitempty"`
-		Persistence  *SessionPersistence `json:"session_persistence,omitempty"`
-		ConnLimit    *int                `json:"connection_limit,omitempty"`
-		AdminStateUp *bool               `json:"admin_state_up,omitempty"`
-	}
-
-	type request struct {
-		VirtualIP vip `json:"vip"`
-	}
-
-	reqBody := request{VirtualIP: vip{
-		Name:         opts.Name,
-		SubnetID:     opts.SubnetID,
-		Protocol:     opts.Protocol,
-		ProtocolPort: opts.ProtocolPort,
-		PoolID:       opts.PoolID,
-		Description:  gophercloud.MaybeString(opts.Description),
-		TenantID:     gophercloud.MaybeString(opts.TenantID),
-		Address:      gophercloud.MaybeString(opts.Address),
-		ConnLimit:    opts.ConnLimit,
-		AdminStateUp: opts.AdminStateUp,
-	}}
-
-	if opts.Persistence != nil {
-		reqBody.VirtualIP.Persistence = opts.Persistence
-	}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular virtual IP based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
+}
+
+// UpdateOptsBuilder is what types must satisfy to be used as Update
+// options.
+type UpdateOptsBuilder interface {
+	ToVIPUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts contains all the values needed to update an existing virtual IP.
@@ -194,63 +124,40 @@
 // be updated.
 type UpdateOpts struct {
 	// Human-readable name for the VIP. Does not have to be unique.
-	Name string
-
-	// Required. The ID of the pool with which the VIP is associated.
-	PoolID string
-
-	// Optional. Human-readable description for the VIP.
-	Description string
-
-	// Optional. Omit this field to prevent session persistence.
-	Persistence *SessionPersistence
-
-	// Optional. The maximum number of connections allowed for the VIP.
-	ConnLimit *int
-
-	// Optional. The administrative state of the VIP. A valid value is true (UP)
+	Name *string `json:"name,omitempty"`
+	// The ID of the pool with which the VIP is associated.
+	PoolID *string `json:"pool_id,omitempty"`
+	// Human-readable description for the VIP.
+	Description *string `json:"description,omitempty"`
+	// Omit this field to prevent session persistence.
+	Persistence *SessionPersistence `json:"session_persistence,omitempty"`
+	// The maximum number of connections allowed for the VIP.
+	ConnLimit *int `json:"connection_limit,omitempty"`
+	// The administrative state of the VIP. A valid value is true (UP)
 	// or false (DOWN).
-	AdminStateUp *bool
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
+}
+
+// ToVIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder interface
+func (opts UpdateOpts) ToVIPUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "vip")
 }
 
 // Update is an operation which modifies the attributes of the specified VIP.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type vip struct {
-		Name         string              `json:"name,omitempty"`
-		PoolID       string              `json:"pool_id,omitempty"`
-		Description  *string             `json:"description,omitempty"`
-		Persistence  *SessionPersistence `json:"session_persistence,omitempty"`
-		ConnLimit    *int                `json:"connection_limit,omitempty"`
-		AdminStateUp *bool               `json:"admin_state_up,omitempty"`
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToVIPUpdateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-
-	type request struct {
-		VirtualIP vip `json:"vip"`
-	}
-
-	reqBody := request{VirtualIP: vip{
-		Name:         opts.Name,
-		PoolID:       opts.PoolID,
-		Description:  gophercloud.MaybeString(opts.Description),
-		ConnLimit:    opts.ConnLimit,
-		AdminStateUp: opts.AdminStateUp,
-	}}
-
-	if opts.Persistence != nil {
-		reqBody.VirtualIP.Persistence = opts.Persistence
-	}
-
-	var res UpdateResult
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
-
-	return res
+	return
 }
 
 // Delete will permanently delete a particular virtual IP based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/lbaas/vips/requests_test.go b/openstack/networking/v2/extensions/lbaas/vips/requests_test.go
deleted file mode 100644
index 430f1a1..0000000
--- a/openstack/networking/v2/extensions/lbaas/vips/requests_test.go
+++ /dev/null
@@ -1,336 +0,0 @@
-package vips
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lb/vips", rootURL(fake.ServiceClient()))
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lb/vips/foo", resourceURL(fake.ServiceClient(), "foo"))
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/vips", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "vips":[
-         {
-           "id": "db902c0c-d5ff-4753-b465-668ad9656918",
-           "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
-           "name": "web_vip",
-           "description": "lb config for the web tier",
-           "subnet_id": "96a4386a-f8c3-42ed-afce-d7954eee77b3",
-           "address" : "10.30.176.47",
-           "port_id" : "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
-           "protocol": "HTTP",
-           "protocol_port": 80,
-           "pool_id" : "cfc6589d-f949-4c66-99d2-c2da56ef3764",
-           "admin_state_up": true,
-           "status": "ACTIVE"
-         },
-         {
-           "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-           "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
-           "name": "db_vip",
-					 "description": "lb config for the db tier",
-           "subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
-           "address" : "10.30.176.48",
-           "port_id" : "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
-           "protocol": "TCP",
-           "protocol_port": 3306,
-           "pool_id" : "41efe233-7591-43c5-9cf7-923964759f9e",
-           "session_persistence" : {"type" : "SOURCE_IP"},
-           "connection_limit" : 2000,
-           "admin_state_up": true,
-           "status": "INACTIVE"
-         }
-      ]
-}
-			`)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractVIPs(page)
-		if err != nil {
-			t.Errorf("Failed to extract LBs: %v", err)
-			return false, err
-		}
-
-		expected := []VirtualIP{
-			VirtualIP{
-				ID:           "db902c0c-d5ff-4753-b465-668ad9656918",
-				TenantID:     "310df60f-2a10-4ee5-9554-98393092194c",
-				Name:         "web_vip",
-				Description:  "lb config for the web tier",
-				SubnetID:     "96a4386a-f8c3-42ed-afce-d7954eee77b3",
-				Address:      "10.30.176.47",
-				PortID:       "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
-				Protocol:     "HTTP",
-				ProtocolPort: 80,
-				PoolID:       "cfc6589d-f949-4c66-99d2-c2da56ef3764",
-				Persistence:  SessionPersistence{},
-				ConnLimit:    0,
-				AdminStateUp: true,
-				Status:       "ACTIVE",
-			},
-			VirtualIP{
-				ID:           "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-				TenantID:     "310df60f-2a10-4ee5-9554-98393092194c",
-				Name:         "db_vip",
-				Description:  "lb config for the db tier",
-				SubnetID:     "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
-				Address:      "10.30.176.48",
-				PortID:       "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
-				Protocol:     "TCP",
-				ProtocolPort: 3306,
-				PoolID:       "41efe233-7591-43c5-9cf7-923964759f9e",
-				Persistence:  SessionPersistence{Type: "SOURCE_IP"},
-				ConnLimit:    2000,
-				AdminStateUp: true,
-				Status:       "INACTIVE",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/vips", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "vip": {
-        "protocol": "HTTP",
-        "name": "NewVip",
-        "admin_state_up": true,
-        "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
-        "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
-        "protocol_port": 80,
-				"session_persistence": {"type": "SOURCE_IP"}
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "vip": {
-        "status": "PENDING_CREATE",
-        "protocol": "HTTP",
-        "description": "",
-        "admin_state_up": true,
-        "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
-        "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
-        "connection_limit": -1,
-        "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
-        "address": "10.0.0.11",
-        "protocol_port": 80,
-        "port_id": "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5",
-        "id": "c987d2be-9a3c-4ac9-a046-e8716b1350e2",
-        "name": "NewVip"
-    }
-}
-		`)
-	})
-
-	opts := CreateOpts{
-		Protocol:     "HTTP",
-		Name:         "NewVip",
-		AdminStateUp: Up,
-		SubnetID:     "8032909d-47a1-4715-90af-5153ffe39861",
-		PoolID:       "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
-		ProtocolPort: 80,
-		Persistence:  &SessionPersistence{Type: "SOURCE_IP"},
-	}
-
-	r, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "PENDING_CREATE", r.Status)
-	th.AssertEquals(t, "HTTP", r.Protocol)
-	th.AssertEquals(t, "", r.Description)
-	th.AssertEquals(t, true, r.AdminStateUp)
-	th.AssertEquals(t, "8032909d-47a1-4715-90af-5153ffe39861", r.SubnetID)
-	th.AssertEquals(t, "83657cfcdfe44cd5920adaf26c48ceea", r.TenantID)
-	th.AssertEquals(t, -1, r.ConnLimit)
-	th.AssertEquals(t, "61b1f87a-7a21-4ad3-9dda-7f81d249944f", r.PoolID)
-	th.AssertEquals(t, "10.0.0.11", r.Address)
-	th.AssertEquals(t, 80, r.ProtocolPort)
-	th.AssertEquals(t, "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5", r.PortID)
-	th.AssertEquals(t, "c987d2be-9a3c-4ac9-a046-e8716b1350e2", r.ID)
-	th.AssertEquals(t, "NewVip", r.Name)
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), CreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Name: "foo"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Name: "foo", SubnetID: "bar"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar", ProtocolPort: 80})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "vip": {
-        "status": "ACTIVE",
-        "protocol": "HTTP",
-        "description": "",
-        "admin_state_up": true,
-        "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
-        "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
-        "connection_limit": 1000,
-        "pool_id": "72741b06-df4d-4715-b142-276b6bce75ab",
-        "session_persistence": {
-            "cookie_name": "MyAppCookie",
-            "type": "APP_COOKIE"
-        },
-        "address": "10.0.0.10",
-        "protocol_port": 80,
-        "port_id": "b5a743d6-056b-468b-862d-fb13a9aa694e",
-        "id": "4ec89087-d057-4e2c-911f-60a3b47ee304",
-        "name": "my-vip"
-    }
-}
-			`)
-	})
-
-	vip, err := Get(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "ACTIVE", vip.Status)
-	th.AssertEquals(t, "HTTP", vip.Protocol)
-	th.AssertEquals(t, "", vip.Description)
-	th.AssertEquals(t, true, vip.AdminStateUp)
-	th.AssertEquals(t, 1000, vip.ConnLimit)
-	th.AssertEquals(t, SessionPersistence{Type: "APP_COOKIE", CookieName: "MyAppCookie"}, vip.Persistence)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "vip": {
-        "connection_limit": 1000,
-				"session_persistence": {"type": "SOURCE_IP"}
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-
-		fmt.Fprintf(w, `
-{
-    "vip": {
-        "status": "PENDING_UPDATE",
-        "protocol": "HTTP",
-        "description": "",
-        "admin_state_up": true,
-        "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
-        "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
-        "connection_limit": 1000,
-        "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
-        "address": "10.0.0.11",
-        "protocol_port": 80,
-        "port_id": "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5",
-        "id": "c987d2be-9a3c-4ac9-a046-e8716b1350e2",
-        "name": "NewVip"
-    }
-}
-		`)
-	})
-
-	i1000 := 1000
-	options := UpdateOpts{
-		ConnLimit:   &i1000,
-		Persistence: &SessionPersistence{Type: "SOURCE_IP"},
-	}
-	vip, err := Update(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "PENDING_UPDATE", vip.Status)
-	th.AssertEquals(t, 1000, vip.ConnLimit)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/extensions/lbaas/vips/results.go b/openstack/networking/v2/extensions/lbaas/vips/results.go
index e1092e7..7ac7e79 100644
--- a/openstack/networking/v2/extensions/lbaas/vips/results.go
+++ b/openstack/networking/v2/extensions/lbaas/vips/results.go
@@ -1,9 +1,8 @@
 package vips
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // SessionPersistence represents the session persistence feature of the load
@@ -23,10 +22,10 @@
 //              same member of the pool.
 type SessionPersistence struct {
 	// The type of persistence mode
-	Type string `mapstructure:"type" json:"type"`
+	Type string `json:"type"`
 
 	// Name of cookie if persistence mode is set appropriately
-	CookieName string `mapstructure:"cookie_name" json:"cookie_name,omitempty"`
+	CookieName string `json:"cookie_name,omitempty"`
 }
 
 // VirtualIP is the primary load balancing configuration object that specifies
@@ -36,49 +35,49 @@
 // server", a "vserver" or a "listener".
 type VirtualIP struct {
 	// The unique ID for the VIP.
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 
 	// Owner of the VIP. Only an admin user can specify a tenant ID other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// Human-readable name for the VIP. Does not have to be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 
 	// Human-readable description for the VIP.
-	Description string `mapstructure:"description" json:"description"`
+	Description string `json:"description"`
 
 	// The ID of the subnet on which to allocate the VIP address.
-	SubnetID string `mapstructure:"subnet_id" json:"subnet_id"`
+	SubnetID string `json:"subnet_id"`
 
 	// The IP address of the VIP.
-	Address string `mapstructure:"address" json:"address"`
+	Address string `json:"address"`
 
 	// The protocol of the VIP address. A valid value is TCP, HTTP, or HTTPS.
-	Protocol string `mapstructure:"protocol" json:"protocol"`
+	Protocol string `json:"protocol"`
 
 	// The port on which to listen to client traffic that is associated with the
 	// VIP address. A valid value is from 0 to 65535.
-	ProtocolPort int `mapstructure:"protocol_port" json:"protocol_port"`
+	ProtocolPort int `json:"protocol_port"`
 
 	// The ID of the pool with which the VIP is associated.
-	PoolID string `mapstructure:"pool_id" json:"pool_id"`
+	PoolID string `json:"pool_id"`
 
 	// The ID of the port which belongs to the load balancer
-	PortID string `mapstructure:"port_id" json:"port_id"`
+	PortID string `json:"port_id"`
 
 	// Indicates whether connections in the same session will be processed by the
 	// same pool member or not.
-	Persistence SessionPersistence `mapstructure:"session_persistence" json:"session_persistence"`
+	Persistence SessionPersistence `json:"session_persistence"`
 
 	// The maximum number of connections allowed for the VIP. Default is -1,
 	// meaning no limit.
-	ConnLimit int `mapstructure:"connection_limit" json:"connection_limit"`
+	ConnLimit int `json:"connection_limit"`
 
 	// The administrative state of the VIP. A valid value is true (UP) or false (DOWN).
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// The status of the VIP. Indicates whether the VIP is operational.
-	Status string `mapstructure:"status" json:"status"`
+	Status string `json:"status"`
 }
 
 // VIPPage is the page returned by a pager when traversing over a
@@ -90,40 +89,32 @@
 // NextPageURL is invoked when a paginated collection of routers has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p VIPPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"vips_links"`
+func (r VIPPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"vips_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
-// IsEmpty checks whether a RouterPage struct is empty.
-func (p VIPPage) IsEmpty() (bool, error) {
-	is, err := ExtractVIPs(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+// IsEmpty checks whether a VIPPage struct is empty.
+func (r VIPPage) IsEmpty() (bool, error) {
+	is, err := ExtractVIPs(r)
+	return len(is) == 0, err
 }
 
 // ExtractVIPs accepts a Page struct, specifically a VIPPage struct,
 // and extracts the elements into a slice of VirtualIP structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractVIPs(page pagination.Page) ([]VirtualIP, error) {
-	var resp struct {
-		VIPs []VirtualIP `mapstructure:"vips" json:"vips"`
+func ExtractVIPs(r pagination.Page) ([]VirtualIP, error) {
+	var s struct {
+		VIPs []VirtualIP `json:"vips"`
 	}
-
-	err := mapstructure.Decode(page.(VIPPage).Body, &resp)
-
-	return resp.VIPs, err
+	err := (r.(VIPPage)).ExtractInto(&s)
+	return s.VIPs, err
 }
 
 type commonResult struct {
@@ -132,17 +123,11 @@
 
 // Extract is a function that accepts a result and extracts a router.
 func (r commonResult) Extract() (*VirtualIP, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		VirtualIP *VirtualIP `json:"vip" json:"vip"`
 	}
-
-	var res struct {
-		VirtualIP *VirtualIP `mapstructure:"vip" json:"vip"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.VirtualIP, err
+	err := r.ExtractInto(&s)
+	return s.VirtualIP, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/lbaas/vips/testing/doc.go b/openstack/networking/v2/extensions/lbaas/vips/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas/vips/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go
new file mode 100644
index 0000000..7f9b6dd
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go
@@ -0,0 +1,330 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/vips", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+  "vips":[
+         {
+           "id": "db902c0c-d5ff-4753-b465-668ad9656918",
+           "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
+           "name": "web_vip",
+           "description": "lb config for the web tier",
+           "subnet_id": "96a4386a-f8c3-42ed-afce-d7954eee77b3",
+           "address" : "10.30.176.47",
+           "port_id" : "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
+           "protocol": "HTTP",
+           "protocol_port": 80,
+           "pool_id" : "cfc6589d-f949-4c66-99d2-c2da56ef3764",
+           "admin_state_up": true,
+           "status": "ACTIVE"
+         },
+         {
+           "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+           "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
+           "name": "db_vip",
+					 "description": "lb config for the db tier",
+           "subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+           "address" : "10.30.176.48",
+           "port_id" : "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
+           "protocol": "TCP",
+           "protocol_port": 3306,
+           "pool_id" : "41efe233-7591-43c5-9cf7-923964759f9e",
+           "session_persistence" : {"type" : "SOURCE_IP"},
+           "connection_limit" : 2000,
+           "admin_state_up": true,
+           "status": "INACTIVE"
+         }
+      ]
+}
+			`)
+	})
+
+	count := 0
+
+	vips.List(fake.ServiceClient(), vips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := vips.ExtractVIPs(page)
+		if err != nil {
+			t.Errorf("Failed to extract LBs: %v", err)
+			return false, err
+		}
+
+		expected := []vips.VirtualIP{
+			{
+				ID:           "db902c0c-d5ff-4753-b465-668ad9656918",
+				TenantID:     "310df60f-2a10-4ee5-9554-98393092194c",
+				Name:         "web_vip",
+				Description:  "lb config for the web tier",
+				SubnetID:     "96a4386a-f8c3-42ed-afce-d7954eee77b3",
+				Address:      "10.30.176.47",
+				PortID:       "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
+				Protocol:     "HTTP",
+				ProtocolPort: 80,
+				PoolID:       "cfc6589d-f949-4c66-99d2-c2da56ef3764",
+				Persistence:  vips.SessionPersistence{},
+				ConnLimit:    0,
+				AdminStateUp: true,
+				Status:       "ACTIVE",
+			},
+			{
+				ID:           "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+				TenantID:     "310df60f-2a10-4ee5-9554-98393092194c",
+				Name:         "db_vip",
+				Description:  "lb config for the db tier",
+				SubnetID:     "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+				Address:      "10.30.176.48",
+				PortID:       "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
+				Protocol:     "TCP",
+				ProtocolPort: 3306,
+				PoolID:       "41efe233-7591-43c5-9cf7-923964759f9e",
+				Persistence:  vips.SessionPersistence{Type: "SOURCE_IP"},
+				ConnLimit:    2000,
+				AdminStateUp: true,
+				Status:       "INACTIVE",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/vips", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "vip": {
+        "protocol": "HTTP",
+        "name": "NewVip",
+        "admin_state_up": true,
+        "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
+        "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
+        "protocol_port": 80,
+				"session_persistence": {"type": "SOURCE_IP"}
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "vip": {
+        "status": "PENDING_CREATE",
+        "protocol": "HTTP",
+        "description": "",
+        "admin_state_up": true,
+        "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
+        "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
+        "connection_limit": -1,
+        "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
+        "address": "10.0.0.11",
+        "protocol_port": 80,
+        "port_id": "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5",
+        "id": "c987d2be-9a3c-4ac9-a046-e8716b1350e2",
+        "name": "NewVip"
+    }
+}
+		`)
+	})
+
+	opts := vips.CreateOpts{
+		Protocol:     "HTTP",
+		Name:         "NewVip",
+		AdminStateUp: gophercloud.Enabled,
+		SubnetID:     "8032909d-47a1-4715-90af-5153ffe39861",
+		PoolID:       "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
+		ProtocolPort: 80,
+		Persistence:  &vips.SessionPersistence{Type: "SOURCE_IP"},
+	}
+
+	r, err := vips.Create(fake.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "PENDING_CREATE", r.Status)
+	th.AssertEquals(t, "HTTP", r.Protocol)
+	th.AssertEquals(t, "", r.Description)
+	th.AssertEquals(t, true, r.AdminStateUp)
+	th.AssertEquals(t, "8032909d-47a1-4715-90af-5153ffe39861", r.SubnetID)
+	th.AssertEquals(t, "83657cfcdfe44cd5920adaf26c48ceea", r.TenantID)
+	th.AssertEquals(t, -1, r.ConnLimit)
+	th.AssertEquals(t, "61b1f87a-7a21-4ad3-9dda-7f81d249944f", r.PoolID)
+	th.AssertEquals(t, "10.0.0.11", r.Address)
+	th.AssertEquals(t, 80, r.ProtocolPort)
+	th.AssertEquals(t, "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5", r.PortID)
+	th.AssertEquals(t, "c987d2be-9a3c-4ac9-a046-e8716b1350e2", r.ID)
+	th.AssertEquals(t, "NewVip", r.Name)
+}
+
+func TestRequiredCreateOpts(t *testing.T) {
+	res := vips.Create(fake.ServiceClient(), vips.CreateOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar", ProtocolPort: 80})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "vip": {
+        "status": "ACTIVE",
+        "protocol": "HTTP",
+        "description": "",
+        "admin_state_up": true,
+        "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
+        "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
+        "connection_limit": 1000,
+        "pool_id": "72741b06-df4d-4715-b142-276b6bce75ab",
+        "session_persistence": {
+            "cookie_name": "MyAppCookie",
+            "type": "APP_COOKIE"
+        },
+        "address": "10.0.0.10",
+        "protocol_port": 80,
+        "port_id": "b5a743d6-056b-468b-862d-fb13a9aa694e",
+        "id": "4ec89087-d057-4e2c-911f-60a3b47ee304",
+        "name": "my-vip"
+    }
+}
+			`)
+	})
+
+	vip, err := vips.Get(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "ACTIVE", vip.Status)
+	th.AssertEquals(t, "HTTP", vip.Protocol)
+	th.AssertEquals(t, "", vip.Description)
+	th.AssertEquals(t, true, vip.AdminStateUp)
+	th.AssertEquals(t, 1000, vip.ConnLimit)
+	th.AssertEquals(t, vips.SessionPersistence{Type: "APP_COOKIE", CookieName: "MyAppCookie"}, vip.Persistence)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "vip": {
+        "connection_limit": 1000,
+				"session_persistence": {"type": "SOURCE_IP"}
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusAccepted)
+
+		fmt.Fprintf(w, `
+{
+    "vip": {
+        "status": "PENDING_UPDATE",
+        "protocol": "HTTP",
+        "description": "",
+        "admin_state_up": true,
+        "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
+        "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
+        "connection_limit": 1000,
+        "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
+        "address": "10.0.0.11",
+        "protocol_port": 80,
+        "port_id": "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5",
+        "id": "c987d2be-9a3c-4ac9-a046-e8716b1350e2",
+        "name": "NewVip"
+    }
+}
+		`)
+	})
+
+	i1000 := 1000
+	options := vips.UpdateOpts{
+		ConnLimit:   &i1000,
+		Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"},
+	}
+	vip, err := vips.Update(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304", options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "PENDING_UPDATE", vip.Status)
+	th.AssertEquals(t, 1000, vip.ConnLimit)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := vips.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/extensions/lbaas/vips/urls.go b/openstack/networking/v2/extensions/lbaas/vips/urls.go
index 2b6f67e..584a1cf 100644
--- a/openstack/networking/v2/extensions/lbaas/vips/urls.go
+++ b/openstack/networking/v2/extensions/lbaas/vips/urls.go
@@ -1,6 +1,6 @@
 package vips
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	rootPath     = "lb"
diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/fixtures.go
deleted file mode 100644
index 4c22f63..0000000
--- a/openstack/networking/v2/extensions/lbaas_v2/listeners/fixtures.go
+++ /dev/null
@@ -1,214 +0,0 @@
-// +build fixtures
-
-package listeners
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ListenersListBody contains the canned body of a listeners list response.
-const ListenersListBody = `
-{
-	"listeners":[
-		{
-			"id": "db902c0c-d5ff-4753-b465-668ad9656918",
-			"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
-			"name": "web",
-			"description": "listener config for the web tier",
-			"loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}],
-			"protocol": "HTTP",
-			"protocol_port": 80,
-			"default_pool_id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
-			"admin_state_up": true,
-			"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
-			"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
-		},
-		{
-			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-			"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
-			"name": "db",
-			"description": "listener config for the db tier",
-			"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
-			"protocol": "TCP",
-			"protocol_port": 3306,
-			"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
-			"connection_limit": 2000,
-			"admin_state_up": true,
-			"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
-			"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
-		}
-	]
-}
-`
-
-// SingleServerBody is the canned body of a Get request on an existing listener.
-const SingleListenerBody = `
-{
-	"listener": {
-		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-		"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
-		"name": "db",
-		"description": "listener config for the db tier",
-		"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
-		"protocol": "TCP",
-		"protocol_port": 3306,
-		"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
-		"connection_limit": 2000,
-		"admin_state_up": true,
-		"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
-		"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
-	}
-}
-`
-
-// PostUpdateListenerBody is the canned response body of a Update request on an existing listener.
-const PostUpdateListenerBody = `
-{
-	"listener": {
-		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-		"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
-		"name": "NewListenerName",
-		"description": "listener config for the db tier",
-		"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
-		"protocol": "TCP",
-		"protocol_port": 3306,
-		"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
-		"connection_limit": 1000,
-		"admin_state_up": true,
-		"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
-		"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
-	}
-}
-`
-
-var (
-	ListenerWeb = Listener{
-		ID:                     "db902c0c-d5ff-4753-b465-668ad9656918",
-		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
-		Name:                   "web",
-		Description:            "listener config for the web tier",
-		Loadbalancers:          []LoadBalancerID{{ID: "53306cda-815d-4354-9444-59e09da9c3c5"}},
-		Protocol:               "HTTP",
-		ProtocolPort:           80,
-		DefaultPoolID:          "fad389a3-9a4a-4762-a365-8c7038508b5d",
-		AdminStateUp:           true,
-		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
-		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
-	}
-	ListenerDb = Listener{
-		ID:                     "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
-		Name:                   "db",
-		Description:            "listener config for the db tier",
-		Loadbalancers:          []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
-		Protocol:               "TCP",
-		ProtocolPort:           3306,
-		DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
-		ConnLimit:              2000,
-		AdminStateUp:           true,
-		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
-		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
-	}
-	ListenerUpdated = Listener{
-		ID:                     "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
-		Name:                   "NewListenerName",
-		Description:            "listener config for the db tier",
-		Loadbalancers:          []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
-		Protocol:               "TCP",
-		ProtocolPort:           3306,
-		DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
-		ConnLimit:              1000,
-		AdminStateUp:           true,
-		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
-		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
-	}
-)
-
-// HandleListenerListSuccessfully sets up the test server to respond to a listener List request.
-func HandleListenerListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, ListenersListBody)
-		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
-			fmt.Fprintf(w, `{ "listeners": [] }`)
-		default:
-			t.Fatalf("/v2.0/lbaas/listeners invoked with unexpected marker=[%s]", marker)
-		}
-	})
-}
-
-// HandleListenerCreationSuccessfully sets up the test server to respond to a listener creation request
-// with a given response.
-func HandleListenerCreationSuccessfully(t *testing.T, response string) {
-	th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{
-			    "listener": {
-			        "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab",
-			        "protocol": "TCP",
-			        "name": "db",
-			        "admin_state_up": true,
-			        "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
-			        "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
-			        "protocol_port": 3306
-			    }
-		}`)
-
-		w.WriteHeader(http.StatusAccepted)
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, response)
-	})
-}
-
-// HandleListenerGetSuccessfully sets up the test server to respond to a listener Get request.
-func HandleListenerGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		fmt.Fprintf(w, SingleListenerBody)
-	})
-}
-
-// HandleListenerDeletionSuccessfully sets up the test server to respond to a listener deletion request.
-func HandleListenerDeletionSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleListenerUpdateSuccessfully sets up the test server to respond to a listener Update request.
-func HandleListenerUpdateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestJSONRequest(t, r, `{
-			"listener": {
-				"name": "NewListenerName",
-				"connection_limit": 1001
-			}
-		}`)
-
-		fmt.Fprintf(w, PostUpdateListenerBody)
-	})
-}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go
index a62e0cd..4a78447 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go
@@ -1,59 +1,17 @@
 package listeners
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
+type Protocol string
 
-type listenerOpts struct {
-	// Required. The protocol - can either be TCP, HTTP or HTTPS.
-	Protocol Protocol
-
-	// Required. The port on which to listen for client traffic.
-	ProtocolPort int
-
-	// Required for admins. Indicates the owner of the Listener.
-	TenantID string
-
-	// Required. The load balancer on which to provision this listener.
-	LoadbalancerID string
-
-	// Human-readable name for the Listener. Does not have to be unique.
-	Name string
-
-	// Optional. The ID of the default pool with which the Listener is associated.
-	DefaultPoolID string
-
-	// Optional. Human-readable description for the Listener.
-	Description string
-
-	// Optional. The maximum number of connections allowed for the Listener.
-	ConnLimit *int
-
-	// Optional. A reference to a container of TLS secrets.
-	DefaultTlsContainerRef string
-
-	// Optional. A list of references to TLS secrets.
-	SniContainerRefs []string
-
-	// Optional. The administrative state of the Listener. A valid value is true (UP)
-	// or false (DOWN).
-	AdminStateUp *bool
-}
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
+// Supported attributes for create/update operations.
+const (
+	ProtocolTCP   Protocol = "TCP"
+	ProtocolHTTP  Protocol = "HTTP"
+	ProtocolHTTPS Protocol = "HTTPS"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -86,10 +44,7 @@
 // ToListenerListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToListenerListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -107,27 +62,11 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return ListenerPage{pagination.LinkedPageBase{PageResult: r}}
 	})
 }
 
-type Protocol string
-
-// Supported attributes for create/update operations.
-const (
-	ProtocolTCP   Protocol = "TCP"
-	ProtocolHTTP  Protocol = "HTTP"
-	ProtocolHTTPS Protocol = "HTTPS"
-)
-
-var (
-	errLoadbalancerIdRequired = fmt.Errorf("LoadbalancerID is required")
-	errProtocolRequired       = fmt.Errorf("Protocol is required")
-	errProtocolPortRequired   = fmt.Errorf("ProtocolPort  is required")
-)
-
 // CreateOptsBuilder is the interface options structs have to satisfy in order
 // to be used in the main Create operation in this package. Since many
 // extensions decorate or modify the common logic, it is useful for them to
@@ -138,53 +77,35 @@
 
 // CreateOpts is the common options struct used in this package's Create
 // operation.
-type CreateOpts listenerOpts
+type CreateOpts struct {
+	// The load balancer on which to provision this listener.
+	LoadbalancerID string `json:"loadbalancer_id" required:"true"`
+	// The protocol - can either be TCP, HTTP or HTTPS.
+	Protocol Protocol `json:"protocol" required:"true"`
+	// The port on which to listen for client traffic.
+	ProtocolPort int `json:"protocol_port" required:"true"`
+	// Indicates the owner of the Listener. Required for admins.
+	TenantID string `json:"tenant_id,omitempty"`
+	// Human-readable name for the Listener. Does not have to be unique.
+	Name string `json:"name,omitempty"`
+	// The ID of the default pool with which the Listener is associated.
+	DefaultPoolID string `json:"default_pool_id,omitempty"`
+	// Human-readable description for the Listener.
+	Description string `json:"description,omitempty"`
+	// The maximum number of connections allowed for the Listener.
+	ConnLimit *int `json:"connection_limit,omitempty"`
+	// A reference to a container of TLS secrets.
+	DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"`
+	// A list of references to TLS secrets.
+	SniContainerRefs []string `json:"sni_container_refs,omitempty"`
+	// The administrative state of the Listener. A valid value is true (UP)
+	// or false (DOWN).
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
+}
 
 // ToListenerCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToListenerCreateMap() (map[string]interface{}, error) {
-	l := make(map[string]interface{})
-
-	if opts.LoadbalancerID != "" {
-		l["loadbalancer_id"] = opts.LoadbalancerID
-	} else {
-		return nil, errLoadbalancerIdRequired
-	}
-	if opts.Protocol != "" {
-		l["protocol"] = opts.Protocol
-	} else {
-		return nil, errProtocolRequired
-	}
-	if opts.ProtocolPort != 0 {
-		l["protocol_port"] = opts.ProtocolPort
-	} else {
-		return nil, errProtocolPortRequired
-	}
-	if opts.AdminStateUp != nil {
-		l["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		l["name"] = opts.Name
-	}
-	if opts.TenantID != "" {
-		l["tenant_id"] = opts.TenantID
-	}
-	if opts.DefaultPoolID != "" {
-		l["default_pool_id"] = opts.DefaultPoolID
-	}
-	if opts.Description != "" {
-		l["description"] = opts.Description
-	}
-	if opts.ConnLimit != nil {
-		l["connection_limit"] = &opts.ConnLimit
-	}
-	if opts.DefaultTlsContainerRef != "" {
-		l["default_tls_container_ref"] = opts.DefaultTlsContainerRef
-	}
-	if opts.SniContainerRefs != nil {
-		l["sni_container_refs"] = opts.SniContainerRefs
-	}
-
-	return map[string]interface{}{"listener": l}, nil
+	return gophercloud.BuildRequestBody(opts, "listener")
 }
 
 // Create is an operation which provisions a new Listeners based on the
@@ -194,25 +115,20 @@
 //
 // Users with an admin role can create Listeners on behalf of other tenants by
 // specifying a TenantID attribute different than their own.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToListenerCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToListenerCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular Listeners based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -225,55 +141,42 @@
 
 // UpdateOpts is the common options struct used in this package's Update
 // operation.
-type UpdateOpts listenerOpts
+type UpdateOpts struct {
+	// Human-readable name for the Listener. Does not have to be unique.
+	Name string `json:"name,omitempty"`
+	// Human-readable description for the Listener.
+	Description string `json:"description,omitempty"`
+	// The maximum number of connections allowed for the Listener.
+	ConnLimit *int `json:"connection_limit,omitempty"`
+	// A reference to a container of TLS secrets.
+	DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"`
+	//  A list of references to TLS secrets.
+	SniContainerRefs []string `json:"sni_container_refs,omitempty"`
+	// The administrative state of the Listener. A valid value is true (UP)
+	// or false (DOWN).
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
+}
 
 // ToListenerUpdateMap casts a UpdateOpts struct to a map.
 func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) {
-	l := make(map[string]interface{})
-
-	if opts.Name != "" {
-		l["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		l["description"] = opts.Description
-	}
-	if opts.ConnLimit != nil {
-		l["connection_limit"] = &opts.ConnLimit
-	}
-	if opts.DefaultTlsContainerRef != "" {
-		l["default_tls_container_ref"] = opts.DefaultTlsContainerRef
-	}
-	if opts.SniContainerRefs != nil {
-		l["sni_container_refs"] = opts.SniContainerRefs
-	}
-	if opts.AdminStateUp != nil {
-		l["admin_state_up"] = &opts.AdminStateUp
-	}
-
-	return map[string]interface{}{"listener": l}, nil
+	return gophercloud.BuildRequestBody(opts, "listener")
 }
 
 // Update is an operation which modifies the attributes of the specified Listener.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToListenerUpdateMap()
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) {
+	b, err := opts.ToListenerUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
-
-	return res
+	return
 }
 
 // Delete will permanently delete a particular Listeners based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests_test.go
deleted file mode 100644
index 4ce3114..0000000
--- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests_test.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package listeners
-
-import (
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"testing"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lbaas/listeners", rootURL(fake.ServiceClient()))
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lbaas/listeners/foo", resourceURL(fake.ServiceClient(), "foo"))
-}
-
-func TestListListeners(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListenerListSuccessfully(t)
-
-	pages := 0
-	err := List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractListeners(page)
-		if err != nil {
-			return false, err
-		}
-
-		if len(actual) != 2 {
-			t.Fatalf("Expected 2 listeners, got %d", len(actual))
-		}
-		th.CheckDeepEquals(t, ListenerWeb, actual[0])
-		th.CheckDeepEquals(t, ListenerDb, actual[1])
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-
-	if pages != 1 {
-		t.Errorf("Expected 1 page, saw %d", pages)
-	}
-}
-
-func TestListAllListeners(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListenerListSuccessfully(t)
-
-	allPages, err := List(fake.ServiceClient(), ListOpts{}).AllPages()
-	th.AssertNoErr(t, err)
-	actual, err := ExtractListeners(allPages)
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, ListenerWeb, actual[0])
-	th.CheckDeepEquals(t, ListenerDb, actual[1])
-}
-
-func TestCreateListener(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListenerCreationSuccessfully(t, SingleListenerBody)
-
-	actual, err := Create(fake.ServiceClient(), CreateOpts{
-		Protocol:               "TCP",
-		Name:                   "db",
-		LoadbalancerID:         "79e05663-7f03-45d2-a092-8b94062f22ab",
-		AdminStateUp:           Up,
-		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
-		DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
-		ProtocolPort:           3306,
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	th.CheckDeepEquals(t, ListenerDb, *actual)
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), CreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Name: "foo"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Name: "foo", TenantID: "bar"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar", ProtocolPort: 80})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestGetListener(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListenerGetSuccessfully(t)
-
-	client := fake.ServiceClient()
-	actual, err := Get(client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract()
-	if err != nil {
-		t.Fatalf("Unexpected Get error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, ListenerDb, *actual)
-}
-
-func TestDeleteListener(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListenerDeletionSuccessfully(t)
-
-	res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestUpdateListener(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListenerUpdateSuccessfully(t)
-
-	client := fake.ServiceClient()
-	i1001 := 1001
-	actual, err := Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", UpdateOpts{
-		Name:      "NewListenerName",
-		ConnLimit: &i1001,
-	}).Extract()
-	if err != nil {
-		t.Fatalf("Unexpected Update error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, ListenerUpdated, *actual)
-}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go
index 77a084a..aa8ed1b 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go
@@ -1,14 +1,13 @@
 package listeners
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 type LoadBalancerID struct {
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 }
 
 // Listener is the primary load balancing configuration object that specifies
@@ -16,44 +15,32 @@
 // as other details such as the load balancing method to be use, protocol, etc.
 type Listener struct {
 	// The unique ID for the Listener.
-	ID string `mapstructure:"id" json:"id"`
-
+	ID string `json:"id"`
 	// Owner of the Listener. Only an admin user can specify a tenant ID other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
-
+	TenantID string `json:"tenant_id"`
 	// Human-readable name for the Listener. Does not have to be unique.
-	Name string `mapstructure:"name" json:"name"`
-
+	Name string `json:"name"`
 	// Human-readable description for the Listener.
-	Description string `mapstructure:"description" json:"description"`
-
+	Description string `json:"description"`
 	// The protocol to loadbalance. A valid value is TCP, HTTP, or HTTPS.
-	Protocol string `mapstructure:"protocol" json:"protocol"`
-
+	Protocol string `json:"protocol"`
 	// The port on which to listen to client traffic that is associated with the
 	// Loadbalancer. A valid value is from 0 to 65535.
-	ProtocolPort int `mapstructure:"protocol_port" json:"protocol_port"`
-
+	ProtocolPort int `json:"protocol_port"`
 	// The UUID of default pool. Must have compatible protocol with listener.
-	DefaultPoolID string `mapstructure:"default_pool_id" json:"default_pool_id"`
-
+	DefaultPoolID string `json:"default_pool_id"`
 	// A list of load balancer IDs.
-	Loadbalancers []LoadBalancerID `mapstructure:"loadbalancers" json:"loadbalancers"`
-
+	Loadbalancers []LoadBalancerID `json:"loadbalancers"`
 	// The maximum number of connections allowed for the Loadbalancer. Default is -1,
 	// meaning no limit.
-	ConnLimit int `mapstructure:"connection_limit" json:"connection_limit"`
-
+	ConnLimit int `json:"connection_limit"`
 	// The list of references to TLS secrets.
-	SniContainerRefs []string `mapstructure:"sni_container_refs" json:"sni_container_refs"`
-
+	SniContainerRefs []string `json:"sni_container_refs"`
 	// Optional. A reference to a container of TLS secrets.
-	DefaultTlsContainerRef string `mapstructure:"default_tls_container_ref" json:"default_tls_container_ref"`
-
+	DefaultTlsContainerRef string `json:"default_tls_container_ref"`
 	// The administrative state of the Listener. A valid value is true (UP) or false (DOWN).
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
-
-	Pools []pools.Pool `mapstructure:"pools" json:"pools"`
+	AdminStateUp bool         `json:"admin_state_up"`
+	Pools        []pools.Pool `json:"pools"`
 }
 
 // ListenerPage is the page returned by a pager when traversing over a
@@ -65,38 +52,32 @@
 // NextPageURL is invoked when a paginated collection of routers has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p ListenerPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"listeners_links"`
+func (r ListenerPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"listeners_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a RouterPage struct is empty.
-func (p ListenerPage) IsEmpty() (bool, error) {
-	is, err := ExtractListeners(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r ListenerPage) IsEmpty() (bool, error) {
+	is, err := ExtractListeners(r)
+	return len(is) == 0, err
 }
 
 // ExtractListeners accepts a Page struct, specifically a ListenerPage struct,
 // and extracts the elements into a slice of Listener structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractListeners(page pagination.Page) ([]Listener, error) {
-	var resp struct {
-		Listeners []Listener `mapstructure:"listeners" json:"listeners"`
+func ExtractListeners(r pagination.Page) ([]Listener, error) {
+	var s struct {
+		Listeners []Listener `json:"listeners"`
 	}
-	err := mapstructure.Decode(page.(ListenerPage).Body, &resp)
-	return resp.Listeners, err
+	err := (r.(ListenerPage)).ExtractInto(&s)
+	return s.Listeners, err
 }
 
 type commonResult struct {
@@ -105,17 +86,11 @@
 
 // Extract is a function that accepts a result and extracts a router.
 func (r commonResult) Extract() (*Listener, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Listener *Listener `json:"listener"`
 	}
-
-	var res struct {
-		Listener *Listener `mapstructure:"listener" json:"listener"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Listener, err
+	err := r.ExtractInto(&s)
+	return s.Listener, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures.go
new file mode 100644
index 0000000..fa4fa25
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures.go
@@ -0,0 +1,213 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ListenersListBody contains the canned body of a listeners list response.
+const ListenersListBody = `
+{
+	"listeners":[
+		{
+			"id": "db902c0c-d5ff-4753-b465-668ad9656918",
+			"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
+			"name": "web",
+			"description": "listener config for the web tier",
+			"loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}],
+			"protocol": "HTTP",
+			"protocol_port": 80,
+			"default_pool_id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
+			"admin_state_up": true,
+			"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
+			"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
+		},
+		{
+			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+			"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
+			"name": "db",
+			"description": "listener config for the db tier",
+			"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+			"protocol": "TCP",
+			"protocol_port": 3306,
+			"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
+			"connection_limit": 2000,
+			"admin_state_up": true,
+			"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
+			"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
+		}
+	]
+}
+`
+
+// SingleServerBody is the canned body of a Get request on an existing listener.
+const SingleListenerBody = `
+{
+	"listener": {
+		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
+		"name": "db",
+		"description": "listener config for the db tier",
+		"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+		"protocol": "TCP",
+		"protocol_port": 3306,
+		"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
+		"connection_limit": 2000,
+		"admin_state_up": true,
+		"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
+		"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
+	}
+}
+`
+
+// PostUpdateListenerBody is the canned response body of a Update request on an existing listener.
+const PostUpdateListenerBody = `
+{
+	"listener": {
+		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
+		"name": "NewListenerName",
+		"description": "listener config for the db tier",
+		"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+		"protocol": "TCP",
+		"protocol_port": 3306,
+		"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
+		"connection_limit": 1000,
+		"admin_state_up": true,
+		"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
+		"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
+	}
+}
+`
+
+var (
+	ListenerWeb = listeners.Listener{
+		ID:                     "db902c0c-d5ff-4753-b465-668ad9656918",
+		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
+		Name:                   "web",
+		Description:            "listener config for the web tier",
+		Loadbalancers:          []listeners.LoadBalancerID{{ID: "53306cda-815d-4354-9444-59e09da9c3c5"}},
+		Protocol:               "HTTP",
+		ProtocolPort:           80,
+		DefaultPoolID:          "fad389a3-9a4a-4762-a365-8c7038508b5d",
+		AdminStateUp:           true,
+		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
+		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
+	}
+	ListenerDb = listeners.Listener{
+		ID:                     "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
+		Name:                   "db",
+		Description:            "listener config for the db tier",
+		Loadbalancers:          []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
+		Protocol:               "TCP",
+		ProtocolPort:           3306,
+		DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
+		ConnLimit:              2000,
+		AdminStateUp:           true,
+		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
+		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
+	}
+	ListenerUpdated = listeners.Listener{
+		ID:                     "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
+		Name:                   "NewListenerName",
+		Description:            "listener config for the db tier",
+		Loadbalancers:          []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
+		Protocol:               "TCP",
+		ProtocolPort:           3306,
+		DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
+		ConnLimit:              1000,
+		AdminStateUp:           true,
+		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
+		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
+	}
+)
+
+// HandleListenerListSuccessfully sets up the test server to respond to a listener List request.
+func HandleListenerListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, ListenersListBody)
+		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
+			fmt.Fprintf(w, `{ "listeners": [] }`)
+		default:
+			t.Fatalf("/v2.0/lbaas/listeners invoked with unexpected marker=[%s]", marker)
+		}
+	})
+}
+
+// HandleListenerCreationSuccessfully sets up the test server to respond to a listener creation request
+// with a given response.
+func HandleListenerCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			    "listener": {
+			        "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab",
+			        "protocol": "TCP",
+			        "name": "db",
+			        "admin_state_up": true,
+			        "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
+			        "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
+			        "protocol_port": 3306
+			    }
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandleListenerGetSuccessfully sets up the test server to respond to a listener Get request.
+func HandleListenerGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleListenerBody)
+	})
+}
+
+// HandleListenerDeletionSuccessfully sets up the test server to respond to a listener deletion request.
+func HandleListenerDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleListenerUpdateSuccessfully sets up the test server to respond to a listener Update request.
+func HandleListenerUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `{
+			"listener": {
+				"name": "NewListenerName",
+				"connection_limit": 1001
+			}
+		}`)
+
+		fmt.Fprintf(w, PostUpdateListenerBody)
+	})
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go
new file mode 100644
index 0000000..8042792
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go
@@ -0,0 +1,137 @@
+package testing
+
+import (
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/jrperritt/gophercloud"
+)
+
+func TestListListeners(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListenerListSuccessfully(t)
+
+	pages := 0
+	err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := listeners.ExtractListeners(page)
+		if err != nil {
+			return false, err
+		}
+
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 listeners, got %d", len(actual))
+		}
+		th.CheckDeepEquals(t, ListenerWeb, actual[0])
+		th.CheckDeepEquals(t, ListenerDb, actual[1])
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
+	}
+}
+
+func TestListAllListeners(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListenerListSuccessfully(t)
+
+	allPages, err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := listeners.ExtractListeners(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, ListenerWeb, actual[0])
+	th.CheckDeepEquals(t, ListenerDb, actual[1])
+}
+
+func TestCreateListener(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListenerCreationSuccessfully(t, SingleListenerBody)
+
+	actual, err := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{
+		Protocol:               "TCP",
+		Name:                   "db",
+		LoadbalancerID:         "79e05663-7f03-45d2-a092-8b94062f22ab",
+		AdminStateUp:           gophercloud.Enabled,
+		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
+		DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
+		ProtocolPort:           3306,
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, ListenerDb, *actual)
+}
+
+func TestRequiredCreateOpts(t *testing.T) {
+	res := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar", ProtocolPort: 80})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestGetListener(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListenerGetSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := listeners.Get(client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, ListenerDb, *actual)
+}
+
+func TestDeleteListener(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListenerDeletionSuccessfully(t)
+
+	res := listeners.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestUpdateListener(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListenerUpdateSuccessfully(t)
+
+	client := fake.ServiceClient()
+	i1001 := 1001
+	actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{
+		Name:      "NewListenerName",
+		ConnLimit: &i1001,
+	}).Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Update error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, ListenerUpdated, *actual)
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go
index b4cb90b..02fb1eb 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go
@@ -1,6 +1,6 @@
 package listeners
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	rootPath     = "lbaas"
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/fixtures.go
deleted file mode 100644
index cb43158..0000000
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/fixtures.go
+++ /dev/null
@@ -1,278 +0,0 @@
-// +build fixtures
-
-package loadbalancers
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
-)
-
-// LoadbalancersListBody contains the canned body of a loadbalancer list response.
-const LoadbalancersListBody = `
-{
-	"loadbalancers":[
-	         {
-			"id": "c331058c-6a40-4144-948e-b9fb1df9db4b",
-			"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
-			"name": "web_lb",
-			"description": "lb config for the web tier",
-			"vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154",
-			"vip_address": "10.30.176.47",
-			"flavor": "small",
-			"provider": "haproxy",
-			"admin_state_up": true,
-			"provisioning_status": "ACTIVE",
-			"operating_status": "ONLINE"
-		},
-		{
-			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-			"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
-			"name": "db_lb",
-			"description": "lb config for the db tier",
-			"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
-			"vip_address": "10.30.176.48",
-			"flavor": "medium",
-			"provider": "haproxy",
-			"admin_state_up": true,
-			"provisioning_status": "PENDING_CREATE",
-			"operating_status": "OFFLINE"
-		}
-	]
-}
-`
-
-// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer.
-const SingleLoadbalancerBody = `
-{
-	"loadbalancer": {
-		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-		"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
-		"name": "db_lb",
-		"description": "lb config for the db tier",
-		"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
-		"vip_address": "10.30.176.48",
-		"flavor": "medium",
-		"provider": "haproxy",
-		"admin_state_up": true,
-		"provisioning_status": "PENDING_CREATE",
-		"operating_status": "OFFLINE"
-	}
-}
-`
-
-// PostUpdateLoadbalancerBody is the canned response body of a Update request on an existing loadbalancer.
-const PostUpdateLoadbalancerBody = `
-{
-	"loadbalancer": {
-		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-		"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
-		"name": "NewLoadbalancerName",
-		"description": "lb config for the db tier",
-		"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
-		"vip_address": "10.30.176.48",
-		"flavor": "medium",
-		"provider": "haproxy",
-		"admin_state_up": true,
-		"provisioning_status": "PENDING_CREATE",
-		"operating_status": "OFFLINE"
-	}
-}
-`
-
-// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer.
-const LoadbalancerStatuesesTree = `
-{
-	"statuses" : {
-		"loadbalancer": {
-			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-			"name": "db_lb",
-			"provisioning_status": "PENDING_UPDATE",
-			"operating_status": "ACTIVE",
-			"listeners": [{
-				"id": "db902c0c-d5ff-4753-b465-668ad9656918",
-				"name": "db",
-				"pools": [{
-					"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
-					"name": "db",
-					"healthmonitor": {
-						"id": "67306cda-815d-4354-9fe4-59e09da9c3c5",
-						"type":"PING"
-					},
-					"members":[{
-						"id": "2a280670-c202-4b0b-a562-34077415aabf",
-						"name": "db",
-						"address": "10.0.2.11",
-						"protocol_port": 80
-					}]
-				}]
-			}]
-		}
-	}
-}
-`
-
-var (
-	LoadbalancerWeb = LoadBalancer{
-		ID:                 "c331058c-6a40-4144-948e-b9fb1df9db4b",
-		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
-		Name:               "web_lb",
-		Description:        "lb config for the web tier",
-		VipSubnetID:        "8a49c438-848f-467b-9655-ea1548708154",
-		VipAddress:         "10.30.176.47",
-		Flavor:             "small",
-		Provider:           "haproxy",
-		AdminStateUp:       true,
-		ProvisioningStatus: "ACTIVE",
-		OperatingStatus:    "ONLINE",
-	}
-	LoadbalancerDb = LoadBalancer{
-		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
-		Name:               "db_lb",
-		Description:        "lb config for the db tier",
-		VipSubnetID:        "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
-		VipAddress:         "10.30.176.48",
-		Flavor:             "medium",
-		Provider:           "haproxy",
-		AdminStateUp:       true,
-		ProvisioningStatus: "PENDING_CREATE",
-		OperatingStatus:    "OFFLINE",
-	}
-	LoadbalancerUpdated = LoadBalancer{
-		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
-		Name:               "NewLoadbalancerName",
-		Description:        "lb config for the db tier",
-		VipSubnetID:        "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
-		VipAddress:         "10.30.176.48",
-		Flavor:             "medium",
-		Provider:           "haproxy",
-		AdminStateUp:       true,
-		ProvisioningStatus: "PENDING_CREATE",
-		OperatingStatus:    "OFFLINE",
-	}
-	LoadbalancerStatusesTree = LoadBalancer{
-		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-		Name:               "db_lb",
-		ProvisioningStatus: "PENDING_UPDATE",
-		OperatingStatus:    "ACTIVE",
-		Listeners: []listeners.Listener{{
-			ID:   "db902c0c-d5ff-4753-b465-668ad9656918",
-			Name: "db",
-			Pools: []pools.Pool{{
-				ID:   "fad389a3-9a4a-4762-a365-8c7038508b5d",
-				Name: "db",
-				Monitor: monitors.Monitor{
-					ID:   "67306cda-815d-4354-9fe4-59e09da9c3c5",
-					Type: "PING",
-				},
-				Members: []pools.Member{{
-					ID:           "2a280670-c202-4b0b-a562-34077415aabf",
-					Name:         "db",
-					Address:      "10.0.2.11",
-					ProtocolPort: 80,
-				}},
-			}},
-		}},
-	}
-)
-
-// HandleLoadbalancerListSuccessfully sets up the test server to respond to a loadbalancer List request.
-func HandleLoadbalancerListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, LoadbalancersListBody)
-		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
-			fmt.Fprintf(w, `{ "loadbalancers": [] }`)
-		default:
-			t.Fatalf("/v2.0/lbaas/loadbalancers invoked with unexpected marker=[%s]", marker)
-		}
-	})
-}
-
-// HandleLoadbalancerCreationSuccessfully sets up the test server to respond to a loadbalancer creation request
-// with a given response.
-func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) {
-	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{
-			"loadbalancer": {
-				"name": "db_lb",
-				"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
-				"vip_address": "10.30.176.48",
-				"flavor": "medium",
-				"provider": "haproxy",
-				"admin_state_up": true
-			}
-		}`)
-
-		w.WriteHeader(http.StatusAccepted)
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, response)
-	})
-}
-
-// HandleLoadbalancerGetSuccessfully sets up the test server to respond to a loadbalancer Get request.
-func HandleLoadbalancerGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		fmt.Fprintf(w, SingleLoadbalancerBody)
-	})
-}
-
-// HandleLoadbalancerGetStatusesTree sets up the test server to respond to a loadbalancer Get statuses tree request.
-func HandleLoadbalancerGetStatusesTree(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/statuses", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		fmt.Fprintf(w, LoadbalancerStatuesesTree)
-	})
-}
-
-// HandleLoadbalancerDeletionSuccessfully sets up the test server to respond to a loadbalancer deletion request.
-func HandleLoadbalancerDeletionSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleLoadbalancerUpdateSuccessfully sets up the test server to respond to a loadbalancer Update request.
-func HandleLoadbalancerUpdateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestJSONRequest(t, r, `{
-			"loadbalancer": {
-				"name": "NewLoadbalancerName"
-			}
-		}`)
-
-		fmt.Fprintf(w, PostUpdateLoadbalancerBody)
-	})
-}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go
index 6607b8a..ce493c9 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go
@@ -1,59 +1,14 @@
 package loadbalancers
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-type loadbalancerOpts struct {
-	// Optional. Human-readable name for the Loadbalancer. Does not have to be unique.
-	Name string
-
-	// Optional. Human-readable description for the Loadbalancer.
-	Description string
-
-	// Required. The network on which to allocate the Loadbalancer's address. A tenant can
-	// only create Loadbalancers on networks authorized by policy (e.g. networks that
-	// belong to them or networks that are shared).
-	VipSubnetID string
-
-	// Required for admins. The UUID of the tenant who owns the Loadbalancer.
-	// Only administrative users can specify a tenant UUID other than their own.
-	TenantID string
-
-	// Optional. The IP address of the Loadbalancer.
-	VipAddress string
-
-	// Optional. The administrative state of the Loadbalancer. A valid value is true (UP)
-	// or false (DOWN).
-	AdminStateUp *bool
-
-	// Optional. The UUID of a flavor.
-	Flavor string
-
-	// Optional. The name of the provider.
-	Provider string
-}
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
 // List request.
 type ListOptsBuilder interface {
-	ToLoadbalancerListQuery() (string, error)
+	ToLoadBalancerListQuery() (string, error)
 }
 
 // ListOpts allows the filtering and sorting of paginated collections through
@@ -80,12 +35,9 @@
 }
 
 // ToLoadbalancerListQuery formats a ListOpts into a query string.
-func (opts ListOpts) ToLoadbalancerListQuery() (string, error) {
+func (opts ListOpts) ToLoadBalancerListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -97,66 +49,53 @@
 func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
 	url := rootURL(c)
 	if opts != nil {
-		query, err := opts.ToLoadbalancerListQuery()
+		query, err := opts.ToLoadBalancerListQuery()
 		if err != nil {
 			return pagination.Pager{Err: err}
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
-		return LoadbalancerPage{pagination.LinkedPageBase{PageResult: r}}
+		return LoadBalancerPage{pagination.LinkedPageBase{PageResult: r}}
 	})
 }
 
-var (
-	errVipSubnetIDRequried = fmt.Errorf("VipSubnetID is required")
-)
-
 // CreateOptsBuilder is the interface options structs have to satisfy in order
 // to be used in the main Create operation in this package. Since many
 // extensions decorate or modify the common logic, it is useful for them to
 // satisfy a basic interface in order for them to be used.
 type CreateOptsBuilder interface {
-	ToLoadbalancerCreateMap() (map[string]interface{}, error)
+	ToLoadBalancerCreateMap() (map[string]interface{}, error)
 }
 
 // CreateOpts is the common options struct used in this package's Create
 // operation.
-type CreateOpts loadbalancerOpts
+type CreateOpts struct {
+	// Optional. Human-readable name for the Loadbalancer. Does not have to be unique.
+	Name string `json:"name,omitempty"`
+	// Optional. Human-readable description for the Loadbalancer.
+	Description string `json:"description,omitempty"`
+	// Required. The network on which to allocate the Loadbalancer's address. A tenant can
+	// only create Loadbalancers on networks authorized by policy (e.g. networks that
+	// belong to them or networks that are shared).
+	VipSubnetID string `json:"vip_subnet_id" required:"true"`
+	// Required for admins. The UUID of the tenant who owns the Loadbalancer.
+	// Only administrative users can specify a tenant UUID other than their own.
+	TenantID string `json:"tenant_id,omitempty"`
+	// Optional. The IP address of the Loadbalancer.
+	VipAddress string `json:"vip_address,omitempty"`
+	// Optional. The administrative state of the Loadbalancer. A valid value is true (UP)
+	// or false (DOWN).
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
+	// Optional. The UUID of a flavor.
+	Flavor string `json:"flavor,omitempty"`
+	// Optional. The name of the provider.
+	Provider string `json:"provider,omitempty"`
+}
 
-// ToLoadbalancerCreateMap casts a CreateOpts struct to a map.
-func (opts CreateOpts) ToLoadbalancerCreateMap() (map[string]interface{}, error) {
-	l := make(map[string]interface{})
-
-	if opts.VipSubnetID != "" {
-		l["vip_subnet_id"] = opts.VipSubnetID
-	} else {
-		return nil, errVipSubnetIDRequried
-	}
-	if opts.AdminStateUp != nil {
-		l["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		l["name"] = opts.Name
-	}
-	if opts.TenantID != "" {
-		l["tenant_id"] = opts.TenantID
-	}
-	if opts.Description != "" {
-		l["description"] = opts.Description
-	}
-	if opts.VipAddress != "" {
-		l["vip_address"] = opts.VipAddress
-	}
-	if opts.Flavor != "" {
-		l["flavor"] = opts.Flavor
-	}
-	if opts.Provider != "" {
-		l["provider"] = opts.Provider
-	}
-
-	return map[string]interface{}{"loadbalancer": l}, nil
+// ToLoadBalancerCreateMap casts a CreateOpts struct to a map.
+func (opts CreateOpts) ToLoadBalancerCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "loadbalancer")
 }
 
 // Create is an operation which provisions a new loadbalancer based on the
@@ -166,25 +105,20 @@
 //
 // Users with an admin role can create loadbalancers on behalf of other tenants by
 // specifying a TenantID attribute different than their own.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToLoadbalancerCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToLoadBalancerCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular Loadbalancer based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -192,57 +126,46 @@
 // extensions decorate or modify the common logic, it is useful for them to
 // satisfy a basic interface in order for them to be used.
 type UpdateOptsBuilder interface {
-	ToLoadbalancerUpdateMap() (map[string]interface{}, error)
+	ToLoadBalancerUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts is the common options struct used in this package's Update
 // operation.
-type UpdateOpts loadbalancerOpts
-
-// ToLoadbalancerUpdateMap casts a UpdateOpts struct to a map.
-func (opts UpdateOpts) ToLoadbalancerUpdateMap() (map[string]interface{}, error) {
-	l := make(map[string]interface{})
-
-	if opts.Name != "" {
-		l["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		l["description"] = opts.Description
-	}
-	if opts.AdminStateUp != nil {
-		l["admin_state_up"] = &opts.AdminStateUp
-	}
-
-	return map[string]interface{}{"loadbalancer": l}, nil
+type UpdateOpts struct {
+	// Optional. Human-readable name for the Loadbalancer. Does not have to be unique.
+	Name string `json:"name,omitempty"`
+	// Optional. Human-readable description for the Loadbalancer.
+	Description string `json:"description,omitempty"`
+	// Optional. The administrative state of the Loadbalancer. A valid value is true (UP)
+	// or false (DOWN).
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
 }
 
-// Update is an operation which modifies the attributes of the specified Loadbalancer.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	var res UpdateResult
+// ToLoadBalancerUpdateMap casts a UpdateOpts struct to a map.
+func (opts UpdateOpts) ToLoadBalancerUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "loadbalancer")
+}
 
-	reqBody, err := opts.ToLoadbalancerUpdateMap()
+// Update is an operation which modifies the attributes of the specified LoadBalancer.
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) {
+	b, err := opts.ToLoadBalancerUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
-
-	return res
+	return
 }
 
-// Delete will permanently delete a particular Loadbalancer based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+// Delete will permanently delete a particular LoadBalancer based on its unique ID.
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
 
-func GetStatuses(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(statusRootURL(c, id), &res.Body, nil)
-	return res
+func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) {
+	_, r.Err = c.Get(statusRootURL(c, id), &r.Body, nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests_test.go
deleted file mode 100644
index 4bc1f1f..0000000
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests_test.go
+++ /dev/null
@@ -1,150 +0,0 @@
-package loadbalancers
-
-import (
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lbaas/loadbalancers", rootURL(fake.ServiceClient()))
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lbaas/loadbalancers/foo", resourceURL(fake.ServiceClient(), "foo"))
-}
-
-func TestListLoadbalancers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleLoadbalancerListSuccessfully(t)
-
-	pages := 0
-	err := List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractLoadbalancers(page)
-		if err != nil {
-			return false, err
-		}
-
-		if len(actual) != 2 {
-			t.Fatalf("Expected 2 loadbalancers, got %d", len(actual))
-		}
-		th.CheckDeepEquals(t, LoadbalancerWeb, actual[0])
-		th.CheckDeepEquals(t, LoadbalancerDb, actual[1])
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-
-	if pages != 1 {
-		t.Errorf("Expected 1 page, saw %d", pages)
-	}
-}
-
-func TestListAllLoadbalancers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleLoadbalancerListSuccessfully(t)
-
-	allPages, err := List(fake.ServiceClient(), ListOpts{}).AllPages()
-	th.AssertNoErr(t, err)
-	actual, err := ExtractLoadbalancers(allPages)
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, LoadbalancerWeb, actual[0])
-	th.CheckDeepEquals(t, LoadbalancerDb, actual[1])
-}
-
-func TestCreateLoadbalancer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleLoadbalancerCreationSuccessfully(t, SingleLoadbalancerBody)
-
-	actual, err := Create(fake.ServiceClient(), CreateOpts{
-		Name:         "db_lb",
-		AdminStateUp: Up,
-		VipSubnetID:  "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
-		VipAddress:   "10.30.176.48",
-		Flavor:       "medium",
-		Provider:     "haproxy",
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	th.CheckDeepEquals(t, LoadbalancerDb, *actual)
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), CreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Name: "foo"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Name: "foo", Description: "bar"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Name: "foo", Description: "bar", VipAddress: "bar"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestGetLoadbalancer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleLoadbalancerGetSuccessfully(t)
-
-	client := fake.ServiceClient()
-	actual, err := Get(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract()
-	if err != nil {
-		t.Fatalf("Unexpected Get error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, LoadbalancerDb, *actual)
-}
-
-func TestGetLoadbalancerStatusesTree(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleLoadbalancerGetStatusesTree(t)
-
-	client := fake.ServiceClient()
-	actual, err := GetStatuses(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractStatuses()
-	if err != nil {
-		t.Fatalf("Unexpected Get error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, LoadbalancerStatusesTree, *(actual.Loadbalancer))
-}
-
-func TestDeleteLoadbalancer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleLoadbalancerDeletionSuccessfully(t)
-
-	res := Delete(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestUpdateLoadbalancer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleLoadbalancerUpdateSuccessfully(t)
-
-	client := fake.ServiceClient()
-	actual, err := Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", UpdateOpts{
-		Name: "NewLoadbalancerName",
-	}).Extract()
-	if err != nil {
-		t.Fatalf("Unexpected Update error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, LoadbalancerUpdated, *actual)
-}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go
index 911c156..168e531 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go
@@ -1,10 +1,9 @@
 package loadbalancers
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // LoadBalancer is the primary load balancing configuration object that specifies
@@ -12,87 +11,69 @@
 // as other details such as the load balancing method to be use, protocol, etc.
 type LoadBalancer struct {
 	// Human-readable description for the Loadbalancer.
-	Description string `mapstructure:"description" json:"description"`
-
+	Description string `json:"description"`
 	// The administrative state of the Loadbalancer. A valid value is true (UP) or false (DOWN).
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
-
+	AdminStateUp bool `json:"admin_state_up"`
 	// Owner of the LoadBalancer. Only an admin user can specify a tenant ID other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
-
+	TenantID string `json:"tenant_id"`
 	// The provisioning status of the LoadBalancer. This value is ACTIVE, PENDING_CREATE or ERROR.
-	ProvisioningStatus string `mapstructure:"provisioning_status" json:"provisioning_status"`
-
+	ProvisioningStatus string `json:"provisioning_status"`
 	// The IP address of the Loadbalancer.
-	VipAddress string `mapstructure:"vip_address" json:"vip_address"`
-
+	VipAddress string `json:"vip_address"`
 	// The UUID of the subnet on which to allocate the virtual IP for the Loadbalancer address.
-	VipSubnetID string `mapstructure:"vip_subnet_id" json:"vip_subnet_id"`
-
+	VipSubnetID string `json:"vip_subnet_id"`
 	// The unique ID for the LoadBalancer.
-	ID string `mapstructure:"id" json:"id"`
-
+	ID string `json:"id"`
 	// The operating status of the LoadBalancer. This value is ONLINE or OFFLINE.
-	OperatingStatus string `mapstructure:"operating_status" json:"operating_status"`
-
+	OperatingStatus string `json:"operating_status"`
 	// Human-readable name for the LoadBalancer. Does not have to be unique.
-	Name string `mapstructure:"name" json:"name"`
-
+	Name string `json:"name"`
 	// The UUID of a flavor if set.
-	Flavor string `mapstructure:"flavor" json:"flavor"`
-
+	Flavor string `json:"flavor"`
 	// The name of the provider.
-	Provider string `mapstructure:"provider" json:"provider"`
-
-	Listeners []listeners.Listener `mapstructure:"listeners" json:"listeners"`
+	Provider  string               `json:"provider"`
+	Listeners []listeners.Listener `json:"listeners"`
 }
 
 type StatusTree struct {
-	Loadbalancer *LoadBalancer `mapstructure:"loadbalancer" json:"loadbalancer"`
+	Loadbalancer *LoadBalancer `json:"loadbalancer"`
 }
 
-// LoadbalancerPage is the page returned by a pager when traversing over a
+// LoadBalancerPage is the page returned by a pager when traversing over a
 // collection of routers.
-type LoadbalancerPage struct {
+type LoadBalancerPage struct {
 	pagination.LinkedPageBase
 }
 
 // NextPageURL is invoked when a paginated collection of routers has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p LoadbalancerPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"loadbalancers_links"`
+func (r LoadBalancerPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"loadbalancers_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
-// IsEmpty checks whether a RouterPage struct is empty.
-func (p LoadbalancerPage) IsEmpty() (bool, error) {
-	is, err := ExtractLoadbalancers(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+// IsEmpty checks whether a LoadBalancerPage struct is empty.
+func (p LoadBalancerPage) IsEmpty() (bool, error) {
+	is, err := ExtractLoadBalancers(p)
+	return len(is) == 0, err
 }
 
-// ExtractLoadbalancers accepts a Page struct, specifically a LoadbalancerPage struct,
+// ExtractLoadBalancers accepts a Page struct, specifically a LoadbalancerPage struct,
 // and extracts the elements into a slice of LoadBalancer structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractLoadbalancers(page pagination.Page) ([]LoadBalancer, error) {
-	var resp struct {
-		LoadBalancers []LoadBalancer `mapstructure:"loadbalancers" json:"loadbalancers"`
+func ExtractLoadBalancers(r pagination.Page) ([]LoadBalancer, error) {
+	var s struct {
+		LoadBalancers []LoadBalancer `json:"loadbalancers"`
 	}
-	err := mapstructure.Decode(page.(LoadbalancerPage).Body, &resp)
-
-	return resp.LoadBalancers, err
+	err := (r.(LoadBalancerPage)).ExtractInto(&s)
+	return s.LoadBalancers, err
 }
 
 type commonResult struct {
@@ -101,28 +82,24 @@
 
 // Extract is a function that accepts a result and extracts a router.
 func (r commonResult) Extract() (*LoadBalancer, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		LoadBalancer *LoadBalancer `json:"loadbalancer"`
 	}
-	var res struct {
-		LoadBalancer *LoadBalancer `mapstructure:"loadbalancer" json:"loadbalancer"`
-	}
-	err := mapstructure.Decode(r.Body, &res)
+	err := r.ExtractInto(&s)
+	return s.LoadBalancer, err
+}
 
-	return res.LoadBalancer, err
+type GetStatusesResult struct {
+	gophercloud.Result
 }
 
 // Extract is a function that accepts a result and extracts a Loadbalancer.
-func (r commonResult) ExtractStatuses() (*StatusTree, error) {
-	if r.Err != nil {
-		return nil, r.Err
+func (r GetStatusesResult) Extract() (*StatusTree, error) {
+	var s struct {
+		Statuses *StatusTree `json:"statuses"`
 	}
-	var res struct {
-		LoadBalancer *StatusTree `mapstructure:"statuses" json:"statuses"`
-	}
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.LoadBalancer, err
+	err := r.ExtractInto(&s)
+	return s.Statuses, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go
new file mode 100644
index 0000000..f882949
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go
@@ -0,0 +1,277 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
+)
+
+// LoadbalancersListBody contains the canned body of a loadbalancer list response.
+const LoadbalancersListBody = `
+{
+	"loadbalancers":[
+	         {
+			"id": "c331058c-6a40-4144-948e-b9fb1df9db4b",
+			"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
+			"name": "web_lb",
+			"description": "lb config for the web tier",
+			"vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154",
+			"vip_address": "10.30.176.47",
+			"flavor": "small",
+			"provider": "haproxy",
+			"admin_state_up": true,
+			"provisioning_status": "ACTIVE",
+			"operating_status": "ONLINE"
+		},
+		{
+			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+			"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
+			"name": "db_lb",
+			"description": "lb config for the db tier",
+			"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+			"vip_address": "10.30.176.48",
+			"flavor": "medium",
+			"provider": "haproxy",
+			"admin_state_up": true,
+			"provisioning_status": "PENDING_CREATE",
+			"operating_status": "OFFLINE"
+		}
+	]
+}
+`
+
+// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer.
+const SingleLoadbalancerBody = `
+{
+	"loadbalancer": {
+		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
+		"name": "db_lb",
+		"description": "lb config for the db tier",
+		"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+		"vip_address": "10.30.176.48",
+		"flavor": "medium",
+		"provider": "haproxy",
+		"admin_state_up": true,
+		"provisioning_status": "PENDING_CREATE",
+		"operating_status": "OFFLINE"
+	}
+}
+`
+
+// PostUpdateLoadbalancerBody is the canned response body of a Update request on an existing loadbalancer.
+const PostUpdateLoadbalancerBody = `
+{
+	"loadbalancer": {
+		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
+		"name": "NewLoadbalancerName",
+		"description": "lb config for the db tier",
+		"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+		"vip_address": "10.30.176.48",
+		"flavor": "medium",
+		"provider": "haproxy",
+		"admin_state_up": true,
+		"provisioning_status": "PENDING_CREATE",
+		"operating_status": "OFFLINE"
+	}
+}
+`
+
+// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer.
+const LoadbalancerStatuesesTree = `
+{
+	"statuses" : {
+		"loadbalancer": {
+			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+			"name": "db_lb",
+			"provisioning_status": "PENDING_UPDATE",
+			"operating_status": "ACTIVE",
+			"listeners": [{
+				"id": "db902c0c-d5ff-4753-b465-668ad9656918",
+				"name": "db",
+				"pools": [{
+					"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
+					"name": "db",
+					"healthmonitor": {
+						"id": "67306cda-815d-4354-9fe4-59e09da9c3c5",
+						"type":"PING"
+					},
+					"members":[{
+						"id": "2a280670-c202-4b0b-a562-34077415aabf",
+						"name": "db",
+						"address": "10.0.2.11",
+						"protocol_port": 80
+					}]
+				}]
+			}]
+		}
+	}
+}
+`
+
+var (
+	LoadbalancerWeb = loadbalancers.LoadBalancer{
+		ID:                 "c331058c-6a40-4144-948e-b9fb1df9db4b",
+		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
+		Name:               "web_lb",
+		Description:        "lb config for the web tier",
+		VipSubnetID:        "8a49c438-848f-467b-9655-ea1548708154",
+		VipAddress:         "10.30.176.47",
+		Flavor:             "small",
+		Provider:           "haproxy",
+		AdminStateUp:       true,
+		ProvisioningStatus: "ACTIVE",
+		OperatingStatus:    "ONLINE",
+	}
+	LoadbalancerDb = loadbalancers.LoadBalancer{
+		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
+		Name:               "db_lb",
+		Description:        "lb config for the db tier",
+		VipSubnetID:        "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+		VipAddress:         "10.30.176.48",
+		Flavor:             "medium",
+		Provider:           "haproxy",
+		AdminStateUp:       true,
+		ProvisioningStatus: "PENDING_CREATE",
+		OperatingStatus:    "OFFLINE",
+	}
+	LoadbalancerUpdated = loadbalancers.LoadBalancer{
+		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
+		Name:               "NewLoadbalancerName",
+		Description:        "lb config for the db tier",
+		VipSubnetID:        "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+		VipAddress:         "10.30.176.48",
+		Flavor:             "medium",
+		Provider:           "haproxy",
+		AdminStateUp:       true,
+		ProvisioningStatus: "PENDING_CREATE",
+		OperatingStatus:    "OFFLINE",
+	}
+	LoadbalancerStatusesTree = loadbalancers.LoadBalancer{
+		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		Name:               "db_lb",
+		ProvisioningStatus: "PENDING_UPDATE",
+		OperatingStatus:    "ACTIVE",
+		Listeners: []listeners.Listener{{
+			ID:   "db902c0c-d5ff-4753-b465-668ad9656918",
+			Name: "db",
+			Pools: []pools.Pool{{
+				ID:   "fad389a3-9a4a-4762-a365-8c7038508b5d",
+				Name: "db",
+				Monitor: monitors.Monitor{
+					ID:   "67306cda-815d-4354-9fe4-59e09da9c3c5",
+					Type: "PING",
+				},
+				Members: []pools.Member{{
+					ID:           "2a280670-c202-4b0b-a562-34077415aabf",
+					Name:         "db",
+					Address:      "10.0.2.11",
+					ProtocolPort: 80,
+				}},
+			}},
+		}},
+	}
+)
+
+// HandleLoadbalancerListSuccessfully sets up the test server to respond to a loadbalancer List request.
+func HandleLoadbalancerListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, LoadbalancersListBody)
+		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
+			fmt.Fprintf(w, `{ "loadbalancers": [] }`)
+		default:
+			t.Fatalf("/v2.0/lbaas/loadbalancers invoked with unexpected marker=[%s]", marker)
+		}
+	})
+}
+
+// HandleLoadbalancerCreationSuccessfully sets up the test server to respond to a loadbalancer creation request
+// with a given response.
+func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"loadbalancer": {
+				"name": "db_lb",
+				"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+				"vip_address": "10.30.176.48",
+				"flavor": "medium",
+				"provider": "haproxy",
+				"admin_state_up": true
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandleLoadbalancerGetSuccessfully sets up the test server to respond to a loadbalancer Get request.
+func HandleLoadbalancerGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleLoadbalancerBody)
+	})
+}
+
+// HandleLoadbalancerGetStatusesTree sets up the test server to respond to a loadbalancer Get statuses tree request.
+func HandleLoadbalancerGetStatusesTree(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/statuses", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, LoadbalancerStatuesesTree)
+	})
+}
+
+// HandleLoadbalancerDeletionSuccessfully sets up the test server to respond to a loadbalancer deletion request.
+func HandleLoadbalancerDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleLoadbalancerUpdateSuccessfully sets up the test server to respond to a loadbalancer Update request.
+func HandleLoadbalancerUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `{
+			"loadbalancer": {
+				"name": "NewLoadbalancerName"
+			}
+		}`)
+
+		fmt.Fprintf(w, PostUpdateLoadbalancerBody)
+	})
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go
new file mode 100644
index 0000000..52da652
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go
@@ -0,0 +1,144 @@
+package testing
+
+import (
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/jrperritt/gophercloud"
+)
+
+func TestListLoadbalancers(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleLoadbalancerListSuccessfully(t)
+
+	pages := 0
+	err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := loadbalancers.ExtractLoadBalancers(page)
+		if err != nil {
+			return false, err
+		}
+
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 loadbalancers, got %d", len(actual))
+		}
+		th.CheckDeepEquals(t, LoadbalancerWeb, actual[0])
+		th.CheckDeepEquals(t, LoadbalancerDb, actual[1])
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
+	}
+}
+
+func TestListAllLoadbalancers(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleLoadbalancerListSuccessfully(t)
+
+	allPages, err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := loadbalancers.ExtractLoadBalancers(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, LoadbalancerWeb, actual[0])
+	th.CheckDeepEquals(t, LoadbalancerDb, actual[1])
+}
+
+func TestCreateLoadbalancer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleLoadbalancerCreationSuccessfully(t, SingleLoadbalancerBody)
+
+	actual, err := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{
+		Name:         "db_lb",
+		AdminStateUp: gophercloud.Enabled,
+		VipSubnetID:  "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+		VipAddress:   "10.30.176.48",
+		Flavor:       "medium",
+		Provider:     "haproxy",
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, LoadbalancerDb, *actual)
+}
+
+func TestRequiredCreateOpts(t *testing.T) {
+	res := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar", VipAddress: "bar"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestGetLoadbalancer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleLoadbalancerGetSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := loadbalancers.Get(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, LoadbalancerDb, *actual)
+}
+
+func TestGetLoadbalancerStatusesTree(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleLoadbalancerGetStatusesTree(t)
+
+	client := fake.ServiceClient()
+	actual, err := loadbalancers.GetStatuses(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, LoadbalancerStatusesTree, *(actual.Loadbalancer))
+}
+
+func TestDeleteLoadbalancer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleLoadbalancerDeletionSuccessfully(t)
+
+	res := loadbalancers.Delete(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestUpdateLoadbalancer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleLoadbalancerUpdateSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := loadbalancers.Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{
+		Name: "NewLoadbalancerName",
+	}).Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Update error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, LoadbalancerUpdated, *actual)
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go
index a307490..73cf5dc 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go
@@ -1,6 +1,6 @@
 package loadbalancers
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	rootPath     = "lbaas"
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/fixtures.go
deleted file mode 100644
index ad0b488..0000000
--- a/openstack/networking/v2/extensions/lbaas_v2/monitors/fixtures.go
+++ /dev/null
@@ -1,216 +0,0 @@
-// +build fixtures
-
-package monitors
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// HealthmonitorsListBody contains the canned body of a healthmonitor list response.
-const HealthmonitorsListBody = `
-{
-	"healthmonitors":[
-		{
-			"admin_state_up":true,
-			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-			"delay":10,
-			"name":"web",
-			"max_retries":1,
-			"timeout":1,
-			"type":"PING",
-			"pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}],
-			"id":"466c8345-28d8-4f84-a246-e04380b0461d"
-		},
-		{
-			"admin_state_up":true,
-			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-			"delay":5,
-			"name":"db",
-			"expected_codes":"200",
-			"max_retries":2,
-			"http_method":"GET",
-			"timeout":2,
-			"url_path":"/",
-			"type":"HTTP",
-			"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
-			"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
-		}
-	]
-}
-`
-
-// SingleHealthmonitorBody is the canned body of a Get request on an existing healthmonitor.
-const SingleHealthmonitorBody = `
-{
-	"healthmonitor": {
-		"admin_state_up":true,
-		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-		"delay":5,
-		"name":"db",
-		"expected_codes":"200",
-		"max_retries":2,
-		"http_method":"GET",
-		"timeout":2,
-		"url_path":"/",
-		"type":"HTTP",
-		"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
-		"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
-	}
-}
-`
-
-// PostUpdateHealthmonitorBody is the canned response body of a Update request on an existing healthmonitor.
-const PostUpdateHealthmonitorBody = `
-{
-	"healthmonitor": {
-		"admin_state_up":true,
-		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-		"delay":3,
-		"name":"NewHealthmonitorName",
-		"expected_codes":"301",
-		"max_retries":10,
-		"http_method":"GET",
-		"timeout":20,
-		"url_path":"/another_check",
-		"type":"HTTP",
-		"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
-		"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
-	}
-}
-`
-
-var (
-	HealthmonitorWeb = Monitor{
-		AdminStateUp: true,
-		Name:         "web",
-		TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
-		Delay:        10,
-		MaxRetries:   1,
-		Timeout:      1,
-		Type:         "PING",
-		ID:           "466c8345-28d8-4f84-a246-e04380b0461d",
-		Pools:        []PoolID{{ID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}},
-	}
-	HealthmonitorDb = Monitor{
-		AdminStateUp:  true,
-		Name:          "db",
-		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
-		Delay:         5,
-		ExpectedCodes: "200",
-		MaxRetries:    2,
-		Timeout:       2,
-		URLPath:       "/",
-		Type:          "HTTP",
-		HTTPMethod:    "GET",
-		ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
-		Pools:         []PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}},
-	}
-	HealthmonitorUpdated = Monitor{
-		AdminStateUp:  true,
-		Name:          "NewHealthmonitorName",
-		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
-		Delay:         3,
-		ExpectedCodes: "301",
-		MaxRetries:    10,
-		Timeout:       20,
-		URLPath:       "/another_check",
-		Type:          "HTTP",
-		HTTPMethod:    "GET",
-		ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
-		Pools:         []PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}},
-	}
-)
-
-// HandleHealthmonitorListSuccessfully sets up the test server to respond to a healthmonitor List request.
-func HandleHealthmonitorListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, HealthmonitorsListBody)
-		case "556c8345-28d8-4f84-a246-e04380b0461d":
-			fmt.Fprintf(w, `{ "healthmonitors": [] }`)
-		default:
-			t.Fatalf("/v2.0/lbaas/healthmonitors invoked with unexpected marker=[%s]", marker)
-		}
-	})
-}
-
-// HandleHealthmonitorCreationSuccessfully sets up the test server to respond to a healthmonitor creation request
-// with a given response.
-func HandleHealthmonitorCreationSuccessfully(t *testing.T, response string) {
-	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{
-			"healthmonitor": {
-				"type":"HTTP",
-				"pool_id":"84f1b61f-58c4-45bf-a8a9-2dafb9e5214d",
-				"tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
-				"delay":20,
-				"name":"db",
-				"timeout":10,
-				"max_retries":5,
-				"url_path":"/check",
-				"expected_codes":"200-299"
-			}
-		}`)
-
-		w.WriteHeader(http.StatusAccepted)
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, response)
-	})
-}
-
-// HandleHealthmonitorGetSuccessfully sets up the test server to respond to a healthmonitor Get request.
-func HandleHealthmonitorGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		fmt.Fprintf(w, SingleHealthmonitorBody)
-	})
-}
-
-// HandleHealthmonitorDeletionSuccessfully sets up the test server to respond to a healthmonitor deletion request.
-func HandleHealthmonitorDeletionSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleHealthmonitorUpdateSuccessfully sets up the test server to respond to a healthmonitor Update request.
-func HandleHealthmonitorUpdateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestJSONRequest(t, r, `{
-			"healthmonitor": {
-				"name": "NewHealthmonitorName",
-				"delay": 3,
-				"timeout": 20,
-				"max_retries": 10,
-				"url_path": "/another_check",
-				"expected_codes": "301"
-			}
-		}`)
-
-		fmt.Fprintf(w, PostUpdateHealthmonitorBody)
-	})
-}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go
index 92e7e83..1e776bf 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go
@@ -2,51 +2,11 @@
 
 import (
 	"fmt"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
-type monitorOpts struct {
-	// Required. The Pool to Monitor.
-	PoolID string
-
-	// Optional. The Name of the Monitor.
-	Name string
-
-	// Required for admins. Indicates the owner of the Loadbalancer.
-	TenantID string
-
-	// Required. The type of probe, which is PING, TCP, HTTP, or HTTPS, that is
-	// sent by the load balancer to verify the member state.
-	Type string
-
-	// Required. The time, in seconds, between sending probes to members.
-	Delay int
-
-	// Required. Maximum number of seconds for a Monitor to wait for a ping reply
-	// before it times out. The value must be less than the delay value.
-	Timeout int
-
-	// Required. Number of permissible ping failures before changing the member's
-	// status to INACTIVE. Must be a number between 1 and 10.
-	MaxRetries int
-
-	// Required for HTTP(S) types. URI path that will be accessed if Monitor type
-	// is HTTP or HTTPS.
-	URLPath string
-
-	// Required for HTTP(S) types. The HTTP method used for requests by the
-	// Monitor. If this attribute is not specified, it defaults to "GET".
-	HTTPMethod string
-
-	// Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S)
-	// Monitor. You can either specify a single status like "200", or a range
-	// like "200-202".
-	ExpectedCodes string
-
-	AdminStateUp *bool
-}
-
 // ListOptsBuilder allows extensions to add additional parameters to the
 // List request.
 type ListOptsBuilder interface {
@@ -102,7 +62,6 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return MonitorPage{pagination.LinkedPageBase{PageResult: r}}
 	})
@@ -117,14 +76,7 @@
 )
 
 var (
-	errPoolIDRequired        = fmt.Errorf("PoolID to monitor is required")
-	errValidTypeRequired     = fmt.Errorf("A valid Type is required. Supported values are PING, TCP, HTTP and HTTPS")
-	errDelayRequired         = fmt.Errorf("Delay is required")
-	errTimeoutRequired       = fmt.Errorf("Timeout is required")
-	errMaxRetriesRequired    = fmt.Errorf("MaxRetries is required")
-	errURLPathRequired       = fmt.Errorf("URL path is required")
-	errExpectedCodesRequired = fmt.Errorf("ExpectedCodes is required")
-	errDelayMustGETimeout    = fmt.Errorf("Delay must be greater than or equal to timeout")
+	errDelayMustGETimeout = fmt.Errorf("Delay must be greater than or equal to timeout")
 )
 
 // CreateOptsBuilder is the interface options structs have to satisfy in order
@@ -137,64 +89,57 @@
 
 // CreateOpts is the common options struct used in this package's Create
 // operation.
-type CreateOpts monitorOpts
+type CreateOpts struct {
+	// Required. The Pool to Monitor.
+	PoolID string `json:"pool_id" required:"true"`
+	// Required. The type of probe, which is PING, TCP, HTTP, or HTTPS, that is
+	// sent by the load balancer to verify the member state.
+	Type string `json:"type" required:"true"`
+	// Required. The time, in seconds, between sending probes to members.
+	Delay int `json:"delay" required:"true"`
+	// Required. Maximum number of seconds for a Monitor to wait for a ping reply
+	// before it times out. The value must be less than the delay value.
+	Timeout int `json:"timeout" required:"true"`
+	// Required. Number of permissible ping failures before changing the member's
+	// status to INACTIVE. Must be a number between 1 and 10.
+	MaxRetries int `json:"max_retries" required:"true"`
+	// Required for HTTP(S) types. URI path that will be accessed if Monitor type
+	// is HTTP or HTTPS.
+	URLPath string `json:"url_path,omitempty"`
+	// Required for HTTP(S) types. The HTTP method used for requests by the
+	// Monitor. If this attribute is not specified, it defaults to "GET".
+	HTTPMethod string `json:"http_method,omitempty"`
+	// Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S)
+	// Monitor. You can either specify a single status like "200", or a range
+	// like "200-202".
+	ExpectedCodes string `json:"expected_codes,omitempty"`
+	// Indicates the owner of the Loadbalancer. Required for admins.
+	TenantID string `json:"tenant_id,omitempty"`
+	// Optional. The Name of the Monitor.
+	Name         string `json:"name,omitempty"`
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
+}
 
 // ToMonitorCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToMonitorCreateMap() (map[string]interface{}, error) {
-	l := make(map[string]interface{})
-	allowed := map[string]bool{TypeHTTP: true, TypeHTTPS: true, TypeTCP: true, TypePING: true}
-
-	if allowed[opts.Type] {
-		l["type"] = opts.Type
-	} else {
-		return nil, errValidTypeRequired
-	}
-	if opts.Type == TypeHTTP || opts.Type == TypeHTTPS {
-		if opts.URLPath != "" {
-			l["url_path"] = opts.URLPath
-		} else {
-			return nil, errURLPathRequired
-		}
-		if opts.ExpectedCodes != "" {
-			l["expected_codes"] = opts.ExpectedCodes
-		} else {
-			return nil, errExpectedCodesRequired
-		}
-	}
-	if opts.PoolID != "" {
-		l["pool_id"] = opts.PoolID
-	} else {
-		return nil, errPoolIDRequired
-	}
-	if opts.Delay != 0 {
-		l["delay"] = opts.Delay
-	} else {
-		return nil, errDelayRequired
-	}
-	if opts.Timeout != 0 {
-		l["timeout"] = opts.Timeout
-	} else {
-		return nil, errMaxRetriesRequired
-	}
-	if opts.MaxRetries != 0 {
-		l["max_retries"] = opts.MaxRetries
-	} else {
-		return nil, errMaxRetriesRequired
-	}
-	if opts.AdminStateUp != nil {
-		l["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		l["name"] = opts.Name
-	}
-	if opts.TenantID != "" {
-		l["tenant_id"] = opts.TenantID
-	}
-	if opts.HTTPMethod != "" {
-		l["http_method"] = opts.HTTPMethod
+	b, err := gophercloud.BuildRequestBody(opts, "healthmonitor")
+	if err != nil {
+		return nil, err
 	}
 
-	return map[string]interface{}{"healthmonitor": l}, nil
+	switch opts.Type {
+	case TypeHTTP, TypeHTTPS:
+		switch opts.URLPath {
+		case "":
+			return nil, fmt.Errorf("URLPath must be provided for HTTP and HTTPS")
+		}
+		switch opts.ExpectedCodes {
+		case "":
+			return nil, fmt.Errorf("ExpectedCodes must be provided for HTTP and HTTPS")
+		}
+	}
+
+	return b, nil
 }
 
 /*
@@ -212,26 +157,20 @@
  CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3,
  HttpMethod: "HEAD", ExpectedCodes: "200", PoolID: "2c946bfc-1804-43ab-a2ff-58f6a762b505"}
 */
-
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToMonitorCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToMonitorCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular Health Monitor based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -244,61 +183,51 @@
 
 // UpdateOpts is the common options struct used in this package's Update
 // operation.
-type UpdateOpts monitorOpts
+type UpdateOpts struct {
+	// Required. The time, in seconds, between sending probes to members.
+	Delay int `json:"delay,omitempty"`
+	// Required. Maximum number of seconds for a Monitor to wait for a ping reply
+	// before it times out. The value must be less than the delay value.
+	Timeout int `json:"timeout,omitempty"`
+	// Required. Number of permissible ping failures before changing the member's
+	// status to INACTIVE. Must be a number between 1 and 10.
+	MaxRetries int `json:"max_retries,omitempty"`
+	// Required for HTTP(S) types. URI path that will be accessed if Monitor type
+	// is HTTP or HTTPS.
+	URLPath string `json:"url_path,omitempty"`
+	// Required for HTTP(S) types. The HTTP method used for requests by the
+	// Monitor. If this attribute is not specified, it defaults to "GET".
+	HTTPMethod string `json:"http_method,omitempty"`
+	// Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S)
+	// Monitor. You can either specify a single status like "200", or a range
+	// like "200-202".
+	ExpectedCodes string `json:"expected_codes,omitempty"`
+	// Optional. The Name of the Monitor.
+	Name         string `json:"name,omitempty"`
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
+}
 
 // ToMonitorUpdateMap casts a UpdateOpts struct to a map.
 func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]interface{}, error) {
-	l := make(map[string]interface{})
-
-	if opts.URLPath != "" {
-		l["url_path"] = opts.URLPath
-	}
-	if opts.ExpectedCodes != "" {
-		l["expected_codes"] = opts.ExpectedCodes
-	}
-	if opts.Delay != 0 {
-		l["delay"] = opts.Delay
-	}
-	if opts.Timeout != 0 {
-		l["timeout"] = opts.Timeout
-	}
-	if opts.MaxRetries != 0 {
-		l["max_retries"] = opts.MaxRetries
-	}
-	if opts.AdminStateUp != nil {
-		l["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		l["name"] = opts.Name
-	}
-	if opts.HTTPMethod != "" {
-		l["http_method"] = opts.HTTPMethod
-	}
-
-	return map[string]interface{}{"healthmonitor": l}, nil
+	return gophercloud.BuildRequestBody(opts, "healthmonitor")
 }
 
 // Update is an operation which modifies the attributes of the specified Monitor.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToMonitorUpdateMap()
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToMonitorUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
 
-	// Send request to API
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
-
-	return res
+	return
 }
 
 // Delete will permanently delete a particular Monitor based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests_test.go
deleted file mode 100644
index 202a182..0000000
--- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests_test.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package monitors
-
-import (
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lbaas/healthmonitors", rootURL(fake.ServiceClient()))
-}
-
-func TestListHealthmonitors(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleHealthmonitorListSuccessfully(t)
-
-	pages := 0
-	err := List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractMonitors(page)
-		if err != nil {
-			return false, err
-		}
-
-		if len(actual) != 2 {
-			t.Fatalf("Expected 2 healthmonitors, got %d", len(actual))
-		}
-		th.CheckDeepEquals(t, HealthmonitorWeb, actual[0])
-		th.CheckDeepEquals(t, HealthmonitorDb, actual[1])
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-
-	if pages != 1 {
-		t.Errorf("Expected 1 page, saw %d", pages)
-	}
-}
-
-func TestListAllHealthmonitors(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleHealthmonitorListSuccessfully(t)
-
-	allPages, err := List(fake.ServiceClient(), ListOpts{}).AllPages()
-	th.AssertNoErr(t, err)
-	actual, err := ExtractMonitors(allPages)
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, HealthmonitorWeb, actual[0])
-	th.CheckDeepEquals(t, HealthmonitorDb, actual[1])
-}
-
-func TestCreateHealthmonitor(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleHealthmonitorCreationSuccessfully(t, SingleHealthmonitorBody)
-
-	actual, err := Create(fake.ServiceClient(), CreateOpts{
-		Type:          "HTTP",
-		Name:          "db",
-		PoolID:        "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d",
-		TenantID:      "453105b9-1754-413f-aab1-55f1af620750",
-		Delay:         20,
-		Timeout:       10,
-		MaxRetries:    5,
-		URLPath:       "/check",
-		ExpectedCodes: "200-299",
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	th.CheckDeepEquals(t, HealthmonitorDb, *actual)
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), CreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Type: TypeHTTP})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestGetHealthmonitor(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleHealthmonitorGetSuccessfully(t)
-
-	client := fake.ServiceClient()
-	actual, err := Get(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7").Extract()
-	if err != nil {
-		t.Fatalf("Unexpected Get error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, HealthmonitorDb, *actual)
-}
-
-func TestDeleteHealthmonitor(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleHealthmonitorDeletionSuccessfully(t)
-
-	res := Delete(fake.ServiceClient(), "5d4b5228-33b0-4e60-b225-9b727c1a20e7")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestUpdateHealthmonitor(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleHealthmonitorUpdateSuccessfully(t)
-
-	client := fake.ServiceClient()
-	actual, err := Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", UpdateOpts{
-		Name:          "NewHealthmonitorName",
-		Delay:         3,
-		Timeout:       20,
-		MaxRetries:    10,
-		URLPath:       "/another_check",
-		ExpectedCodes: "301",
-	}).Extract()
-	if err != nil {
-		t.Fatalf("Unexpected Update error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, HealthmonitorUpdated, *actual)
-}
-
-func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) {
-	_, err := Create(fake.ServiceClient(), CreateOpts{
-		Type:          "HTTP",
-		PoolID:        "d459f7d8-c6ee-439d-8713-d3fc08aeed8d",
-		Delay:         1,
-		Timeout:       10,
-		MaxRetries:    5,
-		URLPath:       "/check",
-		ExpectedCodes: "200-299",
-	}).Extract()
-
-	if err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-
-	_, err = Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", UpdateOpts{
-		Delay:   1,
-		Timeout: 10,
-	}).Extract()
-
-	if err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go
index da363c0..05dcf47 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go
@@ -1,13 +1,12 @@
 package monitors
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 type PoolID struct {
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 }
 
 // Monitor represents a load balancer health monitor. A health monitor is used
@@ -27,52 +26,49 @@
 // must declare the member to be healthy for it to stay ACTIVE.
 type Monitor struct {
 	// The unique ID for the Monitor.
-	ID string
+	ID string `json:"id"`
 
 	// The Name of the Monitor.
-	Name string
+	Name string `json:"name"`
 
 	// Only an administrative user can specify a tenant ID
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// The type of probe sent by the load balancer to verify the member state,
 	// which is PING, TCP, HTTP, or HTTPS.
-	Type string
+	Type string `json:"type"`
 
 	// The time, in seconds, between sending probes to members.
-	Delay int
+	Delay int `json:"delay"`
 
 	// The maximum number of seconds for a monitor to wait for a connection to be
 	// established before it times out. This value must be less than the delay value.
-	Timeout int
+	Timeout int `json:"timeout"`
 
 	// Number of allowed connection failures before changing the status of the
 	// member to INACTIVE. A valid value is from 1 to 10.
-	MaxRetries int `json:"max_retries" mapstructure:"max_retries"`
+	MaxRetries int `json:"max_retries"`
 
 	// The HTTP method that the monitor uses for requests.
-	HTTPMethod string `json:"http_method" mapstructure:"http_method"`
+	HTTPMethod string `json:"http_method"`
 
 	// The HTTP path of the request sent by the monitor to test the health of a
 	// member. Must be a string beginning with a forward slash (/).
-	URLPath string `json:"url_path" mapstructure:"url_path"`
+	URLPath string `json:"url_path" `
 
 	// Expected HTTP codes for a passing HTTP(S) monitor.
-	ExpectedCodes string `json:"expected_codes" mapstructure:"expected_codes"`
+	ExpectedCodes string `json:"expected_codes"`
 
 	// The administrative state of the health monitor, which is up (true) or down (false).
-	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// The status of the health monitor. Indicates whether the health monitor is
 	// operational.
-	Status string
+	Status string `json:"status"`
 
 	// List of pools that are associated with the health monitor.
-	Pools []PoolID `mapstructure:"pools" json:"pools"`
-}
-
-type Pool struct {
+	Pools []PoolID `json:"pools"`
 }
 
 // MonitorPage is the page returned by a pager when traversing over a
@@ -84,40 +80,34 @@
 // NextPageURL is invoked when a paginated collection of monitors has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p MonitorPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"healthmonitors_links"`
+func (r MonitorPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"healthmonitors_links"`
 	}
 
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
 
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
-// IsEmpty checks whether a PoolPage struct is empty.
-func (p MonitorPage) IsEmpty() (bool, error) {
-	is, err := ExtractMonitors(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+// IsEmpty checks whether a MonitorPage struct is empty.
+func (r MonitorPage) IsEmpty() (bool, error) {
+	is, err := ExtractMonitors(r)
+	return len(is) == 0, err
 }
 
 // ExtractMonitors accepts a Page struct, specifically a MonitorPage struct,
 // and extracts the elements into a slice of Monitor structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractMonitors(page pagination.Page) ([]Monitor, error) {
-	var resp struct {
-		Monitors []Monitor `mapstructure:"healthmonitors" json:"healthmonitors"`
+func ExtractMonitors(r pagination.Page) ([]Monitor, error) {
+	var s struct {
+		Monitors []Monitor `json:"healthmonitors"`
 	}
-
-	err := mapstructure.Decode(page.(MonitorPage).Body, &resp)
-
-	return resp.Monitors, err
+	err := (r.(MonitorPage)).ExtractInto(&s)
+	return s.Monitors, err
 }
 
 type commonResult struct {
@@ -126,17 +116,11 @@
 
 // Extract is a function that accepts a result and extracts a monitor.
 func (r commonResult) Extract() (*Monitor, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Monitor *Monitor `json:"healthmonitor"`
 	}
-
-	var res struct {
-		Monitor *Monitor `json:"healthmonitor" mapstructure:"healthmonitor"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Monitor, err
+	err := r.ExtractInto(&s)
+	return s.Monitor, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures.go
new file mode 100644
index 0000000..6d3eb01
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures.go
@@ -0,0 +1,215 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// HealthmonitorsListBody contains the canned body of a healthmonitor list response.
+const HealthmonitorsListBody = `
+{
+	"healthmonitors":[
+		{
+			"admin_state_up":true,
+			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+			"delay":10,
+			"name":"web",
+			"max_retries":1,
+			"timeout":1,
+			"type":"PING",
+			"pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}],
+			"id":"466c8345-28d8-4f84-a246-e04380b0461d"
+		},
+		{
+			"admin_state_up":true,
+			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+			"delay":5,
+			"name":"db",
+			"expected_codes":"200",
+			"max_retries":2,
+			"http_method":"GET",
+			"timeout":2,
+			"url_path":"/",
+			"type":"HTTP",
+			"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
+			"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
+		}
+	]
+}
+`
+
+// SingleHealthmonitorBody is the canned body of a Get request on an existing healthmonitor.
+const SingleHealthmonitorBody = `
+{
+	"healthmonitor": {
+		"admin_state_up":true,
+		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+		"delay":5,
+		"name":"db",
+		"expected_codes":"200",
+		"max_retries":2,
+		"http_method":"GET",
+		"timeout":2,
+		"url_path":"/",
+		"type":"HTTP",
+		"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
+		"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
+	}
+}
+`
+
+// PostUpdateHealthmonitorBody is the canned response body of a Update request on an existing healthmonitor.
+const PostUpdateHealthmonitorBody = `
+{
+	"healthmonitor": {
+		"admin_state_up":true,
+		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+		"delay":3,
+		"name":"NewHealthmonitorName",
+		"expected_codes":"301",
+		"max_retries":10,
+		"http_method":"GET",
+		"timeout":20,
+		"url_path":"/another_check",
+		"type":"HTTP",
+		"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
+		"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
+	}
+}
+`
+
+var (
+	HealthmonitorWeb = monitors.Monitor{
+		AdminStateUp: true,
+		Name:         "web",
+		TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
+		Delay:        10,
+		MaxRetries:   1,
+		Timeout:      1,
+		Type:         "PING",
+		ID:           "466c8345-28d8-4f84-a246-e04380b0461d",
+		Pools:        []monitors.PoolID{{ID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}},
+	}
+	HealthmonitorDb = monitors.Monitor{
+		AdminStateUp:  true,
+		Name:          "db",
+		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+		Delay:         5,
+		ExpectedCodes: "200",
+		MaxRetries:    2,
+		Timeout:       2,
+		URLPath:       "/",
+		Type:          "HTTP",
+		HTTPMethod:    "GET",
+		ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
+		Pools:         []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}},
+	}
+	HealthmonitorUpdated = monitors.Monitor{
+		AdminStateUp:  true,
+		Name:          "NewHealthmonitorName",
+		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+		Delay:         3,
+		ExpectedCodes: "301",
+		MaxRetries:    10,
+		Timeout:       20,
+		URLPath:       "/another_check",
+		Type:          "HTTP",
+		HTTPMethod:    "GET",
+		ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
+		Pools:         []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}},
+	}
+)
+
+// HandleHealthmonitorListSuccessfully sets up the test server to respond to a healthmonitor List request.
+func HandleHealthmonitorListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, HealthmonitorsListBody)
+		case "556c8345-28d8-4f84-a246-e04380b0461d":
+			fmt.Fprintf(w, `{ "healthmonitors": [] }`)
+		default:
+			t.Fatalf("/v2.0/lbaas/healthmonitors invoked with unexpected marker=[%s]", marker)
+		}
+	})
+}
+
+// HandleHealthmonitorCreationSuccessfully sets up the test server to respond to a healthmonitor creation request
+// with a given response.
+func HandleHealthmonitorCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"healthmonitor": {
+				"type":"HTTP",
+				"pool_id":"84f1b61f-58c4-45bf-a8a9-2dafb9e5214d",
+				"tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
+				"delay":20,
+				"name":"db",
+				"timeout":10,
+				"max_retries":5,
+				"url_path":"/check",
+				"expected_codes":"200-299"
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandleHealthmonitorGetSuccessfully sets up the test server to respond to a healthmonitor Get request.
+func HandleHealthmonitorGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleHealthmonitorBody)
+	})
+}
+
+// HandleHealthmonitorDeletionSuccessfully sets up the test server to respond to a healthmonitor deletion request.
+func HandleHealthmonitorDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleHealthmonitorUpdateSuccessfully sets up the test server to respond to a healthmonitor Update request.
+func HandleHealthmonitorUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `{
+			"healthmonitor": {
+				"name": "NewHealthmonitorName",
+				"delay": 3,
+				"timeout": 20,
+				"max_retries": 10,
+				"url_path": "/another_check",
+				"expected_codes": "301"
+			}
+		}`)
+
+		fmt.Fprintf(w, PostUpdateHealthmonitorBody)
+	})
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go
new file mode 100644
index 0000000..743d9c1
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go
@@ -0,0 +1,154 @@
+package testing
+
+import (
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestListHealthmonitors(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleHealthmonitorListSuccessfully(t)
+
+	pages := 0
+	err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := monitors.ExtractMonitors(page)
+		if err != nil {
+			return false, err
+		}
+
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 healthmonitors, got %d", len(actual))
+		}
+		th.CheckDeepEquals(t, HealthmonitorWeb, actual[0])
+		th.CheckDeepEquals(t, HealthmonitorDb, actual[1])
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
+	}
+}
+
+func TestListAllHealthmonitors(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleHealthmonitorListSuccessfully(t)
+
+	allPages, err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := monitors.ExtractMonitors(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, HealthmonitorWeb, actual[0])
+	th.CheckDeepEquals(t, HealthmonitorDb, actual[1])
+}
+
+func TestCreateHealthmonitor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleHealthmonitorCreationSuccessfully(t, SingleHealthmonitorBody)
+
+	actual, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{
+		Type:          "HTTP",
+		Name:          "db",
+		PoolID:        "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d",
+		TenantID:      "453105b9-1754-413f-aab1-55f1af620750",
+		Delay:         20,
+		Timeout:       10,
+		MaxRetries:    5,
+		URLPath:       "/check",
+		ExpectedCodes: "200-299",
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, HealthmonitorDb, *actual)
+}
+
+func TestRequiredCreateOpts(t *testing.T) {
+	res := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = monitors.Create(fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestGetHealthmonitor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleHealthmonitorGetSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := monitors.Get(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, HealthmonitorDb, *actual)
+}
+
+func TestDeleteHealthmonitor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleHealthmonitorDeletionSuccessfully(t)
+
+	res := monitors.Delete(fake.ServiceClient(), "5d4b5228-33b0-4e60-b225-9b727c1a20e7")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestUpdateHealthmonitor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleHealthmonitorUpdateSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{
+		Name:          "NewHealthmonitorName",
+		Delay:         3,
+		Timeout:       20,
+		MaxRetries:    10,
+		URLPath:       "/another_check",
+		ExpectedCodes: "301",
+	}).Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Update error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, HealthmonitorUpdated, *actual)
+}
+
+func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) {
+	_, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{
+		Type:          "HTTP",
+		PoolID:        "d459f7d8-c6ee-439d-8713-d3fc08aeed8d",
+		Delay:         1,
+		Timeout:       10,
+		MaxRetries:    5,
+		URLPath:       "/check",
+		ExpectedCodes: "200-299",
+	}).Extract()
+
+	if err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+
+	_, err = monitors.Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{
+		Delay:   1,
+		Timeout: 10,
+	}).Extract()
+
+	if err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go
index a3abba4..a222e52 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go
@@ -1,6 +1,6 @@
 package monitors
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	rootPath     = "lbaas"
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/pools/fixtures.go
deleted file mode 100644
index 8e6edc5..0000000
--- a/openstack/networking/v2/extensions/lbaas_v2/pools/fixtures.go
+++ /dev/null
@@ -1,389 +0,0 @@
-// +build fixtures
-
-package pools
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// PoolsListBody contains the canned body of a pool list response.
-const PoolsListBody = `
-{
-	"pools":[
-	         {
-			"lb_algorithm":"ROUND_ROBIN",
-			"protocol":"HTTP",
-			"description":"",
-			"healthmonitor_id": "466c8345-28d8-4f84-a246-e04380b0461d",
-			"members":[{"id": "53306cda-815d-4354-9fe4-59e09da9c3c5"}],
-			"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
-			"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
-			"id":"72741b06-df4d-4715-b142-276b6bce75ab",
-			"name":"web",
-			"admin_state_up":true,
-			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-			"provider": "haproxy"
-		},
-		{
-			"lb_algorithm":"LEAST_CONNECTION",
-			"protocol":"HTTP",
-			"description":"",
-			"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
-			"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
-			"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
-			"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
-			"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
-			"name":"db",
-			"admin_state_up":true,
-			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-			"provider": "haproxy"
-		}
-	]
-}
-`
-
-// SinglePoolBody is the canned body of a Get request on an existing pool.
-const SinglePoolBody = `
-{
-	"pool": {
-		"lb_algorithm":"LEAST_CONNECTION",
-		"protocol":"HTTP",
-		"description":"",
-		"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
-		"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
-		"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
-		"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
-		"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
-		"name":"db",
-		"admin_state_up":true,
-		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-		"provider": "haproxy"
-	}
-}
-`
-
-// PostUpdatePoolBody is the canned response body of a Update request on an existing pool.
-const PostUpdatePoolBody = `
-{
-	"pool": {
-		"lb_algorithm":"LEAST_CONNECTION",
-		"protocol":"HTTP",
-		"description":"",
-		"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
-		"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
-		"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
-		"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
-		"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
-		"name":"db",
-		"admin_state_up":true,
-		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-		"provider": "haproxy"
-	}
-}
-`
-
-var (
-	PoolWeb = Pool{
-		LBMethod:      "ROUND_ROBIN",
-		Protocol:      "HTTP",
-		Description:   "",
-		MonitorID:     "466c8345-28d8-4f84-a246-e04380b0461d",
-		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
-		AdminStateUp:  true,
-		Name:          "web",
-		Members:       []Member{{ID: "53306cda-815d-4354-9fe4-59e09da9c3c5"}},
-		ID:            "72741b06-df4d-4715-b142-276b6bce75ab",
-		Loadbalancers: []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
-		Listeners:     []ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
-		Provider:      "haproxy",
-	}
-	PoolDb = Pool{
-		LBMethod:      "LEAST_CONNECTION",
-		Protocol:      "HTTP",
-		Description:   "",
-		MonitorID:     "5f6c8345-28d8-4f84-a246-e04380b0461d",
-		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
-		AdminStateUp:  true,
-		Name:          "db",
-		Members:       []Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}},
-		ID:            "c3741b06-df4d-4715-b142-276b6bce75ab",
-		Loadbalancers: []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
-		Listeners:     []ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
-		Provider:      "haproxy",
-	}
-	PoolUpdated = Pool{
-		LBMethod:      "LEAST_CONNECTION",
-		Protocol:      "HTTP",
-		Description:   "",
-		MonitorID:     "5f6c8345-28d8-4f84-a246-e04380b0461d",
-		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
-		AdminStateUp:  true,
-		Name:          "db",
-		Members:       []Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}},
-		ID:            "c3741b06-df4d-4715-b142-276b6bce75ab",
-		Loadbalancers: []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
-		Listeners:     []ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
-		Provider:      "haproxy",
-	}
-)
-
-// HandlePoolListSuccessfully sets up the test server to respond to a pool List request.
-func HandlePoolListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, PoolsListBody)
-		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
-			fmt.Fprintf(w, `{ "pools": [] }`)
-		default:
-			t.Fatalf("/v2.0/lbaas/pools invoked with unexpected marker=[%s]", marker)
-		}
-	})
-}
-
-// HandlePoolCreationSuccessfully sets up the test server to respond to a pool creation request
-// with a given response.
-func HandlePoolCreationSuccessfully(t *testing.T, response string) {
-	th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{
-			"pool": {
-			        "lb_algorithm": "ROUND_ROBIN",
-			        "protocol": "HTTP",
-			        "name": "Example pool",
-			        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-			        "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab"
-			}
-		}`)
-
-		w.WriteHeader(http.StatusAccepted)
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, response)
-	})
-}
-
-// HandlePoolGetSuccessfully sets up the test server to respond to a pool Get request.
-func HandlePoolGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		fmt.Fprintf(w, SinglePoolBody)
-	})
-}
-
-// HandlePoolDeletionSuccessfully sets up the test server to respond to a pool deletion request.
-func HandlePoolDeletionSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandlePoolUpdateSuccessfully sets up the test server to respond to a pool Update request.
-func HandlePoolUpdateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestJSONRequest(t, r, `{
-			"pool": {
-				"name": "NewPoolName",
-                                "lb_algorithm": "LEAST_CONNECTIONS"
-			}
-		}`)
-
-		fmt.Fprintf(w, PostUpdatePoolBody)
-	})
-}
-
-// MembersListBody contains the canned body of a member list response.
-const MembersListBody = `
-{
-	"members":[
-		{
-			"id": "2a280670-c202-4b0b-a562-34077415aabf",
-			"address": "10.0.2.10",
-			"weight": 5,
-			"name": "web",
-			"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-			"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-			"admin_state_up":true,
-			"protocol_port": 80
-		},
-		{
-			"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
-			"address": "10.0.2.11",
-			"weight": 10,
-			"name": "db",
-			"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-			"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-			"admin_state_up":false,
-			"protocol_port": 80
-		}
-	]
-}
-`
-
-// SingleMemberBody is the canned body of a Get request on an existing member.
-const SingleMemberBody = `
-{
-	"member": {
-		"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
-		"address": "10.0.2.11",
-		"weight": 10,
-		"name": "db",
-		"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-		"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-		"admin_state_up":false,
-		"protocol_port": 80
-	}
-}
-`
-
-// PostUpdateMemberBody is the canned response body of a Update request on an existing member.
-const PostUpdateMemberBody = `
-{
-	"member": {
-		"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
-		"address": "10.0.2.11",
-		"weight": 10,
-		"name": "db",
-		"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-		"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-		"admin_state_up":false,
-		"protocol_port": 80
-	}
-}
-`
-
-var (
-	MemberWeb = Member{
-		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
-		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
-		AdminStateUp: true,
-		Name:         "web",
-		ID:           "2a280670-c202-4b0b-a562-34077415aabf",
-		Address:      "10.0.2.10",
-		Weight:       5,
-		ProtocolPort: 80,
-	}
-	MemberDb = Member{
-		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
-		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
-		AdminStateUp: false,
-		Name:         "db",
-		ID:           "fad389a3-9a4a-4762-a365-8c7038508b5d",
-		Address:      "10.0.2.11",
-		Weight:       10,
-		ProtocolPort: 80,
-	}
-	MemberUpdated = Member{
-		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
-		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
-		AdminStateUp: false,
-		Name:         "db",
-		ID:           "fad389a3-9a4a-4762-a365-8c7038508b5d",
-		Address:      "10.0.2.11",
-		Weight:       10,
-		ProtocolPort: 80,
-	}
-)
-
-// HandleMemberListSuccessfully sets up the test server to respond to a member List request.
-func HandleMemberListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, MembersListBody)
-		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
-			fmt.Fprintf(w, `{ "members": [] }`)
-		default:
-			t.Fatalf("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members invoked with unexpected marker=[%s]", marker)
-		}
-	})
-}
-
-// HandleMemberCreationSuccessfully sets up the test server to respond to a member creation request
-// with a given response.
-func HandleMemberCreationSuccessfully(t *testing.T, response string) {
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{
-			"member": {
-			        "address": "10.0.2.11",
-			        "weight": 10,
-			        "name": "db",
-			        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-			        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-			        "protocol_port": 80
-			}
-		}`)
-
-		w.WriteHeader(http.StatusAccepted)
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, response)
-	})
-}
-
-// HandleMemberGetSuccessfully sets up the test server to respond to a member Get request.
-func HandleMemberGetSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		fmt.Fprintf(w, SingleMemberBody)
-	})
-}
-
-// HandleMemberDeletionSuccessfully sets up the test server to respond to a member deletion request.
-func HandleMemberDeletionSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleMemberUpdateSuccessfully sets up the test server to respond to a member Update request.
-func HandleMemberUpdateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestJSONRequest(t, r, `{
-			"member": {
-				"name": "newMemberName",
-                                "weight": 4
-			}
-		}`)
-
-		fmt.Fprintf(w, PostUpdateMemberBody)
-	})
-}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go
index 706ce96..093df6a 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go
@@ -1,58 +1,8 @@
 package pools
 
 import (
-	"fmt"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-type poolOpts struct {
-	// Only required if the caller has an admin role and wants to create a pool
-	// for another tenant.
-	TenantID string
-
-	// Optional. Name of the pool.
-	Name string
-
-	// Optional. Human-readable description for the pool.
-	Description string
-
-	// Required. The protocol used by the pool members, you can use either
-	// ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS.
-	Protocol Protocol
-
-	// The Loadbalancer on which the members of the pool will be associated with.
-	// Note:  one of LoadbalancerID or ListenerID must be provided.
-	LoadbalancerID string
-
-	// The Listener on which the members of the pool will be associated with.
-	// Note:  one of LoadbalancerID or ListenerID must be provided.
-	ListenerID string
-
-	// Required. The algorithm used to distribute load between the members of the pool. The
-	// current specification supports LBMethodRoundRobin, LBMethodLeastConnections
-	// and LBMethodSourceIp as valid values for this attribute.
-	LBMethod LBMethod
-
-	// Optional. Omit this field to prevent session persistence.
-	Persistence *SessionPersistence
-
-	// Optional. The administrative state of the Pool. A valid value is true (UP)
-	// or false (DOWN).
-	AdminStateUp *bool
-}
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -84,10 +34,7 @@
 // ToPoolListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToPoolListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -105,7 +52,6 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return PoolPage{pagination.LinkedPageBase{PageResult: r}}
 	})
@@ -125,12 +71,6 @@
 	ProtocolHTTPS Protocol = "HTTPS"
 )
 
-var (
-	errLoadbalancerOrListenerRequired = fmt.Errorf("A ListenerID or LoadbalancerID is required")
-	errValidLBMethodRequired          = fmt.Errorf("A valid LBMethod is required. Supported values are ROUND_ROBIN, LEAST_CONNECTIONS, SOURCE_IP")
-	errValidProtocolRequired          = fmt.Errorf("A valid Protocol is required. Supported values are TCP, HTTP, HTTPS")
-)
-
 // CreateOptsBuilder is the interface options structs have to satisfy in order
 // to be used in the main Create operation in this package. Since many
 // extensions decorate or modify the common logic, it is useful for them to
@@ -141,71 +81,55 @@
 
 // CreateOpts is the common options struct used in this package's Create
 // operation.
-type CreateOpts poolOpts
+type CreateOpts struct {
+	// The algorithm used to distribute load between the members of the pool. The
+	// current specification supports LBMethodRoundRobin, LBMethodLeastConnections
+	// and LBMethodSourceIp as valid values for this attribute.
+	LBMethod LBMethod `json:"lb_algorithm" required:"true"`
+	// The protocol used by the pool members, you can use either
+	// ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS.
+	Protocol Protocol `json:"protocol" required:"true"`
+	// The Loadbalancer on which the members of the pool will be associated with.
+	// Note:  one of LoadbalancerID or ListenerID must be provided.
+	LoadbalancerID string `json:"loadbalancer_id,omitempty" xor:"ListenerID"`
+	// The Listener on which the members of the pool will be associated with.
+	// Note:  one of LoadbalancerID or ListenerID must be provided.
+	ListenerID string `json:"listener_id,omitempty" xor:"LoadbalancerID"`
+	// Only required if the caller has an admin role and wants to create a pool
+	// for another tenant.
+	TenantID string `json:"tenant_id,omitempty"`
+	// Name of the pool.
+	Name string `json:"name,omitempty"`
+	// Human-readable description for the pool.
+	Description string `json:"description,omitempty"`
+	// Omit this field to prevent session persistence.
+	Persistence *SessionPersistence `json:"session_persistence,omitempty"`
+	// The administrative state of the Pool. A valid value is true (UP)
+	// or false (DOWN).
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
+}
 
 // ToPoolCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToPoolCreateMap() (map[string]interface{}, error) {
-	l := make(map[string]interface{})
-	allowedLBMethod := map[LBMethod]bool{LBMethodRoundRobin: true, LBMethodLeastConnections: true, LBMethodSourceIp: true}
-	allowedProtocol := map[Protocol]bool{ProtocolTCP: true, ProtocolHTTP: true, ProtocolHTTPS: true}
-
-	if allowedLBMethod[opts.LBMethod] {
-		l["lb_algorithm"] = opts.LBMethod
-	} else {
-		return nil, errValidLBMethodRequired
-	}
-	if allowedProtocol[opts.Protocol] {
-		l["protocol"] = opts.Protocol
-	} else {
-		return nil, errValidProtocolRequired
-	}
-	if opts.LoadbalancerID == "" && opts.ListenerID == "" {
-		return nil, errLoadbalancerOrListenerRequired
-	} else {
-		if opts.LoadbalancerID != "" {
-			l["loadbalancer_id"] = opts.LoadbalancerID
-		}
-		if opts.ListenerID != "" {
-			l["listener_id"] = opts.ListenerID
-		}
-	}
-	if opts.AdminStateUp != nil {
-		l["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		l["name"] = opts.Name
-	}
-	if opts.TenantID != "" {
-		l["tenant_id"] = opts.TenantID
-	}
-	if opts.Persistence != nil {
-		l["session_persistence"] = &opts.Persistence
-	}
-
-	return map[string]interface{}{"pool": l}, nil
+	return gophercloud.BuildRequestBody(opts, "pool")
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new
 // load balancer pool.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToPoolCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToPoolCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular pool based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -218,99 +142,56 @@
 
 // UpdateOpts is the common options struct used in this package's Update
 // operation.
-type UpdateOpts poolOpts
+type UpdateOpts struct {
+	// Name of the pool.
+	Name string `json:"name,omitempty"`
+	// Human-readable description for the pool.
+	Description string `json:"description,omitempty"`
+	// The algorithm used to distribute load between the members of the pool. The
+	// current specification supports LBMethodRoundRobin, LBMethodLeastConnections
+	// and LBMethodSourceIp as valid values for this attribute.
+	LBMethod LBMethod `json:"lb_algorithm,omitempty"`
+	// The administrative state of the Pool. A valid value is true (UP)
+	// or false (DOWN).
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
+}
 
 // ToPoolUpdateMap casts a UpdateOpts struct to a map.
 func (opts UpdateOpts) ToPoolUpdateMap() (map[string]interface{}, error) {
-	l := make(map[string]interface{})
-	allowedLBMethod := map[LBMethod]bool{LBMethodRoundRobin: true, LBMethodLeastConnections: true, LBMethodSourceIp: true}
-
-	if opts.LBMethod != "" {
-		if allowedLBMethod[opts.LBMethod] {
-			l["lb_algorithm"] = opts.LBMethod
-		} else {
-			return nil, errValidLBMethodRequired
-		}
-	}
-	if opts.Name != "" {
-		l["name"] = opts.Name
-	}
-	if opts.Description != "" {
-		l["description"] = opts.Description
-	}
-	if opts.AdminStateUp != nil {
-		l["admin_state_up"] = &opts.AdminStateUp
-	}
-
-	return map[string]interface{}{"pool": l}, nil
+	return gophercloud.BuildRequestBody(opts, "pool")
 }
 
 // Update allows pools to be updated.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToPoolUpdateMap()
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToPoolUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // Delete will permanently delete a particular pool based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
 
-// CreateOpts contains all the values needed to create a new Member for a Pool.
-type memberOpts struct {
-	// Optional. Name of the Member.
-	Name string
-
-	// Only required if the caller has an admin role and wants to create a Member
-	// for another tenant.
-	TenantID string
-
-	// Required. The IP address of the member to receive traffic from the load balancer.
-	Address string
-
-	// Required. The port on which to listen for client traffic.
-	ProtocolPort int
-
-	// Optional. A positive integer value that indicates the relative portion of
-	// traffic that this member should receive from the pool. For example, a
-	// member with a weight of 10 receives five times as much traffic as a member
-	// with a weight of 2.
-	Weight int
-
-	// Optional.  If you omit this parameter, LBaaS uses the vip_subnet_id
-	// parameter value for the subnet UUID.
-	SubnetID string
-
-	// Optional. The administrative state of the Pool. A valid value is true (UP)
-	// or false (DOWN).
-	AdminStateUp *bool
+// ListMemberOptsBuilder allows extensions to add additional parameters to the
+// ListMembers request.
+type ListMembersOptsBuilder interface {
+	ToMembersListQuery() (string, error)
 }
 
-// MemberListOptsBuilder allows extensions to add additional parameters to the
-// Member List request.
-type MemberListOptsBuilder interface {
-	ToMemberListQuery() (string, error)
-}
-
-// MemberListOpts allows the filtering and sorting of paginated collections through
+// ListMembersOpts allows the filtering and sorting of paginated collections through
 // the API. Filtering is achieved by passing in struct field values that map to
 // the Member attributes you want to see returned. SortKey allows you to
 // sort by a particular Member attribute. SortDir sets the direction, and is
 // either `asc' or `desc'. Marker and Limit are used for pagination.
-type MemberListOpts struct {
+type ListMembersOpts struct {
 	Name         string `q:"name"`
 	Weight       int    `q:"weight"`
 	AdminStateUp *bool  `q:"admin_state_up"`
@@ -325,161 +206,129 @@
 }
 
 // ToMemberListQuery formats a ListOpts into a query string.
-func (opts MemberListOpts) ToMemberListQuery() (string, error) {
+func (opts ListMembersOpts) ToMembersListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
-// List returns a Pager which allows you to iterate over a collection of
-// members. It accepts a ListOpts struct, which allows you to filter and sort
+// ListMembers returns a Pager which allows you to iterate over a collection of
+// members. It accepts a ListMembersOptsBuilder, which allows you to filter and sort
 // the returned collection for greater efficiency.
 //
 // Default policy settings return only those members that are owned by the
 // tenant who submits the request, unless an admin user submits the request.
-func ListAssociateMembers(c *gophercloud.ServiceClient, poolID string, opts MemberListOptsBuilder) pagination.Pager {
+func ListMembers(c *gophercloud.ServiceClient, poolID string, opts ListMembersOptsBuilder) pagination.Pager {
 	url := memberRootURL(c, poolID)
 	if opts != nil {
-		query, err := opts.ToMemberListQuery()
+		query, err := opts.ToMembersListQuery()
 		if err != nil {
 			return pagination.Pager{Err: err}
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return MemberPage{pagination.LinkedPageBase{PageResult: r}}
 	})
 }
 
-var (
-	errPoolIdRequired       = fmt.Errorf("PoolID is required")
-	errAddressRequired      = fmt.Errorf("Address is required")
-	errProtocolPortRequired = fmt.Errorf("ProtocolPort is required")
-)
-
-// MemberCreateOptsBuilder is the interface options structs have to satisfy in order
-// to be used in the main Create operation in this package. Since many
+// CreateMemberOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the CreateMember operation in this package. Since many
 // extensions decorate or modify the common logic, it is useful for them to
 // satisfy a basic interface in order for them to be used.
-type MemberCreateOptsBuilder interface {
+type CreateMemberOptsBuilder interface {
 	ToMemberCreateMap() (map[string]interface{}, error)
 }
 
-// MemberCreateOpts is the common options struct used in this package's Create
+// CreateMemberOpts is the common options struct used in this package's CreateMember
 // operation.
-type MemberCreateOpts memberOpts
+type CreateMemberOpts struct {
+	// Required. The IP address of the member to receive traffic from the load balancer.
+	Address string `json:"address" required:"true"`
+	// Required. The port on which to listen for client traffic.
+	ProtocolPort int `json:"protocol_port" required:"true"`
+	// Optional. Name of the Member.
+	Name string `json:"name,omitempty"`
+	// Only required if the caller has an admin role and wants to create a Member
+	// for another tenant.
+	TenantID string `json:"tenant_id,omitempty"`
+	// Optional. A positive integer value that indicates the relative portion of
+	// traffic that this member should receive from the pool. For example, a
+	// member with a weight of 10 receives five times as much traffic as a member
+	// with a weight of 2.
+	Weight int `json:"weight,omitempty"`
+	// Optional.  If you omit this parameter, LBaaS uses the vip_subnet_id
+	// parameter value for the subnet UUID.
+	SubnetID string `json:"subnet_id,omitempty"`
+	// Optional. The administrative state of the Pool. A valid value is true (UP)
+	// or false (DOWN).
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
+}
 
 // ToMemberCreateMap casts a CreateOpts struct to a map.
-func (opts MemberCreateOpts) ToMemberCreateMap() (map[string]interface{}, error) {
-	l := make(map[string]interface{})
-
-	if opts.Address != "" {
-		l["address"] = opts.Address
-	} else {
-		return nil, errAddressRequired
-	}
-	if opts.ProtocolPort != 0 {
-		l["protocol_port"] = opts.ProtocolPort
-	} else {
-		return nil, errProtocolPortRequired
-	}
-	if opts.AdminStateUp != nil {
-		l["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		l["name"] = opts.Name
-	}
-	if opts.TenantID != "" {
-		l["tenant_id"] = opts.TenantID
-	}
-	if opts.SubnetID != "" {
-		l["subnet_id"] = opts.SubnetID
-	}
-	if opts.Weight != 0 {
-		l["weight"] = opts.Weight
-	}
-
-	return map[string]interface{}{"member": l}, nil
+func (opts CreateMemberOpts) ToMemberCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "member")
 }
 
-// CreateAssociateMember will create and associate a Member with a particular Pool.
-func CreateAssociateMember(c *gophercloud.ServiceClient, poolID string, opts MemberCreateOpts) AssociateResult {
-	var res AssociateResult
-
-	if poolID == "" {
-		res.Err = errPoolIdRequired
-		return res
-	}
-
-	reqBody, err := opts.ToMemberCreateMap()
+// CreateMember will create and associate a Member with a particular Pool.
+func CreateMember(c *gophercloud.ServiceClient, poolID string, opts CreateMemberOpts) (r CreateMemberResult) {
+	b, err := opts.ToMemberCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(memberRootURL(c, poolID), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(memberRootURL(c, poolID), b, &r.Body, nil)
+	return
 }
 
-// Get retrieves a particular Pool Member based on its unique ID.
-func GetAssociateMember(c *gophercloud.ServiceClient, poolID string, memberID string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(memberResourceURL(c, poolID, memberID), &res.Body, nil)
-	return res
+// GetMember retrieves a particular Pool Member based on its unique ID.
+func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) {
+	_, r.Err = c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil)
+	return
 }
 
 // MemberUpdateOptsBuilder is the interface options structs have to satisfy in order
 // to be used in the main Update operation in this package. Since many
 // extensions decorate or modify the common logic, it is useful for them to
 // satisfy a basic interface in order for them to be used.
-type MemberUpdateOptsBuilder interface {
+type UpdateMemberOptsBuilder interface {
 	ToMemberUpdateMap() (map[string]interface{}, error)
 }
 
-// UpdateOpts is the common options struct used in this package's Update
+// UpdateMemberOpts is the common options struct used in this package's Update
 // operation.
-type MemberUpdateOpts memberOpts
+type UpdateMemberOpts struct {
+	// Optional. Name of the Member.
+	Name string `json:"name,omitempty"`
+	// Optional. A positive integer value that indicates the relative portion of
+	// traffic that this member should receive from the pool. For example, a
+	// member with a weight of 10 receives five times as much traffic as a member
+	// with a weight of 2.
+	Weight int `json:"weight,omitempty"`
+	// Optional. The administrative state of the Pool. A valid value is true (UP)
+	// or false (DOWN).
+	AdminStateUp *bool `json:"admin_state_up,omitempty"`
+}
 
 // ToMemberUpdateMap casts a UpdateOpts struct to a map.
-func (opts MemberUpdateOpts) ToMemberUpdateMap() (map[string]interface{}, error) {
-	l := make(map[string]interface{})
-
-	if opts.AdminStateUp != nil {
-		l["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		l["name"] = opts.Name
-	}
-	if opts.Weight != 0 {
-		l["weight"] = opts.Weight
-	}
-
-	return map[string]interface{}{"member": l}, nil
+func (opts UpdateMemberOpts) ToMemberUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "member")
 }
 
 // Update allows Member to be updated.
-func UpdateAssociateMember(c *gophercloud.ServiceClient, poolID string, memberID string, opts MemberUpdateOpts) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToMemberUpdateMap()
+func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, opts UpdateMemberOptsBuilder) (r UpdateMemberResult) {
+	b, err := opts.ToMemberUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(memberResourceURL(c, poolID, memberID), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201, 202},
 	})
-	return res
+	return
 }
 
 // DisassociateMember will remove and disassociate a Member from a particular Pool.
-func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(memberResourceURL(c, poolID, memberID), nil)
-	return res
+func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) {
+	_, r.Err = c.Delete(memberResourceURL(c, poolID, memberID), nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests_test.go
deleted file mode 100644
index 288637d..0000000
--- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests_test.go
+++ /dev/null
@@ -1,255 +0,0 @@
-package pools
-
-import (
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lbaas/pools", rootURL(fake.ServiceClient()))
-}
-
-func TestListPools(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePoolListSuccessfully(t)
-
-	pages := 0
-	err := List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractPools(page)
-		if err != nil {
-			return false, err
-		}
-
-		if len(actual) != 2 {
-			t.Fatalf("Expected 2 pools, got %d", len(actual))
-		}
-		th.CheckDeepEquals(t, PoolWeb, actual[0])
-		th.CheckDeepEquals(t, PoolDb, actual[1])
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-
-	if pages != 1 {
-		t.Errorf("Expected 1 page, saw %d", pages)
-	}
-}
-
-func TestListAllPools(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePoolListSuccessfully(t)
-
-	allPages, err := List(fake.ServiceClient(), ListOpts{}).AllPages()
-	th.AssertNoErr(t, err)
-	actual, err := ExtractPools(allPages)
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, PoolWeb, actual[0])
-	th.CheckDeepEquals(t, PoolDb, actual[1])
-}
-
-func TestCreatePool(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePoolCreationSuccessfully(t, SinglePoolBody)
-
-	actual, err := Create(fake.ServiceClient(), CreateOpts{
-		LBMethod:       LBMethodRoundRobin,
-		Protocol:       "HTTP",
-		Name:           "Example pool",
-		TenantID:       "2ffc6e22aae24e4795f87155d24c896f",
-		LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab",
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	th.CheckDeepEquals(t, PoolDb, *actual)
-}
-
-func TestGetPool(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePoolGetSuccessfully(t)
-
-	client := fake.ServiceClient()
-	actual, err := Get(client, "c3741b06-df4d-4715-b142-276b6bce75ab").Extract()
-	if err != nil {
-		t.Fatalf("Unexpected Get error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, PoolDb, *actual)
-}
-
-func TestDeletePool(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePoolDeletionSuccessfully(t)
-
-	res := Delete(fake.ServiceClient(), "c3741b06-df4d-4715-b142-276b6bce75ab")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestUpdatePool(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePoolUpdateSuccessfully(t)
-
-	client := fake.ServiceClient()
-	actual, err := Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", UpdateOpts{
-		Name:     "NewPoolName",
-		LBMethod: LBMethodLeastConnections,
-	}).Extract()
-	if err != nil {
-		t.Fatalf("Unexpected Update error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, PoolUpdated, *actual)
-}
-
-func TestRequiredPoolCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), CreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{LBMethod: LBMethod("invalid"), Protocol: ProtocolHTTPS, LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a"})
-	if res.Err == nil || res.Err != errValidLBMethodRequired {
-		t.Fatalf("Expected '%s' error, but got '%s'", errValidLBMethodRequired, res.Err)
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{LBMethod: LBMethodRoundRobin, Protocol: Protocol("invalid"), LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a"})
-	if res.Err == nil || res.Err != errValidProtocolRequired {
-		t.Fatalf("Expected '%s' error, but got '%s'", errValidProtocolRequired, res.Err)
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{LBMethod: LBMethodRoundRobin, Protocol: ProtocolHTTPS})
-	if res.Err == nil || res.Err != errLoadbalancerOrListenerRequired {
-		t.Fatalf("Expected '%s' error, but got '%s'", errLoadbalancerOrListenerRequired, res.Err)
-	}
-}
-
-func TestListMembers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleMemberListSuccessfully(t)
-
-	pages := 0
-	err := ListAssociateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", MemberListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractMembers(page)
-		if err != nil {
-			return false, err
-		}
-
-		if len(actual) != 2 {
-			t.Fatalf("Expected 2 members, got %d", len(actual))
-		}
-		th.CheckDeepEquals(t, MemberWeb, actual[0])
-		th.CheckDeepEquals(t, MemberDb, actual[1])
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-
-	if pages != 1 {
-		t.Errorf("Expected 1 page, saw %d", pages)
-	}
-}
-
-func TestListAllMembers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleMemberListSuccessfully(t)
-
-	allPages, err := ListAssociateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", MemberListOpts{}).AllPages()
-	th.AssertNoErr(t, err)
-	actual, err := ExtractMembers(allPages)
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, MemberWeb, actual[0])
-	th.CheckDeepEquals(t, MemberDb, actual[1])
-}
-
-func TestCreateMember(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleMemberCreationSuccessfully(t, SingleMemberBody)
-
-	actual, err := CreateAssociateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", MemberCreateOpts{
-		Name:         "db",
-		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
-		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
-		Address:      "10.0.2.11",
-		ProtocolPort: 80,
-		Weight:       10,
-	}).ExtractMember()
-	th.AssertNoErr(t, err)
-
-	th.CheckDeepEquals(t, MemberDb, *actual)
-}
-
-func TestRequiredMemberCreateOpts(t *testing.T) {
-	res := CreateAssociateMember(fake.ServiceClient(), "", MemberCreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = CreateAssociateMember(fake.ServiceClient(), "", MemberCreateOpts{Address: "1.2.3.4", ProtocolPort: 80})
-	if res.Err == nil || res.Err != errPoolIdRequired {
-		t.Fatalf("Expected '%s' error, but got '%s'", errPoolIdRequired, res.Err)
-	}
-	res = CreateAssociateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", MemberCreateOpts{ProtocolPort: 80})
-	if res.Err == nil || res.Err != errAddressRequired {
-		t.Fatalf("Expected '%s' error, but got '%s'", errAddressRequired, res.Err)
-	}
-	res = CreateAssociateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", MemberCreateOpts{Address: "1.2.3.4"})
-	if res.Err == nil || res.Err != errProtocolPortRequired {
-		t.Fatalf("Expected '%s' error, but got '%s'", errProtocolPortRequired, res.Err)
-	}
-}
-
-func TestGetMember(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleMemberGetSuccessfully(t)
-
-	client := fake.ServiceClient()
-	actual, err := GetAssociateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").ExtractMember()
-	if err != nil {
-		t.Fatalf("Unexpected Get error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, MemberDb, *actual)
-}
-
-func TestDeleteMember(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleMemberDeletionSuccessfully(t)
-
-	res := DeleteMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestUpdateMember(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleMemberUpdateSuccessfully(t)
-
-	client := fake.ServiceClient()
-	actual, err := UpdateAssociateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", MemberUpdateOpts{
-		Name:   "newMemberName",
-		Weight: 4,
-	}).ExtractMember()
-	if err != nil {
-		t.Fatalf("Unexpected Update error: %v", err)
-	}
-
-	th.CheckDeepEquals(t, MemberUpdated, *actual)
-}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go
index 17f677d..0e0bf36 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go
@@ -1,10 +1,9 @@
 package pools
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // SessionPersistence represents the session persistence feature of the load
@@ -24,18 +23,18 @@
 //              same Member of the Pool.
 type SessionPersistence struct {
 	// The type of persistence mode
-	Type string `mapstructure:"type" json:"type"`
+	Type string `json:"type"`
 
 	// Name of cookie if persistence mode is set appropriately
-	CookieName string `mapstructure:"cookie_name" json:"cookie_name,omitempty"`
+	CookieName string `json:"cookie_name,omitempty"`
 }
 
 type LoadBalancerID struct {
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 }
 
 type ListenerID struct {
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 }
 
 // Pool represents a logical set of devices, such as web servers, that you
@@ -46,51 +45,37 @@
 	// The load-balancer algorithm, which is round-robin, least-connections, and
 	// so on. This value, which must be supported, is dependent on the provider.
 	// Round-robin must be supported.
-	LBMethod string `json:"lb_algorithm" mapstructure:"lb_algorithm"`
-
+	LBMethod string `json:"lb_algorithm"`
 	// The protocol of the Pool, which is TCP, HTTP, or HTTPS.
-	Protocol string
-
+	Protocol string `json:"protocol"`
 	// Description for the Pool.
-	Description string
-
+	Description string `json:"description"`
 	// A list of listeners objects IDs.
-	Listeners []ListenerID `mapstructure:"listeners" json:"listeners"` //[]map[string]interface{}
-
+	Listeners []ListenerID `json:"listeners"` //[]map[string]interface{}
 	// A list of member objects IDs.
-	Members []Member `mapstructure:"members" json:"members"`
-
+	Members []Member `json:"members"`
 	// The ID of associated health monitor.
-	MonitorID string `json:"healthmonitor_id" mapstructure:"healthmonitor_id"`
-
+	MonitorID string `json:"healthmonitor_id"`
 	// The network on which the members of the Pool will be located. Only members
 	// that are on this network can be added to the Pool.
-	SubnetID string `json:"subnet_id" mapstructure:"subnet_id"`
-
+	SubnetID string `json:"subnet_id"`
 	// Owner of the Pool. Only an administrative user can specify a tenant ID
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
-
+	TenantID string `json:"tenant_id"`
 	// The administrative state of the Pool, which is up (true) or down (false).
-	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
-
+	AdminStateUp bool `json:"admin_state_up"`
 	// Pool name. Does not have to be unique.
-	Name string
-
+	Name string `json:"name"`
 	// The unique ID for the Pool.
-	ID string
-
+	ID string `json:"id"`
 	// A list of load balancer objects IDs.
-	Loadbalancers []LoadBalancerID `mapstructure:"loadbalancers" json:"loadbalancers"`
-
+	Loadbalancers []LoadBalancerID `json:"loadbalancers"`
 	// Indicates whether connections in the same session will be processed by the
 	// same Pool member or not.
-	Persistence SessionPersistence `mapstructure:"session_persistence" json:"session_persistence"`
-
+	Persistence SessionPersistence `json:"session_persistence"`
 	// The provider
-	Provider string
-
-	Monitor monitors.Monitor `mapstructure:"healthmonitor" json:"healthmonitor"`
+	Provider string           `json:"provider"`
+	Monitor  monitors.Monitor `json:"healthmonitor"`
 }
 
 // PoolPage is the page returned by a pager when traversing over a
@@ -102,40 +87,32 @@
 // NextPageURL is invoked when a paginated collection of pools has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p PoolPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"pools_links"`
+func (r PoolPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"pools_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a PoolPage struct is empty.
-func (p PoolPage) IsEmpty() (bool, error) {
-	is, err := ExtractPools(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r PoolPage) IsEmpty() (bool, error) {
+	is, err := ExtractPools(r)
+	return len(is) == 0, err
 }
 
-// ExtractPools accepts a Page struct, specifically a RouterPage struct,
+// ExtractPools accepts a Page struct, specifically a PoolPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractPools(page pagination.Page) ([]Pool, error) {
-	var resp struct {
-		Pools []Pool `mapstructure:"pools" json:"pools"`
+func ExtractPools(r pagination.Page) ([]Pool, error) {
+	var s struct {
+		Pools []Pool `json:"pools"`
 	}
-
-	err := mapstructure.Decode(page.(PoolPage).Body, &resp)
-
-	return resp.Pools, err
+	err := (r.(PoolPage)).ExtractInto(&s)
+	return s.Pools, err
 }
 
 type commonResult struct {
@@ -144,48 +121,54 @@
 
 // Extract is a function that accepts a result and extracts a router.
 func (r commonResult) Extract() (*Pool, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Pool *Pool `json:"pool"`
 	}
+	err := r.ExtractInto(&s)
+	return s.Pool, err
+}
 
-	err := mapstructure.Decode(r.Body, &res)
+// CreateResult represents the result of a Create operation.
+type CreateResult struct {
+	commonResult
+}
 
-	return res.Pool, err
+// GetResult represents the result of a Get operation.
+type GetResult struct {
+	commonResult
+}
+
+// UpdateResult represents the result of an Update operation.
+type UpdateResult struct {
+	commonResult
+}
+
+// DeleteResult represents the result of a Delete operation.
+type DeleteResult struct {
+	gophercloud.ErrResult
 }
 
 // Member represents the application running on a backend server.
 type Member struct {
 	// Name of the Member.
-	Name string `json:"name" mapstructure:"name"`
-
+	Name string `json:"name"`
 	// Weight of Member.
-	Weight int `json:"weight" mapstructure:"weight"`
-
+	Weight int `json:"weight"`
 	// The administrative state of the member, which is up (true) or down (false).
-	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
-
+	AdminStateUp bool `json:"admin_state_up"`
 	// Owner of the Member. Only an administrative user can specify a tenant ID
 	// other than its own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
-
+	TenantID string `json:"tenant_id"`
 	// parameter value for the subnet UUID.
-	SubnetID string `json:"subnet_id" mapstructure:"subnet_id"`
-
+	SubnetID string `json:"subnet_id"`
 	// The Pool to which the Member belongs.
-	PoolID string `json:"pool_id" mapstructure:"pool_id"`
-
+	PoolID string `json:"pool_id"`
 	// The IP address of the Member.
-	Address string `json:"address" mapstructure:"address"`
-
+	Address string `json:"address"`
 	// The port on which the application is hosted.
-	ProtocolPort int `json:"protocol_port" mapstructure:"protocol_port"`
-
+	ProtocolPort int `json:"protocol_port"`
 	// The unique ID for the Member.
-	ID string
+	ID string `json:"id"`
 }
 
 // MemberPage is the page returned by a pager when traversing over a
@@ -197,78 +180,63 @@
 // NextPageURL is invoked when a paginated collection of members has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p MemberPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"members_links"`
+func (r MemberPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"members_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a MemberPage struct is empty.
-func (p MemberPage) IsEmpty() (bool, error) {
-	is, err := ExtractMembers(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r MemberPage) IsEmpty() (bool, error) {
+	is, err := ExtractMembers(r)
+	return len(is) == 0, err
 }
 
 // ExtractMembers accepts a Page struct, specifically a RouterPage struct,
 // and extracts the elements into a slice of Router structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractMembers(page pagination.Page) ([]Member, error) {
-	var resp struct {
-		Member []Member `mapstructure:"members" json:"members"`
+func ExtractMembers(r pagination.Page) ([]Member, error) {
+	var s struct {
+		Members []Member `json:"members"`
 	}
+	err := (r.(MemberPage)).ExtractInto(&s)
+	return s.Members, err
+}
 
-	err := mapstructure.Decode(page.(MemberPage).Body, &resp)
-
-	return resp.Member, err
+type commonMemberResult struct {
+	gophercloud.Result
 }
 
 // ExtractMember is a function that accepts a result and extracts a router.
-func (r commonResult) ExtractMember() (*Member, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+func (r commonMemberResult) Extract() (*Member, error) {
+	var s struct {
 		Member *Member `json:"member"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Member, err
+	err := r.ExtractInto(&s)
+	return s.Member, err
 }
 
-// CreateResult represents the result of a create operation.
-type CreateResult struct {
-	commonResult
+// CreateMemberResult represents the result of a CreateMember operation.
+type CreateMemberResult struct {
+	commonMemberResult
 }
 
-// GetResult represents the result of a get operation.
-type GetResult struct {
-	commonResult
+// GetMemberResult represents the result of a GetMember operation.
+type GetMemberResult struct {
+	commonMemberResult
 }
 
-// UpdateResult represents the result of an update operation.
-type UpdateResult struct {
-	commonResult
+// UpdateMemberResult represents the result of an UpdateMember operation.
+type UpdateMemberResult struct {
+	commonMemberResult
 }
 
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
+// DeleteMemberResult represents the result of a DeleteMember operation.
+type DeleteMemberResult struct {
 	gophercloud.ErrResult
 }
-
-// AssociateResult represents the result of an association operation.
-type AssociateResult struct {
-	commonResult
-}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures.go
new file mode 100644
index 0000000..df9d1fd
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures.go
@@ -0,0 +1,388 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// PoolsListBody contains the canned body of a pool list response.
+const PoolsListBody = `
+{
+	"pools":[
+	         {
+			"lb_algorithm":"ROUND_ROBIN",
+			"protocol":"HTTP",
+			"description":"",
+			"healthmonitor_id": "466c8345-28d8-4f84-a246-e04380b0461d",
+			"members":[{"id": "53306cda-815d-4354-9fe4-59e09da9c3c5"}],
+			"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
+			"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+			"id":"72741b06-df4d-4715-b142-276b6bce75ab",
+			"name":"web",
+			"admin_state_up":true,
+			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+			"provider": "haproxy"
+		},
+		{
+			"lb_algorithm":"LEAST_CONNECTION",
+			"protocol":"HTTP",
+			"description":"",
+			"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
+			"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
+			"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
+			"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+			"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
+			"name":"db",
+			"admin_state_up":true,
+			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+			"provider": "haproxy"
+		}
+	]
+}
+`
+
+// SinglePoolBody is the canned body of a Get request on an existing pool.
+const SinglePoolBody = `
+{
+	"pool": {
+		"lb_algorithm":"LEAST_CONNECTION",
+		"protocol":"HTTP",
+		"description":"",
+		"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
+		"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
+		"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
+		"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+		"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
+		"name":"db",
+		"admin_state_up":true,
+		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+		"provider": "haproxy"
+	}
+}
+`
+
+// PostUpdatePoolBody is the canned response body of a Update request on an existing pool.
+const PostUpdatePoolBody = `
+{
+	"pool": {
+		"lb_algorithm":"LEAST_CONNECTION",
+		"protocol":"HTTP",
+		"description":"",
+		"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
+		"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
+		"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
+		"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+		"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
+		"name":"db",
+		"admin_state_up":true,
+		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+		"provider": "haproxy"
+	}
+}
+`
+
+var (
+	PoolWeb = pools.Pool{
+		LBMethod:      "ROUND_ROBIN",
+		Protocol:      "HTTP",
+		Description:   "",
+		MonitorID:     "466c8345-28d8-4f84-a246-e04380b0461d",
+		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+		AdminStateUp:  true,
+		Name:          "web",
+		Members:       []pools.Member{{ID: "53306cda-815d-4354-9fe4-59e09da9c3c5"}},
+		ID:            "72741b06-df4d-4715-b142-276b6bce75ab",
+		Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
+		Listeners:     []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
+		Provider:      "haproxy",
+	}
+	PoolDb = pools.Pool{
+		LBMethod:      "LEAST_CONNECTION",
+		Protocol:      "HTTP",
+		Description:   "",
+		MonitorID:     "5f6c8345-28d8-4f84-a246-e04380b0461d",
+		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+		AdminStateUp:  true,
+		Name:          "db",
+		Members:       []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}},
+		ID:            "c3741b06-df4d-4715-b142-276b6bce75ab",
+		Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
+		Listeners:     []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
+		Provider:      "haproxy",
+	}
+	PoolUpdated = pools.Pool{
+		LBMethod:      "LEAST_CONNECTION",
+		Protocol:      "HTTP",
+		Description:   "",
+		MonitorID:     "5f6c8345-28d8-4f84-a246-e04380b0461d",
+		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+		AdminStateUp:  true,
+		Name:          "db",
+		Members:       []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}},
+		ID:            "c3741b06-df4d-4715-b142-276b6bce75ab",
+		Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
+		Listeners:     []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
+		Provider:      "haproxy",
+	}
+)
+
+// HandlePoolListSuccessfully sets up the test server to respond to a pool List request.
+func HandlePoolListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, PoolsListBody)
+		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
+			fmt.Fprintf(w, `{ "pools": [] }`)
+		default:
+			t.Fatalf("/v2.0/lbaas/pools invoked with unexpected marker=[%s]", marker)
+		}
+	})
+}
+
+// HandlePoolCreationSuccessfully sets up the test server to respond to a pool creation request
+// with a given response.
+func HandlePoolCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"pool": {
+			        "lb_algorithm": "ROUND_ROBIN",
+			        "protocol": "HTTP",
+			        "name": "Example pool",
+			        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+			        "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab"
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandlePoolGetSuccessfully sets up the test server to respond to a pool Get request.
+func HandlePoolGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SinglePoolBody)
+	})
+}
+
+// HandlePoolDeletionSuccessfully sets up the test server to respond to a pool deletion request.
+func HandlePoolDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandlePoolUpdateSuccessfully sets up the test server to respond to a pool Update request.
+func HandlePoolUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `{
+			"pool": {
+				"name": "NewPoolName",
+                                "lb_algorithm": "LEAST_CONNECTIONS"
+			}
+		}`)
+
+		fmt.Fprintf(w, PostUpdatePoolBody)
+	})
+}
+
+// MembersListBody contains the canned body of a member list response.
+const MembersListBody = `
+{
+	"members":[
+		{
+			"id": "2a280670-c202-4b0b-a562-34077415aabf",
+			"address": "10.0.2.10",
+			"weight": 5,
+			"name": "web",
+			"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+			"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+			"admin_state_up":true,
+			"protocol_port": 80
+		},
+		{
+			"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
+			"address": "10.0.2.11",
+			"weight": 10,
+			"name": "db",
+			"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+			"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+			"admin_state_up":false,
+			"protocol_port": 80
+		}
+	]
+}
+`
+
+// SingleMemberBody is the canned body of a Get request on an existing member.
+const SingleMemberBody = `
+{
+	"member": {
+		"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
+		"address": "10.0.2.11",
+		"weight": 10,
+		"name": "db",
+		"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+		"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+		"admin_state_up":false,
+		"protocol_port": 80
+	}
+}
+`
+
+// PostUpdateMemberBody is the canned response body of a Update request on an existing member.
+const PostUpdateMemberBody = `
+{
+	"member": {
+		"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
+		"address": "10.0.2.11",
+		"weight": 10,
+		"name": "db",
+		"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+		"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+		"admin_state_up":false,
+		"protocol_port": 80
+	}
+}
+`
+
+var (
+	MemberWeb = pools.Member{
+		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
+		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
+		AdminStateUp: true,
+		Name:         "web",
+		ID:           "2a280670-c202-4b0b-a562-34077415aabf",
+		Address:      "10.0.2.10",
+		Weight:       5,
+		ProtocolPort: 80,
+	}
+	MemberDb = pools.Member{
+		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
+		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
+		AdminStateUp: false,
+		Name:         "db",
+		ID:           "fad389a3-9a4a-4762-a365-8c7038508b5d",
+		Address:      "10.0.2.11",
+		Weight:       10,
+		ProtocolPort: 80,
+	}
+	MemberUpdated = pools.Member{
+		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
+		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
+		AdminStateUp: false,
+		Name:         "db",
+		ID:           "fad389a3-9a4a-4762-a365-8c7038508b5d",
+		Address:      "10.0.2.11",
+		Weight:       10,
+		ProtocolPort: 80,
+	}
+)
+
+// HandleMemberListSuccessfully sets up the test server to respond to a member List request.
+func HandleMemberListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, MembersListBody)
+		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
+			fmt.Fprintf(w, `{ "members": [] }`)
+		default:
+			t.Fatalf("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members invoked with unexpected marker=[%s]", marker)
+		}
+	})
+}
+
+// HandleMemberCreationSuccessfully sets up the test server to respond to a member creation request
+// with a given response.
+func HandleMemberCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"member": {
+			        "address": "10.0.2.11",
+			        "weight": 10,
+			        "name": "db",
+			        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+			        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+			        "protocol_port": 80
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandleMemberGetSuccessfully sets up the test server to respond to a member Get request.
+func HandleMemberGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleMemberBody)
+	})
+}
+
+// HandleMemberDeletionSuccessfully sets up the test server to respond to a member deletion request.
+func HandleMemberDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleMemberUpdateSuccessfully sets up the test server to respond to a member Update request.
+func HandleMemberUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `{
+			"member": {
+				"name": "newMemberName",
+                                "weight": 4
+			}
+		}`)
+
+		fmt.Fprintf(w, PostUpdateMemberBody)
+	})
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go
new file mode 100644
index 0000000..4af00ec
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go
@@ -0,0 +1,262 @@
+package testing
+
+import (
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestListPools(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePoolListSuccessfully(t)
+
+	pages := 0
+	err := pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := pools.ExtractPools(page)
+		if err != nil {
+			return false, err
+		}
+
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 pools, got %d", len(actual))
+		}
+		th.CheckDeepEquals(t, PoolWeb, actual[0])
+		th.CheckDeepEquals(t, PoolDb, actual[1])
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
+	}
+}
+
+func TestListAllPools(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePoolListSuccessfully(t)
+
+	allPages, err := pools.List(fake.ServiceClient(), pools.ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := pools.ExtractPools(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, PoolWeb, actual[0])
+	th.CheckDeepEquals(t, PoolDb, actual[1])
+}
+
+func TestCreatePool(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePoolCreationSuccessfully(t, SinglePoolBody)
+
+	actual, err := pools.Create(fake.ServiceClient(), pools.CreateOpts{
+		LBMethod:       pools.LBMethodRoundRobin,
+		Protocol:       "HTTP",
+		Name:           "Example pool",
+		TenantID:       "2ffc6e22aae24e4795f87155d24c896f",
+		LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab",
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, PoolDb, *actual)
+}
+
+func TestGetPool(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePoolGetSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := pools.Get(client, "c3741b06-df4d-4715-b142-276b6bce75ab").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, PoolDb, *actual)
+}
+
+func TestDeletePool(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePoolDeletionSuccessfully(t)
+
+	res := pools.Delete(fake.ServiceClient(), "c3741b06-df4d-4715-b142-276b6bce75ab")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestUpdatePool(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePoolUpdateSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := pools.Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{
+		Name:     "NewPoolName",
+		LBMethod: pools.LBMethodLeastConnections,
+	}).Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Update error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, PoolUpdated, *actual)
+}
+
+func TestRequiredPoolCreateOpts(t *testing.T) {
+	res := pools.Create(fake.ServiceClient(), pools.CreateOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = pools.Create(fake.ServiceClient(), pools.CreateOpts{
+		LBMethod:       pools.LBMethod("invalid"),
+		Protocol:       pools.ProtocolHTTPS,
+		LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a",
+	})
+	if res.Err == nil {
+		t.Fatalf("Expected error, but got none")
+	}
+
+	res = pools.Create(fake.ServiceClient(), pools.CreateOpts{
+		LBMethod:       pools.LBMethodRoundRobin,
+		Protocol:       pools.Protocol("invalid"),
+		LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a",
+	})
+	if res.Err == nil {
+		t.Fatalf("Expected error, but got none")
+	}
+
+	res = pools.Create(fake.ServiceClient(), pools.CreateOpts{
+		LBMethod: pools.LBMethodRoundRobin,
+		Protocol: pools.ProtocolHTTPS,
+	})
+	if res.Err == nil {
+		t.Fatalf("Expected error, but got none")
+	}
+}
+
+func TestListMembers(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleMemberListSuccessfully(t)
+
+	pages := 0
+	err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := pools.ExtractMembers(page)
+		if err != nil {
+			return false, err
+		}
+
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 members, got %d", len(actual))
+		}
+		th.CheckDeepEquals(t, MemberWeb, actual[0])
+		th.CheckDeepEquals(t, MemberDb, actual[1])
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
+	}
+}
+
+func TestListAllMembers(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleMemberListSuccessfully(t)
+
+	allPages, err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := pools.ExtractMembers(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, MemberWeb, actual[0])
+	th.CheckDeepEquals(t, MemberDb, actual[1])
+}
+
+func TestCreateMember(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleMemberCreationSuccessfully(t, SingleMemberBody)
+
+	actual, err := pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{
+		Name:         "db",
+		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
+		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
+		Address:      "10.0.2.11",
+		ProtocolPort: 80,
+		Weight:       10,
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, MemberDb, *actual)
+}
+
+func TestRequiredMemberCreateOpts(t *testing.T) {
+	res := pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{Address: "1.2.3.4", ProtocolPort: 80})
+	if res.Err == nil {
+		t.Fatalf("Expected error, but got none")
+	}
+	res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ProtocolPort: 80})
+	if res.Err == nil {
+		t.Fatalf("Expected error, but got none")
+	}
+	res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{Address: "1.2.3.4"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, but got none")
+	}
+}
+
+func TestGetMember(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleMemberGetSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := pools.GetMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, MemberDb, *actual)
+}
+
+func TestDeleteMember(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleMemberDeletionSuccessfully(t)
+
+	res := pools.DeleteMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestUpdateMember(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleMemberUpdateSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{
+		Name:   "newMemberName",
+		Weight: 4,
+	}).Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Update error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, MemberUpdated, *actual)
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go b/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go
index e206406..bceca67 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go
@@ -1,6 +1,6 @@
 package pools
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const (
 	rootPath     = "lbaas"
diff --git a/openstack/networking/v2/extensions/portsbinding/fixtures.go b/openstack/networking/v2/extensions/portsbinding/fixtures.go
deleted file mode 100644
index 9f7bd08..0000000
--- a/openstack/networking/v2/extensions/portsbinding/fixtures.go
+++ /dev/null
@@ -1,206 +0,0 @@
-package portsbinding
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func HandleListSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "ports": [
-        {
-            "status": "ACTIVE",
-            "binding:host_id": "devstack",
-            "name": "",
-            "admin_state_up": true,
-            "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3",
-            "tenant_id": "",
-            "device_owner": "network:router_gateway",
-            "mac_address": "fa:16:3e:58:42:ed",
-            "fixed_ips": [
-                {
-                    "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062",
-                    "ip_address": "172.24.4.2"
-                }
-            ],
-            "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
-            "security_groups": [],
-            "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824",
-            "binding:vnic_type": "normal"
-        }
-    ]
-}
-      `)
-	})
-}
-
-func HandleGet(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "port": {
-        "status": "ACTIVE",
-        "binding:host_id": "devstack",
-        "name": "",
-        "allowed_address_pairs": [],
-        "admin_state_up": true,
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "tenant_id": "7e02058126cc4950b75f9970368ba177",
-        "extra_dhcp_opts": [],
-        "binding:vif_details": {
-            "port_filter": true,
-            "ovs_hybrid_plug": true
-        },
-        "binding:vif_type": "ovs",
-        "device_owner": "network:router_interface",
-        "port_security_enabled": false,
-        "mac_address": "fa:16:3e:23:fd:d7",
-        "binding:profile": {},
-        "binding:vnic_type": "normal",
-        "fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.1"
-            }
-        ],
-        "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2",
-        "security_groups": [],
-        "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e"
-    }
-}
-    `)
-	})
-}
-
-func HandleCreate(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "port": {
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "name": "private-port",
-        "admin_state_up": true,
-		"fixed_ips": [
-				{
-						"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-						"ip_address": "10.0.0.2"
-				}
-		],
-		"security_groups": ["foo"],
-		"binding:host_id": "HOST1",
-        "binding:vnic_type": "normal"
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "port": {
-        "status": "DOWN",
-        "name": "private-port",
-        "allowed_address_pairs": [],
-        "admin_state_up": true,
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
-        "device_owner": "",
-        "mac_address": "fa:16:3e:c9:cb:f0",
-        "fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.2"
-            }
-        ],
-        "binding:host_id": "HOST1",
-        "binding:vnic_type": "normal",
-        "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
-        "security_groups": [
-            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
-        ],
-        "device_id": ""
-    }
-}
-		`)
-	})
-}
-
-func HandleUpdate(t *testing.T) {
-	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-		"port": {
-			"name": "new_port_name",
-			"fixed_ips": [
-				{
-					"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-					"ip_address": "10.0.0.3"
-				}
-			],
-			"security_groups": [
-            	"f0ac4394-7e4a-4409-9701-ba8be283dbc3"
-        	],
-        	"binding:host_id": "HOST1",
-        	"binding:vnic_type": "normal"
-		}
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "port": {
-        "status": "DOWN",
-        "name": "new_port_name",
-        "admin_state_up": true,
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
-        "device_owner": "",
-        "mac_address": "fa:16:3e:c9:cb:f0",
-        "fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.3"
-            }
-        ],
-        "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
-        "security_groups": [
-            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
-        ],
-        "device_id": "",
-        "binding:host_id": "HOST1",
-        "binding:vnic_type": "normal"
-    }
-}
-		`)
-	})
-}
diff --git a/openstack/networking/v2/extensions/portsbinding/requests.go b/openstack/networking/v2/extensions/portsbinding/requests.go
index 4d4300a..b46172b 100644
--- a/openstack/networking/v2/extensions/portsbinding/requests.go
+++ b/openstack/networking/v2/extensions/portsbinding/requests.go
@@ -1,15 +1,14 @@
 package portsbinding
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
 )
 
 // Get retrieves a specific port based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(getURL(c, id), &r.Body, nil)
+	return
 }
 
 // CreateOpts represents the attributes used when creating a new
@@ -17,35 +16,34 @@
 type CreateOpts struct {
 	// CreateOptsBuilder is the interface options structs have to satisfy in order
 	// to be used in the main Create operation in this package.
-	ports.CreateOptsBuilder
+	ports.CreateOptsBuilder `json:"-"`
 	// The ID of the host where the port is allocated
-	HostID string
+	HostID string `json:"binding:host_id,omitempty"`
 	// The virtual network interface card (vNIC) type that is bound to the
 	// neutron port
-	VNICType string
+	VNICType string `json:"binding:vnic_type,omitempty"`
 	// A dictionary that enables the application running on the specified
 	// host to pass and receive virtual network interface (VIF) port-specific
 	// information to the plug-in
-	Profile map[string]string
+	Profile map[string]string `json:"binding:profile,omitempty"`
 }
 
 // ToPortCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
-	p, err := opts.CreateOptsBuilder.ToPortCreateMap()
+	b1, err := opts.CreateOptsBuilder.ToPortCreateMap()
 	if err != nil {
 		return nil, err
 	}
 
-	port := p["port"].(map[string]interface{})
+	b2, err := gophercloud.BuildRequestBody(opts, "")
+	if err != nil {
+		return nil, err
+	}
 
-	if opts.HostID != "" {
-		port["binding:host_id"] = opts.HostID
-	}
-	if opts.VNICType != "" {
-		port["binding:vnic_type"] = opts.VNICType
-	}
-	if opts.Profile != nil {
-		port["binding:profile"] = opts.Profile
+	port := b1["port"].(map[string]interface{})
+
+	for k, v := range b2 {
+		port[k] = v
 	}
 
 	return map[string]interface{}{"port": port}, nil
@@ -53,59 +51,48 @@
 
 // Create accepts a CreateOpts struct and creates a new port with extended attributes.
 // You must remember to provide a NetworkID value.
-func Create(c *gophercloud.ServiceClient, opts ports.CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToPortCreateMap()
+func Create(c *gophercloud.ServiceClient, opts ports.CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToPortCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(createURL(c), b, &r.Body, nil)
+	return
 }
 
 // UpdateOpts represents the attributes used when updating an existing port.
 type UpdateOpts struct {
 	// UpdateOptsBuilder is the interface options structs have to satisfy in order
 	// to be used in the main Update operation in this package.
-	ports.UpdateOptsBuilder
+	ports.UpdateOptsBuilder `json:"-"`
 	// The ID of the host where the port is allocated
-	HostID string
+	HostID string `json:"binding:host_id,omitempty"`
 	// The virtual network interface card (vNIC) type that is bound to the
 	// neutron port
-	VNICType string
+	VNICType string `json:"binding:vnic_type,omitempty"`
 	// A dictionary that enables the application running on the specified
 	// host to pass and receive virtual network interface (VIF) port-specific
 	// information to the plug-in
-	Profile map[string]string
+	Profile map[string]string `json:"binding:profile,omitempty"`
 }
 
 // ToPortUpdateMap casts an UpdateOpts struct to a map.
 func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) {
-	var port map[string]interface{}
-	if opts.UpdateOptsBuilder != nil {
-		p, err := opts.UpdateOptsBuilder.ToPortUpdateMap()
-		if err != nil {
-			return nil, err
-		}
-
-		port = p["port"].(map[string]interface{})
+	b1, err := opts.UpdateOptsBuilder.ToPortUpdateMap()
+	if err != nil {
+		return nil, err
 	}
 
-	if port == nil {
-		port = make(map[string]interface{})
+	b2, err := gophercloud.BuildRequestBody(opts, "")
+	if err != nil {
+		return nil, err
 	}
 
-	if opts.HostID != "" {
-		port["binding:host_id"] = opts.HostID
-	}
-	if opts.VNICType != "" {
-		port["binding:vnic_type"] = opts.VNICType
-	}
-	if opts.Profile != nil {
-		port["binding:profile"] = opts.Profile
+	port := b1["port"].(map[string]interface{})
+
+	for k, v := range b2 {
+		port[k] = v
 	}
 
 	return map[string]interface{}{"port": port}, nil
@@ -113,17 +100,14 @@
 
 // Update accepts a UpdateOpts struct and updates an existing port using the
 // values provided.
-func Update(c *gophercloud.ServiceClient, id string, opts ports.UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToPortUpdateMap()
+func Update(c *gophercloud.ServiceClient, id string, opts ports.UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToPortUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = c.Put(updateURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201},
 	})
-	return res
+	return
 }
diff --git a/openstack/networking/v2/extensions/portsbinding/requests_test.go b/openstack/networking/v2/extensions/portsbinding/requests_test.go
deleted file mode 100644
index a226031..0000000
--- a/openstack/networking/v2/extensions/portsbinding/requests_test.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package portsbinding
-
-import (
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleListSuccessfully(t)
-
-	count := 0
-
-	ports.List(fake.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractPorts(page)
-		th.AssertNoErr(t, err)
-
-		expected := []Port{
-			Port{
-				Port: ports.Port{
-					Status:       "ACTIVE",
-					Name:         "",
-					AdminStateUp: true,
-					NetworkID:    "70c1db1f-b701-45bd-96e0-a313ee3430b3",
-					TenantID:     "",
-					DeviceOwner:  "network:router_gateway",
-					MACAddress:   "fa:16:3e:58:42:ed",
-					FixedIPs: []ports.IP{
-						ports.IP{
-							SubnetID:  "008ba151-0b8c-4a67-98b5-0d2b87666062",
-							IPAddress: "172.24.4.2",
-						},
-					},
-					ID:             "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
-					SecurityGroups: []string{},
-					DeviceID:       "9ae135f4-b6e0-4dad-9e91-3c223e385824",
-				},
-				VNICType: "normal",
-				HostID:   "devstack",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleGet(t)
-
-	n, err := Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertEquals(t, n.Name, "")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
-	th.AssertEquals(t, n.TenantID, "7e02058126cc4950b75f9970368ba177")
-	th.AssertEquals(t, n.DeviceOwner, "network:router_interface")
-	th.AssertEquals(t, n.MACAddress, "fa:16:3e:23:fd:d7")
-	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
-		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"},
-	})
-	th.AssertEquals(t, n.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2")
-	th.AssertDeepEquals(t, n.SecurityGroups, []string{})
-	th.AssertEquals(t, n.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e")
-
-	th.AssertEquals(t, n.HostID, "devstack")
-	th.AssertEquals(t, n.VNICType, "normal")
-	th.AssertEquals(t, n.VIFType, "ovs")
-	th.AssertDeepEquals(t, n.VIFDetails, map[string]interface{}{"port_filter": true, "ovs_hybrid_plug": true})
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleCreate(t)
-
-	asu := true
-	options := CreateOpts{
-		CreateOptsBuilder: ports.CreateOpts{
-			Name:         "private-port",
-			AdminStateUp: &asu,
-			NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-			FixedIPs: []ports.IP{
-				{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
-			},
-			SecurityGroups: []string{"foo"},
-		},
-		HostID:   "HOST1",
-		VNICType: "normal",
-	}
-	n, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Status, "DOWN")
-	th.AssertEquals(t, n.Name, "private-port")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
-	th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
-	th.AssertEquals(t, n.DeviceOwner, "")
-	th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0")
-	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
-		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
-	})
-	th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
-	th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
-	th.AssertEquals(t, n.HostID, "HOST1")
-	th.AssertEquals(t, n.VNICType, "normal")
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), CreateOpts{CreateOptsBuilder: ports.CreateOpts{}})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	HandleUpdate(t)
-
-	options := UpdateOpts{
-		UpdateOptsBuilder: ports.UpdateOpts{
-			Name: "new_port_name",
-			FixedIPs: []ports.IP{
-				{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
-			},
-			SecurityGroups: []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"},
-		},
-		HostID:   "HOST1",
-		VNICType: "normal",
-	}
-
-	s, err := Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.Name, "new_port_name")
-	th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
-		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
-	})
-	th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
-	th.AssertEquals(t, s.HostID, "HOST1")
-	th.AssertEquals(t, s.VNICType, "normal")
-}
diff --git a/openstack/networking/v2/extensions/portsbinding/results.go b/openstack/networking/v2/extensions/portsbinding/results.go
index 356e51c..9527473 100644
--- a/openstack/networking/v2/extensions/portsbinding/results.go
+++ b/openstack/networking/v2/extensions/portsbinding/results.go
@@ -1,11 +1,10 @@
 package portsbinding
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 
-	"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 type commonResult struct {
@@ -14,17 +13,11 @@
 
 // Extract is a function that accepts a result and extracts a port resource.
 func (r commonResult) Extract() (*Port, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Port *Port `json:"port"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Port, err
+	err := r.ExtractInto(&s)
+	return s.Port, err
 }
 
 // CreateResult represents the result of a create operation.
@@ -44,38 +37,37 @@
 
 // IP is a sub-struct that represents an individual IP.
 type IP struct {
-	SubnetID  string `mapstructure:"subnet_id" json:"subnet_id"`
-	IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"`
+	SubnetID  string `json:"subnet_id"`
+	IPAddress string `json:"ip_address"`
 }
 
 // Port represents a Neutron port. See package documentation for a top-level
 // description of what this is.
 type Port struct {
-	ports.Port `mapstructure:",squash"`
+	ports.Port
 	// The ID of the host where the port is allocated
-	HostID string `mapstructure:"binding:host_id" json:"binding:host_id"`
+	HostID string `json:"binding:host_id"`
 	// A dictionary that enables the application to pass information about
 	// functions that the Networking API provides.
-	VIFDetails map[string]interface{} `mapstructure:"binding:vif_details" json:"binding:vif_details"`
+	VIFDetails map[string]interface{} `json:"binding:vif_details"`
 	// The VIF type for the port.
-	VIFType string `mapstructure:"binding:vif_type" json:"binding:vif_type"`
+	VIFType string `json:"binding:vif_type"`
 	// The virtual network interface card (vNIC) type that is bound to the
 	// neutron port
-	VNICType string `mapstructure:"binding:vnic_type" json:"binding:vnic_type"`
+	VNICType string `json:"binding:vnic_type"`
 	// A dictionary that enables the application running on the specified
 	// host to pass and receive virtual network interface (VIF) port-specific
 	// information to the plug-in
-	Profile map[string]string `mapstructure:"binding:profile" json:"binding:profile"`
+	Profile map[string]string `json:"binding:profile"`
 }
 
 // ExtractPorts accepts a Page struct, specifically a PortPage struct,
 // and extracts the elements into a slice of Port structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractPorts(page pagination.Page) ([]Port, error) {
-	var resp struct {
-		Ports []Port `mapstructure:"ports" json:"ports"`
+func ExtractPorts(r pagination.Page) ([]Port, error) {
+	var s struct {
+		Ports []Port `json:"ports"`
 	}
-
-	err := mapstructure.Decode(page.(ports.PortPage).Body, &resp)
-	return resp.Ports, err
+	err := (r.(ports.PortPage)).ExtractInto(&s)
+	return s.Ports, err
 }
diff --git a/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go b/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go
new file mode 100644
index 0000000..3231f66
--- /dev/null
+++ b/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go
@@ -0,0 +1,206 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func HandleListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "ports": [
+        {
+            "status": "ACTIVE",
+            "binding:host_id": "devstack",
+            "name": "",
+            "admin_state_up": true,
+            "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3",
+            "tenant_id": "",
+            "device_owner": "network:router_gateway",
+            "mac_address": "fa:16:3e:58:42:ed",
+            "fixed_ips": [
+                {
+                    "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062",
+                    "ip_address": "172.24.4.2"
+                }
+            ],
+            "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
+            "security_groups": [],
+            "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824",
+            "binding:vnic_type": "normal"
+        }
+    ]
+}
+      `)
+	})
+}
+
+func HandleGet(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "port": {
+        "status": "ACTIVE",
+        "binding:host_id": "devstack",
+        "name": "",
+        "allowed_address_pairs": [],
+        "admin_state_up": true,
+        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+        "tenant_id": "7e02058126cc4950b75f9970368ba177",
+        "extra_dhcp_opts": [],
+        "binding:vif_details": {
+            "port_filter": true,
+            "ovs_hybrid_plug": true
+        },
+        "binding:vif_type": "ovs",
+        "device_owner": "network:router_interface",
+        "port_security_enabled": false,
+        "mac_address": "fa:16:3e:23:fd:d7",
+        "binding:profile": {},
+        "binding:vnic_type": "normal",
+        "fixed_ips": [
+            {
+                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+                "ip_address": "10.0.0.1"
+            }
+        ],
+        "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2",
+        "security_groups": [],
+        "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e"
+    }
+}
+    `)
+	})
+}
+
+func HandleCreate(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "port": {
+        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+        "name": "private-port",
+        "admin_state_up": true,
+		"fixed_ips": [
+				{
+						"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+						"ip_address": "10.0.0.2"
+				}
+		],
+		"security_groups": ["foo"],
+		"binding:host_id": "HOST1",
+        "binding:vnic_type": "normal"
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "port": {
+        "status": "DOWN",
+        "name": "private-port",
+        "allowed_address_pairs": [],
+        "admin_state_up": true,
+        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+        "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
+        "device_owner": "",
+        "mac_address": "fa:16:3e:c9:cb:f0",
+        "fixed_ips": [
+            {
+                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+                "ip_address": "10.0.0.2"
+            }
+        ],
+        "binding:host_id": "HOST1",
+        "binding:vnic_type": "normal",
+        "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
+        "security_groups": [
+            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
+        ],
+        "device_id": ""
+    }
+}
+		`)
+	})
+}
+
+func HandleUpdate(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+		"port": {
+			"name": "new_port_name",
+			"fixed_ips": [
+				{
+					"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+					"ip_address": "10.0.0.3"
+				}
+			],
+			"security_groups": [
+            	"f0ac4394-7e4a-4409-9701-ba8be283dbc3"
+        	],
+        	"binding:host_id": "HOST1",
+        	"binding:vnic_type": "normal"
+		}
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "port": {
+        "status": "DOWN",
+        "name": "new_port_name",
+        "admin_state_up": true,
+        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+        "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
+        "device_owner": "",
+        "mac_address": "fa:16:3e:c9:cb:f0",
+        "fixed_ips": [
+            {
+                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+                "ip_address": "10.0.0.3"
+            }
+        ],
+        "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
+        "security_groups": [
+            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
+        ],
+        "device_id": "",
+        "binding:host_id": "HOST1",
+        "binding:vnic_type": "normal"
+    }
+}
+		`)
+	})
+}
diff --git a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go
new file mode 100644
index 0000000..f41f1cc
--- /dev/null
+++ b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go
@@ -0,0 +1,164 @@
+package testing
+
+import (
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleListSuccessfully(t)
+
+	count := 0
+
+	ports.List(fake.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := portsbinding.ExtractPorts(page)
+		th.AssertNoErr(t, err)
+
+		expected := []portsbinding.Port{
+			{
+				Port: ports.Port{
+					Status:       "ACTIVE",
+					Name:         "",
+					AdminStateUp: true,
+					NetworkID:    "70c1db1f-b701-45bd-96e0-a313ee3430b3",
+					TenantID:     "",
+					DeviceOwner:  "network:router_gateway",
+					MACAddress:   "fa:16:3e:58:42:ed",
+					FixedIPs: []ports.IP{
+						{
+							SubnetID:  "008ba151-0b8c-4a67-98b5-0d2b87666062",
+							IPAddress: "172.24.4.2",
+						},
+					},
+					ID:             "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
+					SecurityGroups: []string{},
+					DeviceID:       "9ae135f4-b6e0-4dad-9e91-3c223e385824",
+				},
+				VNICType: "normal",
+				HostID:   "devstack",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleGet(t)
+
+	n, err := portsbinding.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Status, "ACTIVE")
+	th.AssertEquals(t, n.Name, "")
+	th.AssertEquals(t, n.AdminStateUp, true)
+	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
+	th.AssertEquals(t, n.TenantID, "7e02058126cc4950b75f9970368ba177")
+	th.AssertEquals(t, n.DeviceOwner, "network:router_interface")
+	th.AssertEquals(t, n.MACAddress, "fa:16:3e:23:fd:d7")
+	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
+		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"},
+	})
+	th.AssertEquals(t, n.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2")
+	th.AssertDeepEquals(t, n.SecurityGroups, []string{})
+	th.AssertEquals(t, n.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e")
+
+	th.AssertEquals(t, n.HostID, "devstack")
+	th.AssertEquals(t, n.VNICType, "normal")
+	th.AssertEquals(t, n.VIFType, "ovs")
+	th.AssertDeepEquals(t, n.VIFDetails, map[string]interface{}{"port_filter": true, "ovs_hybrid_plug": true})
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleCreate(t)
+
+	asu := true
+	options := portsbinding.CreateOpts{
+		CreateOptsBuilder: ports.CreateOpts{
+			Name:         "private-port",
+			AdminStateUp: &asu,
+			NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+			FixedIPs: []ports.IP{
+				{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
+			},
+			SecurityGroups: []string{"foo"},
+		},
+		HostID:   "HOST1",
+		VNICType: "normal",
+	}
+	n, err := portsbinding.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Status, "DOWN")
+	th.AssertEquals(t, n.Name, "private-port")
+	th.AssertEquals(t, n.AdminStateUp, true)
+	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
+	th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
+	th.AssertEquals(t, n.DeviceOwner, "")
+	th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0")
+	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
+		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
+	})
+	th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
+	th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
+	th.AssertEquals(t, n.HostID, "HOST1")
+	th.AssertEquals(t, n.VNICType, "normal")
+}
+
+func TestRequiredCreateOpts(t *testing.T) {
+	res := portsbinding.Create(fake.ServiceClient(), portsbinding.CreateOpts{CreateOptsBuilder: ports.CreateOpts{}})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	HandleUpdate(t)
+
+	options := portsbinding.UpdateOpts{
+		UpdateOptsBuilder: ports.UpdateOpts{
+			Name: "new_port_name",
+			FixedIPs: []ports.IP{
+				{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
+			},
+			SecurityGroups: []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"},
+		},
+		HostID:   "HOST1",
+		VNICType: "normal",
+	}
+
+	s, err := portsbinding.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, s.Name, "new_port_name")
+	th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
+		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
+	})
+	th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
+	th.AssertEquals(t, s.HostID, "HOST1")
+	th.AssertEquals(t, s.VNICType, "normal")
+}
diff --git a/openstack/networking/v2/extensions/portsbinding/urls.go b/openstack/networking/v2/extensions/portsbinding/urls.go
index 55307f4..a531a7e 100644
--- a/openstack/networking/v2/extensions/portsbinding/urls.go
+++ b/openstack/networking/v2/extensions/portsbinding/urls.go
@@ -1,6 +1,6 @@
 package portsbinding
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func resourceURL(c *gophercloud.ServiceClient, id string) string {
 	return c.ServiceURL("ports", id)
diff --git a/openstack/networking/v2/extensions/portsbinding/urls_test.go b/openstack/networking/v2/extensions/portsbinding/urls_test.go
deleted file mode 100644
index f9359ce..0000000
--- a/openstack/networking/v2/extensions/portsbinding/urls_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package portsbinding
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint, ResourceBase: endpoint + "v2.0/"}
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "v2.0/ports/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "v2.0/ports"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient(), "foo")
-	expected := endpoint + "v2.0/ports/foo"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/networking/v2/extensions/provider/results.go b/openstack/networking/v2/extensions/provider/results.go
index f07d628..229013b 100755
--- a/openstack/networking/v2/extensions/provider/results.go
+++ b/openstack/networking/v2/extensions/provider/results.go
@@ -1,51 +1,37 @@
 package provider
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // NetworkExtAttrs represents an extended form of a Network with additional fields.
 type NetworkExtAttrs struct {
 	// UUID for the network
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 
 	// Human-readable name for the network. Might not be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 
 	// The administrative state of network. If false (down), the network does not forward packets.
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Indicates whether network is currently operational. Possible values include
 	// `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values.
-	Status string `mapstructure:"status" json:"status"`
+	Status string `json:"status"`
 
 	// Subnets associated with this network.
-	Subnets []string `mapstructure:"subnets" json:"subnets"`
+	Subnets []string `json:"subnets"`
 
 	// Owner of network. Only admin users can specify a tenant_id other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// Specifies whether the network resource can be accessed by any tenant or not.
-	Shared bool `mapstructure:"shared" json:"shared"`
+	Shared bool `json:"shared"`
 
 	// Specifies the nature of the physical network mapped to this network
 	// resource. Examples are flat, vlan, or gre.
-	NetworkType string `json:"provider:network_type" mapstructure:"provider:network_type"`
+	NetworkType string `json:"provider:network_type"`
 
 	// Identifies the physical network on top of which this network object is
 	// being implemented. The OpenStack Networking API does not expose any facility
@@ -53,72 +39,52 @@
 	// the Open vSwitch plug-in this is a symbolic name which is then mapped to
 	// specific bridges on each compute host through the Open vSwitch plug-in
 	// configuration file.
-	PhysicalNetwork string `json:"provider:physical_network" mapstructure:"provider:physical_network"`
+	PhysicalNetwork string `json:"provider:physical_network"`
 
 	// Identifies an isolated segment on the physical network; the nature of the
 	// segment depends on the segmentation model defined by network_type. For
 	// instance, if network_type is vlan, then this is a vlan identifier;
 	// otherwise, if network_type is gre, then this will be a gre key.
-	SegmentationID string `json:"provider:segmentation_id" mapstructure:"provider:segmentation_id"`
+	SegmentationID string `json:"provider:segmentation_id"`
 }
 
 // ExtractGet decorates a GetResult struct returned from a networks.Get()
 // function with extended attributes.
 func ExtractGet(r networks.GetResult) (*NetworkExtAttrs, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Network *NetworkExtAttrs `json:"network"`
 	}
-
-	err := mapstructure.WeakDecode(r.Body, &res)
-
-	return res.Network, err
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractCreate decorates a CreateResult struct returned from a networks.Create()
 // function with extended attributes.
 func ExtractCreate(r networks.CreateResult) (*NetworkExtAttrs, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Network *NetworkExtAttrs `json:"network"`
 	}
-
-	err := mapstructure.WeakDecode(r.Body, &res)
-
-	return res.Network, err
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractUpdate decorates a UpdateResult struct returned from a
 // networks.Update() function with extended attributes.
 func ExtractUpdate(r networks.UpdateResult) (*NetworkExtAttrs, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Network *NetworkExtAttrs `json:"network"`
 	}
-
-	err := mapstructure.WeakDecode(r.Body, &res)
-
-	return res.Network, err
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // ExtractList accepts a Page struct, specifically a NetworkPage struct, and
 // extracts the elements into a slice of NetworkExtAttrs structs. In other
 // words, a generic collection is mapped into a relevant slice.
-func ExtractList(page pagination.Page) ([]NetworkExtAttrs, error) {
-	var resp struct {
-		Networks []NetworkExtAttrs `mapstructure:"networks" json:"networks"`
+func ExtractList(r pagination.Page) ([]NetworkExtAttrs, error) {
+	var s struct {
+		Networks []NetworkExtAttrs `json:"networks" json:"networks"`
 	}
-
-	err := mapstructure.WeakDecode(page.(networks.NetworkPage).Body, &resp)
-
-	return resp.Networks, err
+	err := (r.(networks.NetworkPage)).ExtractInto(&s)
+	return s.Networks, err
 }
diff --git a/openstack/networking/v2/extensions/provider/results_test.go b/openstack/networking/v2/extensions/provider/results_test.go
deleted file mode 100644
index 8081692..0000000
--- a/openstack/networking/v2/extensions/provider/results_test.go
+++ /dev/null
@@ -1,253 +0,0 @@
-package provider
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "networks": [
-        {
-            "status": "ACTIVE",
-            "subnets": [
-                "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-            ],
-            "name": "private-network",
-            "admin_state_up": true,
-            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-            "shared": true,
-            "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-            "provider:segmentation_id": null,
-            "provider:physical_network": null,
-            "provider:network_type": "local"
-        },
-        {
-            "status": "ACTIVE",
-            "subnets": [
-                "08eae331-0402-425a-923c-34f7cfe39c1b"
-            ],
-            "name": "private",
-            "admin_state_up": true,
-            "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
-            "shared": true,
-            "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-            "provider:segmentation_id": 1234567890,
-            "provider:physical_network": null,
-            "provider:network_type": "local"
-        }
-    ]
-}
-			`)
-	})
-
-	count := 0
-
-	networks.List(fake.ServiceClient(), networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractList(page)
-		if err != nil {
-			t.Errorf("Failed to extract networks: %v", err)
-			return false, err
-		}
-
-		expected := []NetworkExtAttrs{
-			NetworkExtAttrs{
-				Status:          "ACTIVE",
-				Subnets:         []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"},
-				Name:            "private-network",
-				AdminStateUp:    true,
-				TenantID:        "4fd44f30292945e481c7b8a0c8908869",
-				Shared:          true,
-				ID:              "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-				NetworkType:     "local",
-				PhysicalNetwork: "",
-				SegmentationID:  "",
-			},
-			NetworkExtAttrs{
-				Status:          "ACTIVE",
-				Subnets:         []string{"08eae331-0402-425a-923c-34f7cfe39c1b"},
-				Name:            "private",
-				AdminStateUp:    true,
-				TenantID:        "26a7980765d0414dbc1fc1f88cdb7e6e",
-				Shared:          true,
-				ID:              "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-				NetworkType:     "local",
-				PhysicalNetwork: "",
-				SegmentationID:  "1234567890",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "network": {
-        "status": "ACTIVE",
-        "subnets": [
-            "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-        ],
-        "name": "private-network",
-        "provider:physical_network": null,
-        "admin_state_up": true,
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "provider:network_type": "local",
-        "shared": true,
-        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-        "provider:segmentation_id": null
-    }
-}
-			`)
-	})
-
-	res := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	n, err := ExtractGet(res)
-
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "", n.PhysicalNetwork)
-	th.AssertEquals(t, "local", n.NetworkType)
-	th.AssertEquals(t, "", n.SegmentationID)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "network": {
-        "name": "sample_network",
-        "admin_state_up": true
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "network": {
-        "status": "ACTIVE",
-        "subnets": [
-            "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-        ],
-        "name": "private-network",
-        "provider:physical_network": null,
-        "admin_state_up": true,
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "provider:network_type": "local",
-        "shared": true,
-        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-        "provider:segmentation_id": null
-    }
-}
-		`)
-	})
-
-	options := networks.CreateOpts{Name: "sample_network", AdminStateUp: Up}
-	res := networks.Create(fake.ServiceClient(), options)
-	n, err := ExtractCreate(res)
-
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "", n.PhysicalNetwork)
-	th.AssertEquals(t, "local", n.NetworkType)
-	th.AssertEquals(t, "", n.SegmentationID)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-		"network": {
-				"name": "new_network_name",
-				"admin_state_up": false,
-				"shared": true
-		}
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "network": {
-        "status": "ACTIVE",
-        "subnets": [
-            "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-        ],
-        "name": "private-network",
-        "provider:physical_network": null,
-        "admin_state_up": true,
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "provider:network_type": "local",
-        "shared": true,
-        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-        "provider:segmentation_id": null
-    }
-}
-		`)
-	})
-
-	iTrue := true
-	options := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: Down, Shared: &iTrue}
-	res := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options)
-	n, err := ExtractUpdate(res)
-
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "", n.PhysicalNetwork)
-	th.AssertEquals(t, "local", n.NetworkType)
-	th.AssertEquals(t, "", n.SegmentationID)
-}
diff --git a/openstack/networking/v2/extensions/provider/testing/doc.go b/openstack/networking/v2/extensions/provider/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/provider/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/provider/testing/results_test.go b/openstack/networking/v2/extensions/provider/testing/results_test.go
new file mode 100644
index 0000000..3b6a6fd
--- /dev/null
+++ b/openstack/networking/v2/extensions/provider/testing/results_test.go
@@ -0,0 +1,255 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/provider"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "networks": [
+        {
+            "status": "ACTIVE",
+            "subnets": [
+                "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+            ],
+            "name": "private-network",
+            "admin_state_up": true,
+            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+            "shared": true,
+            "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+            "provider:segmentation_id": null,
+            "provider:physical_network": null,
+            "provider:network_type": "local"
+        },
+        {
+            "status": "ACTIVE",
+            "subnets": [
+                "08eae331-0402-425a-923c-34f7cfe39c1b"
+            ],
+            "name": "private",
+            "admin_state_up": true,
+            "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
+            "shared": true,
+            "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+            "provider:segmentation_id": "1234567890",
+            "provider:physical_network": null,
+            "provider:network_type": "local"
+        }
+    ]
+}
+			`)
+	})
+
+	count := 0
+
+	networks.List(fake.ServiceClient(), networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := provider.ExtractList(page)
+		if err != nil {
+			t.Errorf("Failed to extract networks: %v", err)
+			return false, err
+		}
+
+		expected := []provider.NetworkExtAttrs{
+			{
+				Status:          "ACTIVE",
+				Subnets:         []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"},
+				Name:            "private-network",
+				AdminStateUp:    true,
+				TenantID:        "4fd44f30292945e481c7b8a0c8908869",
+				Shared:          true,
+				ID:              "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+				NetworkType:     "local",
+				PhysicalNetwork: "",
+				SegmentationID:  "",
+			},
+			{
+				Status:          "ACTIVE",
+				Subnets:         []string{"08eae331-0402-425a-923c-34f7cfe39c1b"},
+				Name:            "private",
+				AdminStateUp:    true,
+				TenantID:        "26a7980765d0414dbc1fc1f88cdb7e6e",
+				Shared:          true,
+				ID:              "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+				NetworkType:     "local",
+				PhysicalNetwork: "",
+				SegmentationID:  "1234567890",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "network": {
+        "status": "ACTIVE",
+        "subnets": [
+            "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+        ],
+        "name": "private-network",
+        "provider:physical_network": null,
+        "admin_state_up": true,
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "provider:network_type": "local",
+        "shared": true,
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+        "provider:segmentation_id": null
+    }
+}
+			`)
+	})
+
+	res := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	n, err := provider.ExtractGet(res)
+
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "", n.PhysicalNetwork)
+	th.AssertEquals(t, "local", n.NetworkType)
+	th.AssertEquals(t, "", n.SegmentationID)
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "network": {
+        "name": "sample_network",
+        "admin_state_up": true
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "network": {
+        "status": "ACTIVE",
+        "subnets": [
+            "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+        ],
+        "name": "private-network",
+        "provider:physical_network": null,
+        "admin_state_up": true,
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "provider:network_type": "local",
+        "shared": true,
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+        "provider:segmentation_id": null
+    }
+}
+		`)
+	})
+
+	options := networks.CreateOpts{Name: "sample_network", AdminStateUp: gophercloud.Enabled}
+	res := networks.Create(fake.ServiceClient(), options)
+	n, err := provider.ExtractCreate(res)
+
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "", n.PhysicalNetwork)
+	th.AssertEquals(t, "local", n.NetworkType)
+	th.AssertEquals(t, "", n.SegmentationID)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+		"network": {
+				"name": "new_network_name",
+				"admin_state_up": false,
+				"shared": true
+		}
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "network": {
+        "status": "ACTIVE",
+        "subnets": [
+            "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+        ],
+        "name": "private-network",
+        "provider:physical_network": null,
+        "admin_state_up": true,
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "provider:network_type": "local",
+        "shared": true,
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+        "provider:segmentation_id": null
+    }
+}
+		`)
+	})
+
+	iTrue := true
+	options := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: gophercloud.Disabled, Shared: &iTrue}
+	res := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options)
+	n, err := provider.ExtractUpdate(res)
+
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "", n.PhysicalNetwork)
+	th.AssertEquals(t, "local", n.NetworkType)
+	th.AssertEquals(t, "", n.SegmentationID)
+}
diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go
index 2712ac1..5ca4850 100644
--- a/openstack/networking/v2/extensions/security/groups/requests.go
+++ b/openstack/networking/v2/extensions/security/groups/requests.go
@@ -1,10 +1,8 @@
 package groups
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOpts allows the filtering and sorting of paginated collections through
@@ -36,96 +34,75 @@
 	})
 }
 
-var (
-	errNameRequired = fmt.Errorf("Name is required")
-)
+type CreateOptsBuilder interface {
+	ToSecGroupCreateMap() (map[string]interface{}, error)
+}
 
 // CreateOpts contains all the values needed to create a new security group.
 type CreateOpts struct {
 	// Required. Human-readable name for the VIP. Does not have to be unique.
-	Name string
-
+	Name string `json:"name" required:"true"`
 	// Required for admins. Indicates the owner of the VIP.
-	TenantID string
-
+	TenantID string `json:"tenant_id,omitempty"`
 	// Optional. Describes the security group.
-	Description string
+	Description string `json:"description,omitempty"`
+}
+
+func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "security_group")
 }
 
 // Create is an operation which provisions a new security group with default
 // security group rules for the IPv4 and IPv6 ether types.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	var res CreateResult
-
-	// Validate required opts
-	if opts.Name == "" {
-		res.Err = errNameRequired
-		return res
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToSecGroupCreateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-
-	type secgroup struct {
-		Name        string `json:"name"`
-		TenantID    string `json:"tenant_id,omitempty"`
-		Description string `json:"description,omitempty"`
-	}
-
-	type request struct {
-		SecGroup secgroup `json:"security_group"`
-	}
-
-	reqBody := request{SecGroup: secgroup{
-		Name:        opts.Name,
-		TenantID:    opts.TenantID,
-		Description: opts.Description,
-	}}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular security group based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
 }
 
 // Delete will permanently delete a particular security group based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
 
 // IDFromName is a convenience function that returns a security group's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
-	securityGroupCount := 0
-	securityGroupID := ""
-	if name == "" {
-		return "", fmt.Errorf("A security group name must be provided.")
+	count := 0
+	id := ""
+	pages, err := List(client, ListOpts{}).AllPages()
+	if err != nil {
+		return "", err
 	}
-	pager := List(client, ListOpts{})
-	pager.EachPage(func(page pagination.Page) (bool, error) {
-		securityGroupList, err := ExtractGroups(page)
-		if err != nil {
-			return false, err
-		}
 
-		for _, s := range securityGroupList {
-			if s.Name == name {
-				securityGroupCount++
-				securityGroupID = s.ID
-			}
-		}
-		return true, nil
-	})
+	all, err := ExtractGroups(pages)
+	if err != nil {
+		return "", err
+	}
 
-	switch securityGroupCount {
+	for _, s := range all {
+		if s.Name == name {
+			count++
+			id = s.ID
+		}
+	}
+
+	switch count {
 	case 0:
-		return "", fmt.Errorf("Unable to find security group: %s", name)
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "security group"}
 	case 1:
-		return securityGroupID, nil
+		return id, nil
 	default:
-		return "", fmt.Errorf("Found %d security groups matching %s", securityGroupCount, name)
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "security group"}
 	}
 }
diff --git a/openstack/networking/v2/extensions/security/groups/requests_test.go b/openstack/networking/v2/extensions/security/groups/requests_test.go
deleted file mode 100644
index 5f074c7..0000000
--- a/openstack/networking/v2/extensions/security/groups/requests_test.go
+++ /dev/null
@@ -1,213 +0,0 @@
-package groups
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/security-groups", rootURL(fake.ServiceClient()))
-	th.AssertEquals(t, th.Endpoint()+"v2.0/security-groups/foo", resourceURL(fake.ServiceClient(), "foo"))
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-groups", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "security_groups": [
-        {
-            "description": "default",
-            "id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-            "name": "default",
-            "security_group_rules": [],
-            "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-        }
-    ]
-}
-      `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractGroups(page)
-		if err != nil {
-			t.Errorf("Failed to extract secgroups: %v", err)
-			return false, err
-		}
-
-		expected := []SecGroup{
-			SecGroup{
-				Description: "default",
-				ID:          "85cc3048-abc3-43cc-89b3-377341426ac5",
-				Name:        "default",
-				Rules:       []rules.SecGroupRule{},
-				TenantID:    "e4f50856753b4dc6afee5fa6b9b6c550",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-groups", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "security_group": {
-        "name": "new-webservers",
-        "description": "security group for webservers"
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "security_group": {
-        "description": "security group for webservers",
-        "id": "2076db17-a522-4506-91de-c6dd8e837028",
-        "name": "new-webservers",
-        "security_group_rules": [
-            {
-                "direction": "egress",
-                "ethertype": "IPv4",
-                "id": "38ce2d8e-e8f1-48bd-83c2-d33cb9f50c3d",
-                "port_range_max": null,
-                "port_range_min": null,
-                "protocol": null,
-                "remote_group_id": null,
-                "remote_ip_prefix": null,
-                "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028",
-                "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-            },
-            {
-                "direction": "egress",
-                "ethertype": "IPv6",
-                "id": "565b9502-12de-4ffd-91e9-68885cff6ae1",
-                "port_range_max": null,
-                "port_range_min": null,
-                "protocol": null,
-                "remote_group_id": null,
-                "remote_ip_prefix": null,
-                "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028",
-                "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-            }
-        ],
-        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-    }
-}
-    `)
-	})
-
-	opts := CreateOpts{Name: "new-webservers", Description: "security group for webservers"}
-	_, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-groups/85cc3048-abc3-43cc-89b3-377341426ac5", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "security_group": {
-        "description": "default",
-        "id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-        "name": "default",
-        "security_group_rules": [
-            {
-                "direction": "egress",
-                "ethertype": "IPv6",
-                "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
-                "port_range_max": null,
-                "port_range_min": null,
-                "protocol": null,
-                "remote_group_id": null,
-                "remote_ip_prefix": null,
-                "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-                "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-            },
-            {
-                "direction": "egress",
-                "ethertype": "IPv4",
-                "id": "93aa42e5-80db-4581-9391-3a608bd0e448",
-                "port_range_max": null,
-                "port_range_min": null,
-                "protocol": null,
-                "remote_group_id": null,
-                "remote_ip_prefix": null,
-                "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-                "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-            }
-        ],
-        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-    }
-}
-      `)
-	})
-
-	sg, err := Get(fake.ServiceClient(), "85cc3048-abc3-43cc-89b3-377341426ac5").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "default", sg.Description)
-	th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sg.ID)
-	th.AssertEquals(t, "default", sg.Name)
-	th.AssertEquals(t, 2, len(sg.Rules))
-	th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sg.TenantID)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-groups/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/extensions/security/groups/results.go b/openstack/networking/v2/extensions/security/groups/results.go
index 49db261..ea3ad65 100644
--- a/openstack/networking/v2/extensions/security/groups/results.go
+++ b/openstack/networking/v2/extensions/security/groups/results.go
@@ -1,10 +1,9 @@
 package groups
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // SecGroup represents a container for security group rules.
@@ -21,11 +20,11 @@
 
 	// A slice of security group rules that dictate the permitted behaviour for
 	// traffic entering and leaving the group.
-	Rules []rules.SecGroupRule `json:"security_group_rules" mapstructure:"security_group_rules"`
+	Rules []rules.SecGroupRule `json:"security_group_rules"`
 
 	// Owner of the security group. Only admin users can specify a TenantID
 	// other than their own.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 }
 
 // SecGroupPage is the page returned by a pager when traversing over a
@@ -37,40 +36,33 @@
 // NextPageURL is invoked when a paginated collection of security groups has
 // reached the end of a page and the pager seeks to traverse over a new one. In
 // order to do this, it needs to construct the next page's URL.
-func (p SecGroupPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"security_groups_links"`
+func (r SecGroupPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"security_groups_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
 
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a SecGroupPage struct is empty.
-func (p SecGroupPage) IsEmpty() (bool, error) {
-	is, err := ExtractGroups(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r SecGroupPage) IsEmpty() (bool, error) {
+	is, err := ExtractGroups(r)
+	return len(is) == 0, err
 }
 
 // ExtractGroups accepts a Page struct, specifically a SecGroupPage struct,
 // and extracts the elements into a slice of SecGroup structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractGroups(page pagination.Page) ([]SecGroup, error) {
-	var resp struct {
-		SecGroups []SecGroup `mapstructure:"security_groups" json:"security_groups"`
+func ExtractGroups(r pagination.Page) ([]SecGroup, error) {
+	var s struct {
+		SecGroups []SecGroup `json:"security_groups"`
 	}
-
-	err := mapstructure.Decode(page.(SecGroupPage).Body, &resp)
-
-	return resp.SecGroups, err
+	err := (r.(SecGroupPage)).ExtractInto(&s)
+	return s.SecGroups, err
 }
 
 type commonResult struct {
@@ -79,17 +71,11 @@
 
 // Extract is a function that accepts a result and extracts a security group.
 func (r commonResult) Extract() (*SecGroup, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		SecGroup *SecGroup `json:"security_group"`
 	}
-
-	var res struct {
-		SecGroup *SecGroup `mapstructure:"security_group" json:"security_group"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.SecGroup, err
+	err := r.ExtractInto(&s)
+	return s.SecGroup, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/security/groups/testing/doc.go b/openstack/networking/v2/extensions/security/groups/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/security/groups/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/security/groups/testing/requests_test.go b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go
new file mode 100644
index 0000000..acd3230
--- /dev/null
+++ b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go
@@ -0,0 +1,206 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/security-groups", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "security_groups": [
+        {
+            "description": "default",
+            "id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+            "name": "default",
+            "security_group_rules": [],
+            "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+        }
+    ]
+}
+      `)
+	})
+
+	count := 0
+
+	groups.List(fake.ServiceClient(), groups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := groups.ExtractGroups(page)
+		if err != nil {
+			t.Errorf("Failed to extract secgroups: %v", err)
+			return false, err
+		}
+
+		expected := []groups.SecGroup{
+			{
+				Description: "default",
+				ID:          "85cc3048-abc3-43cc-89b3-377341426ac5",
+				Name:        "default",
+				Rules:       []rules.SecGroupRule{},
+				TenantID:    "e4f50856753b4dc6afee5fa6b9b6c550",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/security-groups", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "security_group": {
+        "name": "new-webservers",
+        "description": "security group for webservers"
+    }
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "security_group": {
+        "description": "security group for webservers",
+        "id": "2076db17-a522-4506-91de-c6dd8e837028",
+        "name": "new-webservers",
+        "security_group_rules": [
+            {
+                "direction": "egress",
+                "ethertype": "IPv4",
+                "id": "38ce2d8e-e8f1-48bd-83c2-d33cb9f50c3d",
+                "port_range_max": null,
+                "port_range_min": null,
+                "protocol": null,
+                "remote_group_id": null,
+                "remote_ip_prefix": null,
+                "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028",
+                "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+            },
+            {
+                "direction": "egress",
+                "ethertype": "IPv6",
+                "id": "565b9502-12de-4ffd-91e9-68885cff6ae1",
+                "port_range_max": null,
+                "port_range_min": null,
+                "protocol": null,
+                "remote_group_id": null,
+                "remote_ip_prefix": null,
+                "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028",
+                "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+            }
+        ],
+        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+    }
+}
+    `)
+	})
+
+	opts := groups.CreateOpts{Name: "new-webservers", Description: "security group for webservers"}
+	_, err := groups.Create(fake.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/security-groups/85cc3048-abc3-43cc-89b3-377341426ac5", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "security_group": {
+        "description": "default",
+        "id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+        "name": "default",
+        "security_group_rules": [
+            {
+                "direction": "egress",
+                "ethertype": "IPv6",
+                "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
+                "port_range_max": null,
+                "port_range_min": null,
+                "protocol": null,
+                "remote_group_id": null,
+                "remote_ip_prefix": null,
+                "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+                "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+            },
+            {
+                "direction": "egress",
+                "ethertype": "IPv4",
+                "id": "93aa42e5-80db-4581-9391-3a608bd0e448",
+                "port_range_max": null,
+                "port_range_min": null,
+                "protocol": null,
+                "remote_group_id": null,
+                "remote_ip_prefix": null,
+                "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+                "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+            }
+        ],
+        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+    }
+}
+      `)
+	})
+
+	sg, err := groups.Get(fake.ServiceClient(), "85cc3048-abc3-43cc-89b3-377341426ac5").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "default", sg.Description)
+	th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sg.ID)
+	th.AssertEquals(t, "default", sg.Name)
+	th.AssertEquals(t, 2, len(sg.Rules))
+	th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sg.TenantID)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/security-groups/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := groups.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/extensions/security/groups/urls.go b/openstack/networking/v2/extensions/security/groups/urls.go
index 84f7324..104cbcc 100644
--- a/openstack/networking/v2/extensions/security/groups/urls.go
+++ b/openstack/networking/v2/extensions/security/groups/urls.go
@@ -1,6 +1,6 @@
 package groups
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const rootPath = "security-groups"
 
diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go
index e06934a..77f7e37 100644
--- a/openstack/networking/v2/extensions/security/rules/requests.go
+++ b/openstack/networking/v2/extensions/security/rules/requests.go
@@ -1,10 +1,8 @@
 package rules
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOpts allows the filtering and sorting of paginated collections through
@@ -43,132 +41,87 @@
 	})
 }
 
-// Errors
-var (
-	errValidDirectionRequired = fmt.Errorf("A valid Direction is required")
-	errValidEtherTypeRequired = fmt.Errorf("A valid EtherType is required")
-	errSecGroupIDRequired     = fmt.Errorf("A valid SecGroupID is required")
-	errValidProtocolRequired  = fmt.Errorf("A valid Protocol is required")
-)
+type RuleDirection string
+type RuleProtocol string
+type RuleEtherType string
 
 // Constants useful for CreateOpts
 const (
-	DirIngress   = "ingress"
-	DirEgress    = "egress"
-	Ether4       = "IPv4"
-	Ether6       = "IPv6"
-	ProtocolTCP  = "tcp"
-	ProtocolUDP  = "udp"
-	ProtocolICMP = "icmp"
+	DirIngress   RuleDirection = "ingress"
+	DirEgress    RuleDirection = "egress"
+	ProtocolTCP  RuleProtocol  = "tcp"
+	ProtocolUDP  RuleProtocol  = "udp"
+	ProtocolICMP RuleProtocol  = "icmp"
+	EtherType4   RuleEtherType = "IPv4"
+	EtherType6   RuleEtherType = "IPv6"
 )
 
+// CreateOptsBuilder is what types must satisfy to be used as Create
+// options.
+type CreateOptsBuilder interface {
+	ToSecGroupRuleCreateMap() (map[string]interface{}, error)
+}
+
 // CreateOpts contains all the values needed to create a new security group rule.
 type CreateOpts struct {
 	// Required. Must be either "ingress" or "egress": the direction in which the
 	// security group rule is applied.
-	Direction string
-
+	Direction RuleDirection `json:"direction" required:"true"`
 	// Required. Must be "IPv4" or "IPv6", and addresses represented in CIDR must
 	// match the ingress or egress rules.
-	EtherType string
-
+	EtherType RuleEtherType `json:"ethertype" required:"true"`
 	// Required. The security group ID to associate with this security group rule.
-	SecGroupID string
-
+	SecGroupID string `json:"security_group_id" required:"true"`
 	// Optional. The maximum port number in the range that is matched by the
 	// security group rule. The PortRangeMin attribute constrains the PortRangeMax
 	// attribute. If the protocol is ICMP, this value must be an ICMP type.
-	PortRangeMax int
-
+	PortRangeMax int `json:"port_range_max,omitempty"`
 	// Optional. The minimum port number in the range that is matched by the
 	// security group rule. If the protocol is TCP or UDP, this value must be
 	// less than or equal to the value of the PortRangeMax attribute. If the
 	// protocol is ICMP, this value must be an ICMP type.
-	PortRangeMin int
-
+	PortRangeMin int `json:"port_range_min,omitempty"`
 	// Optional. The protocol that is matched by the security group rule. Valid
 	// values are "tcp", "udp", "icmp" or an empty string.
-	Protocol string
-
+	Protocol RuleProtocol `json:"protocol,omitempty"`
 	// Optional. The remote group ID to be associated with this security group
 	// rule. You can specify either RemoteGroupID or RemoteIPPrefix.
-	RemoteGroupID string
-
+	RemoteGroupID string `json:"remote_group_id,omitempty"`
 	// Optional. The remote IP prefix to be associated with this security group
 	// rule. You can specify either RemoteGroupID or RemoteIPPrefix. This
 	// attribute matches the specified IP prefix as the source IP address of the
 	// IP packet.
-	RemoteIPPrefix string
-
+	RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"`
 	// Required for admins. Indicates the owner of the VIP.
-	TenantID string
+	TenantID string `json:"tenant_id,omitempty"`
+}
+
+// ToSecGroupRuleCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
+// interface
+func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "security_group_rule")
 }
 
 // Create is an operation which adds a new security group rule and associates it
 // with an existing security group (whose ID is specified in CreateOpts).
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	var res CreateResult
-
-	// Validate required opts
-	if opts.Direction != DirIngress && opts.Direction != DirEgress {
-		res.Err = errValidDirectionRequired
-		return res
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToSecGroupRuleCreateMap()
+	if err != nil {
+		r.Err = err
+		return
 	}
-	if opts.EtherType != Ether4 && opts.EtherType != Ether6 {
-		res.Err = errValidEtherTypeRequired
-		return res
-	}
-	if opts.SecGroupID == "" {
-		res.Err = errSecGroupIDRequired
-		return res
-	}
-	if opts.Protocol != "" && opts.Protocol != ProtocolTCP && opts.Protocol != ProtocolUDP && opts.Protocol != ProtocolICMP {
-		res.Err = errValidProtocolRequired
-		return res
-	}
-
-	type secrule struct {
-		Direction      string `json:"direction"`
-		EtherType      string `json:"ethertype"`
-		SecGroupID     string `json:"security_group_id"`
-		PortRangeMax   int    `json:"port_range_max,omitempty"`
-		PortRangeMin   int    `json:"port_range_min,omitempty"`
-		Protocol       string `json:"protocol,omitempty"`
-		RemoteGroupID  string `json:"remote_group_id,omitempty"`
-		RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"`
-		TenantID       string `json:"tenant_id,omitempty"`
-	}
-
-	type request struct {
-		SecRule secrule `json:"security_group_rule"`
-	}
-
-	reqBody := request{SecRule: secrule{
-		Direction:      opts.Direction,
-		EtherType:      opts.EtherType,
-		SecGroupID:     opts.SecGroupID,
-		PortRangeMax:   opts.PortRangeMax,
-		PortRangeMin:   opts.PortRangeMin,
-		Protocol:       opts.Protocol,
-		RemoteGroupID:  opts.RemoteGroupID,
-		RemoteIPPrefix: opts.RemoteIPPrefix,
-		TenantID:       opts.TenantID,
-	}}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+	return
 }
 
 // Get retrieves a particular security group rule based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
+	return
 }
 
 // Delete will permanently delete a particular security group rule based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(resourceURL(c, id), nil)
+	return
 }
diff --git a/openstack/networking/v2/extensions/security/rules/requests_test.go b/openstack/networking/v2/extensions/security/rules/requests_test.go
deleted file mode 100644
index b5afef3..0000000
--- a/openstack/networking/v2/extensions/security/rules/requests_test.go
+++ /dev/null
@@ -1,243 +0,0 @@
-package rules
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestURLs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.AssertEquals(t, th.Endpoint()+"v2.0/security-group-rules", rootURL(fake.ServiceClient()))
-	th.AssertEquals(t, th.Endpoint()+"v2.0/security-group-rules/foo", resourceURL(fake.ServiceClient(), "foo"))
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "security_group_rules": [
-        {
-            "direction": "egress",
-            "ethertype": "IPv6",
-            "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
-            "port_range_max": null,
-            "port_range_min": null,
-            "protocol": null,
-            "remote_group_id": null,
-            "remote_ip_prefix": null,
-            "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-            "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-        },
-        {
-            "direction": "egress",
-            "ethertype": "IPv4",
-            "id": "93aa42e5-80db-4581-9391-3a608bd0e448",
-            "port_range_max": null,
-            "port_range_min": null,
-            "protocol": null,
-            "remote_group_id": null,
-            "remote_ip_prefix": null,
-            "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-            "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-        }
-    ]
-}
-      `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractRules(page)
-		if err != nil {
-			t.Errorf("Failed to extract secrules: %v", err)
-			return false, err
-		}
-
-		expected := []SecGroupRule{
-			SecGroupRule{
-				Direction:      "egress",
-				EtherType:      "IPv6",
-				ID:             "3c0e45ff-adaf-4124-b083-bf390e5482ff",
-				PortRangeMax:   0,
-				PortRangeMin:   0,
-				Protocol:       "",
-				RemoteGroupID:  "",
-				RemoteIPPrefix: "",
-				SecGroupID:     "85cc3048-abc3-43cc-89b3-377341426ac5",
-				TenantID:       "e4f50856753b4dc6afee5fa6b9b6c550",
-			},
-			SecGroupRule{
-				Direction:      "egress",
-				EtherType:      "IPv4",
-				ID:             "93aa42e5-80db-4581-9391-3a608bd0e448",
-				PortRangeMax:   0,
-				PortRangeMin:   0,
-				Protocol:       "",
-				RemoteGroupID:  "",
-				RemoteIPPrefix: "",
-				SecGroupID:     "85cc3048-abc3-43cc-89b3-377341426ac5",
-				TenantID:       "e4f50856753b4dc6afee5fa6b9b6c550",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "security_group_rule": {
-        "direction": "ingress",
-        "port_range_min": 80,
-        "ethertype": "IPv4",
-        "port_range_max": 80,
-        "protocol": "tcp",
-        "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-        "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a"
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "security_group_rule": {
-        "direction": "ingress",
-        "ethertype": "IPv4",
-        "id": "2bc0accf-312e-429a-956e-e4407625eb62",
-        "port_range_max": 80,
-        "port_range_min": 80,
-        "protocol": "tcp",
-        "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-        "remote_ip_prefix": null,
-        "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a",
-        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-    }
-}
-    `)
-	})
-
-	opts := CreateOpts{
-		Direction:     "ingress",
-		PortRangeMin:  80,
-		EtherType:     "IPv4",
-		PortRangeMax:  80,
-		Protocol:      "tcp",
-		RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5",
-		SecGroupID:    "a7734e61-b545-452d-a3cd-0189cbd9747a",
-	}
-	_, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), CreateOpts{Direction: "something"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Direction: DirIngress, EtherType: "something"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Direction: DirIngress, EtherType: Ether4})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Direction: DirIngress, EtherType: Ether4, SecGroupID: "something", Protocol: "foo"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-group-rules/3c0e45ff-adaf-4124-b083-bf390e5482ff", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "security_group_rule": {
-        "direction": "egress",
-        "ethertype": "IPv6",
-        "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
-        "port_range_max": null,
-        "port_range_min": null,
-        "protocol": null,
-        "remote_group_id": null,
-        "remote_ip_prefix": null,
-        "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-    }
-}
-      `)
-	})
-
-	sr, err := Get(fake.ServiceClient(), "3c0e45ff-adaf-4124-b083-bf390e5482ff").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "egress", sr.Direction)
-	th.AssertEquals(t, "IPv6", sr.EtherType)
-	th.AssertEquals(t, "3c0e45ff-adaf-4124-b083-bf390e5482ff", sr.ID)
-	th.AssertEquals(t, 0, sr.PortRangeMax)
-	th.AssertEquals(t, 0, sr.PortRangeMin)
-	th.AssertEquals(t, "", sr.Protocol)
-	th.AssertEquals(t, "", sr.RemoteGroupID)
-	th.AssertEquals(t, "", sr.RemoteIPPrefix)
-	th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sr.SecGroupID)
-	th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sr.TenantID)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-group-rules/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go
index 6e13857..18476a6 100644
--- a/openstack/networking/v2/extensions/security/rules/results.go
+++ b/openstack/networking/v2/extensions/security/rules/results.go
@@ -1,9 +1,8 @@
 package rules
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // SecGroupRule represents a rule to dictate the behaviour of incoming or
@@ -20,21 +19,21 @@
 
 	// Must be IPv4 or IPv6, and addresses represented in CIDR must match the
 	// ingress or egress rules.
-	EtherType string `json:"ethertype" mapstructure:"ethertype"`
+	EtherType string `json:"ethertype"`
 
 	// The security group ID to associate with this security group rule.
-	SecGroupID string `json:"security_group_id" mapstructure:"security_group_id"`
+	SecGroupID string `json:"security_group_id"`
 
 	// The minimum port number in the range that is matched by the security group
 	// rule. If the protocol is TCP or UDP, this value must be less than or equal
 	// to the value of the PortRangeMax attribute. If the protocol is ICMP, this
 	// value must be an ICMP type.
-	PortRangeMin int `json:"port_range_min" mapstructure:"port_range_min"`
+	PortRangeMin int `json:"port_range_min"`
 
 	// The maximum port number in the range that is matched by the security group
 	// rule. The PortRangeMin attribute constrains the PortRangeMax attribute. If
 	// the protocol is ICMP, this value must be an ICMP type.
-	PortRangeMax int `json:"port_range_max" mapstructure:"port_range_max"`
+	PortRangeMax int `json:"port_range_max"`
 
 	// The protocol that is matched by the security group rule. Valid values are
 	// "tcp", "udp", "icmp" or an empty string.
@@ -42,15 +41,15 @@
 
 	// The remote group ID to be associated with this security group rule. You
 	// can specify either RemoteGroupID or RemoteIPPrefix.
-	RemoteGroupID string `json:"remote_group_id" mapstructure:"remote_group_id"`
+	RemoteGroupID string `json:"remote_group_id"`
 
 	// The remote IP prefix to be associated with this security group rule. You
 	// can specify either RemoteGroupID or RemoteIPPrefix . This attribute
 	// matches the specified IP prefix as the source IP address of the IP packet.
-	RemoteIPPrefix string `json:"remote_ip_prefix" mapstructure:"remote_ip_prefix"`
+	RemoteIPPrefix string `json:"remote_ip_prefix"`
 
 	// The owner of this security group rule.
-	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 }
 
 // SecGroupRulePage is the page returned by a pager when traversing over a
@@ -62,40 +61,32 @@
 // NextPageURL is invoked when a paginated collection of security group rules has
 // reached the end of a page and the pager seeks to traverse over a new one. In
 // order to do this, it needs to construct the next page's URL.
-func (p SecGroupRulePage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"security_group_rules_links"`
+func (r SecGroupRulePage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"security_group_rules_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a SecGroupRulePage struct is empty.
-func (p SecGroupRulePage) IsEmpty() (bool, error) {
-	is, err := ExtractRules(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r SecGroupRulePage) IsEmpty() (bool, error) {
+	is, err := ExtractRules(r)
+	return len(is) == 0, err
 }
 
 // ExtractRules accepts a Page struct, specifically a SecGroupRulePage struct,
 // and extracts the elements into a slice of SecGroupRule structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractRules(page pagination.Page) ([]SecGroupRule, error) {
-	var resp struct {
-		SecGroupRules []SecGroupRule `mapstructure:"security_group_rules" json:"security_group_rules"`
+func ExtractRules(r pagination.Page) ([]SecGroupRule, error) {
+	var s struct {
+		SecGroupRules []SecGroupRule `json:"security_group_rules"`
 	}
-
-	err := mapstructure.Decode(page.(SecGroupRulePage).Body, &resp)
-
-	return resp.SecGroupRules, err
+	err := (r.(SecGroupRulePage)).ExtractInto(&s)
+	return s.SecGroupRules, err
 }
 
 type commonResult struct {
@@ -104,17 +95,11 @@
 
 // Extract is a function that accepts a result and extracts a security rule.
 func (r commonResult) Extract() (*SecGroupRule, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		SecGroupRule *SecGroupRule `json:"security_group_rule"`
 	}
-
-	var res struct {
-		SecGroupRule *SecGroupRule `mapstructure:"security_group_rule" json:"security_group_rule"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.SecGroupRule, err
+	err := r.ExtractInto(&s)
+	return s.SecGroupRule, err
 }
 
 // CreateResult represents the result of a create operation.
diff --git a/openstack/networking/v2/extensions/security/rules/testing/doc.go b/openstack/networking/v2/extensions/security/rules/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/security/rules/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go
new file mode 100644
index 0000000..968fd04
--- /dev/null
+++ b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go
@@ -0,0 +1,236 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "security_group_rules": [
+        {
+            "direction": "egress",
+            "ethertype": "IPv6",
+            "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
+            "port_range_max": null,
+            "port_range_min": null,
+            "protocol": null,
+            "remote_group_id": null,
+            "remote_ip_prefix": null,
+            "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+            "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+        },
+        {
+            "direction": "egress",
+            "ethertype": "IPv4",
+            "id": "93aa42e5-80db-4581-9391-3a608bd0e448",
+            "port_range_max": null,
+            "port_range_min": null,
+            "protocol": null,
+            "remote_group_id": null,
+            "remote_ip_prefix": null,
+            "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+            "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+        }
+    ]
+}
+      `)
+	})
+
+	count := 0
+
+	rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := rules.ExtractRules(page)
+		if err != nil {
+			t.Errorf("Failed to extract secrules: %v", err)
+			return false, err
+		}
+
+		expected := []rules.SecGroupRule{
+			{
+				Direction:      "egress",
+				EtherType:      "IPv6",
+				ID:             "3c0e45ff-adaf-4124-b083-bf390e5482ff",
+				PortRangeMax:   0,
+				PortRangeMin:   0,
+				Protocol:       "",
+				RemoteGroupID:  "",
+				RemoteIPPrefix: "",
+				SecGroupID:     "85cc3048-abc3-43cc-89b3-377341426ac5",
+				TenantID:       "e4f50856753b4dc6afee5fa6b9b6c550",
+			},
+			{
+				Direction:      "egress",
+				EtherType:      "IPv4",
+				ID:             "93aa42e5-80db-4581-9391-3a608bd0e448",
+				PortRangeMax:   0,
+				PortRangeMin:   0,
+				Protocol:       "",
+				RemoteGroupID:  "",
+				RemoteIPPrefix: "",
+				SecGroupID:     "85cc3048-abc3-43cc-89b3-377341426ac5",
+				TenantID:       "e4f50856753b4dc6afee5fa6b9b6c550",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "security_group_rule": {
+        "direction": "ingress",
+        "port_range_min": 80,
+        "ethertype": "IPv4",
+        "port_range_max": 80,
+        "protocol": "tcp",
+        "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+        "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a"
+    }
+}
+      `)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "security_group_rule": {
+        "direction": "ingress",
+        "ethertype": "IPv4",
+        "id": "2bc0accf-312e-429a-956e-e4407625eb62",
+        "port_range_max": 80,
+        "port_range_min": 80,
+        "protocol": "tcp",
+        "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+        "remote_ip_prefix": null,
+        "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a",
+        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+    }
+}
+    `)
+	})
+
+	opts := rules.CreateOpts{
+		Direction:     "ingress",
+		PortRangeMin:  80,
+		EtherType:     rules.EtherType4,
+		PortRangeMax:  80,
+		Protocol:      "tcp",
+		RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5",
+		SecGroupID:    "a7734e61-b545-452d-a3cd-0189cbd9747a",
+	}
+	_, err := rules.Create(fake.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestRequiredCreateOpts(t *testing.T) {
+	res := rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4, SecGroupID: "something", Protocol: "foo"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/security-group-rules/3c0e45ff-adaf-4124-b083-bf390e5482ff", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "security_group_rule": {
+        "direction": "egress",
+        "ethertype": "IPv6",
+        "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
+        "port_range_max": null,
+        "port_range_min": null,
+        "protocol": null,
+        "remote_group_id": null,
+        "remote_ip_prefix": null,
+        "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+    }
+}
+      `)
+	})
+
+	sr, err := rules.Get(fake.ServiceClient(), "3c0e45ff-adaf-4124-b083-bf390e5482ff").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "egress", sr.Direction)
+	th.AssertEquals(t, "IPv6", sr.EtherType)
+	th.AssertEquals(t, "3c0e45ff-adaf-4124-b083-bf390e5482ff", sr.ID)
+	th.AssertEquals(t, 0, sr.PortRangeMax)
+	th.AssertEquals(t, 0, sr.PortRangeMin)
+	th.AssertEquals(t, "", sr.Protocol)
+	th.AssertEquals(t, "", sr.RemoteGroupID)
+	th.AssertEquals(t, "", sr.RemoteIPPrefix)
+	th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sr.SecGroupID)
+	th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sr.TenantID)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/security-group-rules/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := rules.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/extensions/security/rules/urls.go b/openstack/networking/v2/extensions/security/rules/urls.go
index 8e2b2bb..a5ede0e 100644
--- a/openstack/networking/v2/extensions/security/rules/urls.go
+++ b/openstack/networking/v2/extensions/security/rules/urls.go
@@ -1,6 +1,6 @@
 package rules
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 const rootPath = "security-group-rules"
 
diff --git a/openstack/networking/v2/extensions/testing/delegate_test.go b/openstack/networking/v2/extensions/testing/delegate_test.go
new file mode 100755
index 0000000..618d052
--- /dev/null
+++ b/openstack/networking/v2/extensions/testing/delegate_test.go
@@ -0,0 +1,106 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	common "github.com/gophercloud/gophercloud/openstack/common/extensions"
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/extensions", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+
+		fmt.Fprintf(w, `
+{
+    "extensions": [
+        {
+            "updated": "2013-01-20T00:00:00-00:00",
+            "name": "Neutron Service Type Management",
+            "links": [],
+            "namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
+            "alias": "service-type",
+            "description": "API for retrieving service providers for Neutron advanced services"
+        }
+    ]
+}
+      `)
+	})
+
+	count := 0
+
+	extensions.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := extensions.ExtractExtensions(page)
+		if err != nil {
+			t.Errorf("Failed to extract extensions: %v", err)
+		}
+
+		expected := []extensions.Extension{
+			{
+				common.Extension{
+					Updated:     "2013-01-20T00:00:00-00:00",
+					Name:        "Neutron Service Type Management",
+					Links:       []interface{}{},
+					Namespace:   "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
+					Alias:       "service-type",
+					Description: "API for retrieving service providers for Neutron advanced services",
+				},
+			},
+		}
+
+		th.AssertDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/extensions/agent", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "extension": {
+        "updated": "2013-02-03T10:00:00-00:00",
+        "name": "agent",
+        "links": [],
+        "namespace": "http://docs.openstack.org/ext/agent/api/v2.0",
+        "alias": "agent",
+        "description": "The agent management extension."
+    }
+}
+    `)
+	})
+
+	ext, err := extensions.Get(fake.ServiceClient(), "agent").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, ext.Updated, "2013-02-03T10:00:00-00:00")
+	th.AssertEquals(t, ext.Name, "agent")
+	th.AssertEquals(t, ext.Namespace, "http://docs.openstack.org/ext/agent/api/v2.0")
+	th.AssertEquals(t, ext.Alias, "agent")
+	th.AssertEquals(t, ext.Description, "The agent management extension.")
+}
diff --git a/openstack/networking/v2/extensions/testing/doc.go b/openstack/networking/v2/extensions/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/extensions/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/networks/errors.go b/openstack/networking/v2/networks/errors.go
deleted file mode 100644
index 83c4a6a..0000000
--- a/openstack/networking/v2/networks/errors.go
+++ /dev/null
@@ -1 +0,0 @@
-package networks
diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go
index 25ab7a8..876a00b 100644
--- a/openstack/networking/v2/networks/requests.go
+++ b/openstack/networking/v2/networks/requests.go
@@ -1,32 +1,10 @@
 package networks
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
-)
-
-type networkOpts struct {
-	AdminStateUp *bool
-	Name         string
-	Shared       *bool
-	TenantID     string
-}
-
 // ListOptsBuilder allows extensions to add additional parameters to the
 // List request.
 type ListOptsBuilder interface {
@@ -54,10 +32,7 @@
 // ToNetworkListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToNetworkListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -72,17 +47,15 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return NetworkPage{pagination.LinkedPageBase{PageResult: r}}
 	})
 }
 
 // Get retrieves a specific network based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(getURL(c, id), &r.Body, nil)
+	return
 }
 
 // CreateOptsBuilder is the interface options structs have to satisfy in order
@@ -93,28 +66,17 @@
 	ToNetworkCreateMap() (map[string]interface{}, error)
 }
 
-// CreateOpts is the common options struct used in this package's Create
-// operation.
-type CreateOpts networkOpts
+// CreateOpts satisfies the CreateOptsBuilder interface
+type CreateOpts struct {
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
+	Name         string `json:"name,omitempty"`
+	Shared       *bool  `json:"shared,omitempty"`
+	TenantID     string `json:"tenant_id,omitempty"`
+}
 
 // ToNetworkCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
-	n := make(map[string]interface{})
-
-	if opts.AdminStateUp != nil {
-		n["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		n["name"] = opts.Name
-	}
-	if opts.Shared != nil {
-		n["shared"] = &opts.Shared
-	}
-	if opts.TenantID != "" {
-		n["tenant_id"] = opts.TenantID
-	}
-
-	return map[string]interface{}{"network": n}, nil
+	return gophercloud.BuildRequestBody(opts, "network")
 }
 
 // Create accepts a CreateOpts struct and creates a new network using the values
@@ -124,17 +86,14 @@
 // The tenant ID that is contained in the URI is the tenant that creates the
 // network. An admin user, however, has the option of specifying another tenant
 // ID in the CreateOpts struct.
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToNetworkCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToNetworkCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(createURL(c), b, &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -145,82 +104,65 @@
 	ToNetworkUpdateMap() (map[string]interface{}, error)
 }
 
-// UpdateOpts is the common options struct used in this package's Update
-// operation.
-type UpdateOpts networkOpts
+// UpdateOpts satisfies the UpdateOptsBuilder interface
+type UpdateOpts struct {
+	AdminStateUp *bool  `json:"admin_state_up,omitempty"`
+	Name         string `json:"name,omitempty"`
+	Shared       *bool  `json:"shared,omitempty"`
+}
 
 // ToNetworkUpdateMap casts a UpdateOpts struct to a map.
 func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) {
-	n := make(map[string]interface{})
-
-	if opts.AdminStateUp != nil {
-		n["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		n["name"] = opts.Name
-	}
-	if opts.Shared != nil {
-		n["shared"] = &opts.Shared
-	}
-
-	return map[string]interface{}{"network": n}, nil
+	return gophercloud.BuildRequestBody(opts, "network")
 }
 
 // Update accepts a UpdateOpts struct and updates an existing network using the
 // values provided. For more information, see the Create function.
-func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToNetworkUpdateMap()
+func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToNetworkUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Put(updateURL(c, networkID), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201},
 	})
-
-	return res
+	return
 }
 
 // Delete accepts a unique ID and deletes the network associated with it.
-func Delete(c *gophercloud.ServiceClient, networkID string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(deleteURL(c, networkID), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, networkID string) (r DeleteResult) {
+	_, r.Err = c.Delete(deleteURL(c, networkID), nil)
+	return
 }
 
 // IDFromName is a convenience function that returns a network's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
-	networkCount := 0
-	networkID := ""
-	if name == "" {
-		return "", fmt.Errorf("A network name must be provided.")
+	count := 0
+	id := ""
+	pages, err := List(client, nil).AllPages()
+	if err != nil {
+		return "", err
 	}
-	pager := List(client, nil)
-	pager.EachPage(func(page pagination.Page) (bool, error) {
-		networkList, err := ExtractNetworks(page)
-		if err != nil {
-			return false, err
-		}
 
-		for _, n := range networkList {
-			if n.Name == name {
-				networkCount++
-				networkID = n.ID
-			}
-		}
-		return true, nil
-	})
+	all, err := ExtractNetworks(pages)
+	if err != nil {
+		return "", err
+	}
 
-	switch networkCount {
+	for _, s := range all {
+		if s.Name == name {
+			count++
+			id = s.ID
+		}
+	}
+
+	switch count {
 	case 0:
-		return "", fmt.Errorf("Unable to find network: %s", name)
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "network"}
 	case 1:
-		return networkID, nil
+		return id, nil
 	default:
-		return "", fmt.Errorf("Found %d networks matching %s", networkCount, name)
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "network"}
 	}
 }
diff --git a/openstack/networking/v2/networks/requests_test.go b/openstack/networking/v2/networks/requests_test.go
deleted file mode 100644
index 81eb79c..0000000
--- a/openstack/networking/v2/networks/requests_test.go
+++ /dev/null
@@ -1,276 +0,0 @@
-package networks
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "networks": [
-        {
-            "status": "ACTIVE",
-            "subnets": [
-                "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-            ],
-            "name": "private-network",
-            "admin_state_up": true,
-            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-            "shared": true,
-            "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
-        },
-        {
-            "status": "ACTIVE",
-            "subnets": [
-                "08eae331-0402-425a-923c-34f7cfe39c1b"
-            ],
-            "name": "private",
-            "admin_state_up": true,
-            "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
-            "shared": true,
-            "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324"
-        }
-    ]
-}
-			`)
-	})
-
-	client := fake.ServiceClient()
-	count := 0
-
-	List(client, ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNetworks(page)
-		if err != nil {
-			t.Errorf("Failed to extract networks: %v", err)
-			return false, err
-		}
-
-		expected := []Network{
-			Network{
-				Status:       "ACTIVE",
-				Subnets:      []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"},
-				Name:         "private-network",
-				AdminStateUp: true,
-				TenantID:     "4fd44f30292945e481c7b8a0c8908869",
-				Shared:       true,
-				ID:           "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-			},
-			Network{
-				Status:       "ACTIVE",
-				Subnets:      []string{"08eae331-0402-425a-923c-34f7cfe39c1b"},
-				Name:         "private",
-				AdminStateUp: true,
-				TenantID:     "26a7980765d0414dbc1fc1f88cdb7e6e",
-				Shared:       true,
-				ID:           "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "network": {
-        "status": "ACTIVE",
-        "subnets": [
-            "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-        ],
-        "name": "private-network",
-        "admin_state_up": true,
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "shared": true,
-        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
-    }
-}
-			`)
-	})
-
-	n, err := Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertDeepEquals(t, n.Subnets, []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"})
-	th.AssertEquals(t, n.Name, "private-network")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869")
-	th.AssertEquals(t, n.Shared, true)
-	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "network": {
-        "name": "sample_network",
-        "admin_state_up": true
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "network": {
-        "status": "ACTIVE",
-        "subnets": [],
-        "name": "net1",
-        "admin_state_up": true,
-        "tenant_id": "9bacb3c5d39d41a79512987f338cf177",
-        "shared": false,
-        "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c"
-    }
-}
-		`)
-	})
-
-	iTrue := true
-	options := CreateOpts{Name: "sample_network", AdminStateUp: &iTrue}
-	n, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertDeepEquals(t, n.Subnets, []string{})
-	th.AssertEquals(t, n.Name, "net1")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.TenantID, "9bacb3c5d39d41a79512987f338cf177")
-	th.AssertEquals(t, n.Shared, false)
-	th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
-}
-
-func TestCreateWithOptionalFields(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-	"network": {
-			"name": "sample_network",
-			"admin_state_up": true,
-			"shared": true,
-			"tenant_id": "12345"
-	}
-}
-		`)
-
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, `{}`)
-	})
-
-	iTrue := true
-	options := CreateOpts{Name: "sample_network", AdminStateUp: &iTrue, Shared: &iTrue, TenantID: "12345"}
-	_, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-		"network": {
-				"name": "new_network_name",
-				"admin_state_up": false,
-				"shared": true
-		}
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "network": {
-        "status": "ACTIVE",
-        "subnets": [],
-        "name": "new_network_name",
-        "admin_state_up": false,
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "shared": true,
-        "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c"
-    }
-}
-		`)
-	})
-
-	iTrue, iFalse := true, false
-	options := UpdateOpts{Name: "new_network_name", AdminStateUp: &iFalse, Shared: &iTrue}
-	n, err := Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Name, "new_network_name")
-	th.AssertEquals(t, n.AdminStateUp, false)
-	th.AssertEquals(t, n.Shared, true)
-	th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go
index 3ecedde..d928980 100644
--- a/openstack/networking/v2/networks/results.go
+++ b/openstack/networking/v2/networks/results.go
@@ -1,9 +1,8 @@
 package networks
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 type commonResult struct {
@@ -12,17 +11,11 @@
 
 // Extract is a function that accepts a result and extracts a network resource.
 func (r commonResult) Extract() (*Network, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Network *Network `json:"network"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Network, err
+	err := r.ExtractInto(&s)
+	return s.Network, err
 }
 
 // CreateResult represents the result of a create operation.
@@ -48,26 +41,26 @@
 // Network represents, well, a network.
 type Network struct {
 	// UUID for the network
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 
 	// Human-readable name for the network. Might not be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 
 	// The administrative state of network. If false (down), the network does not forward packets.
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 
 	// Indicates whether network is currently operational. Possible values include
 	// `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values.
-	Status string `mapstructure:"status" json:"status"`
+	Status string `json:"status"`
 
 	// Subnets associated with this network.
-	Subnets []string `mapstructure:"subnets" json:"subnets"`
+	Subnets []string `json:"subnets"`
 
 	// Owner of network. Only admin users can specify a tenant_id other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 
 	// Specifies whether the network resource can be accessed by any tenant or not.
-	Shared bool `mapstructure:"shared" json:"shared"`
+	Shared bool `json:"shared"`
 }
 
 // NetworkPage is the page returned by a pager when traversing over a
@@ -79,38 +72,30 @@
 // NextPageURL is invoked when a paginated collection of networks has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p NetworkPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"networks_links"`
+func (r NetworkPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"networks_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a NetworkPage struct is empty.
-func (p NetworkPage) IsEmpty() (bool, error) {
-	is, err := ExtractNetworks(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r NetworkPage) IsEmpty() (bool, error) {
+	is, err := ExtractNetworks(r)
+	return len(is) == 0, err
 }
 
 // ExtractNetworks accepts a Page struct, specifically a NetworkPage struct,
 // and extracts the elements into a slice of Network structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractNetworks(page pagination.Page) ([]Network, error) {
-	var resp struct {
-		Networks []Network `mapstructure:"networks" json:"networks"`
+func ExtractNetworks(r pagination.Page) ([]Network, error) {
+	var s struct {
+		Networks []Network `json:"networks"`
 	}
-
-	err := mapstructure.Decode(page.(NetworkPage).Body, &resp)
-
-	return resp.Networks, err
+	err := (r.(NetworkPage)).ExtractInto(&s)
+	return s.Networks, err
 }
diff --git a/openstack/networking/v2/networks/testing/doc.go b/openstack/networking/v2/networks/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/networks/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go
new file mode 100644
index 0000000..5b9f03d
--- /dev/null
+++ b/openstack/networking/v2/networks/testing/requests_test.go
@@ -0,0 +1,277 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "networks": [
+        {
+            "status": "ACTIVE",
+            "subnets": [
+                "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+            ],
+            "name": "private-network",
+            "admin_state_up": true,
+            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+            "shared": true,
+            "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
+        },
+        {
+            "status": "ACTIVE",
+            "subnets": [
+                "08eae331-0402-425a-923c-34f7cfe39c1b"
+            ],
+            "name": "private",
+            "admin_state_up": true,
+            "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
+            "shared": true,
+            "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324"
+        }
+    ]
+}
+			`)
+	})
+
+	client := fake.ServiceClient()
+	count := 0
+
+	networks.List(client, networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := networks.ExtractNetworks(page)
+		if err != nil {
+			t.Errorf("Failed to extract networks: %v", err)
+			return false, err
+		}
+
+		expected := []networks.Network{
+			{
+				Status:       "ACTIVE",
+				Subnets:      []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"},
+				Name:         "private-network",
+				AdminStateUp: true,
+				TenantID:     "4fd44f30292945e481c7b8a0c8908869",
+				Shared:       true,
+				ID:           "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+			},
+			{
+				Status:       "ACTIVE",
+				Subnets:      []string{"08eae331-0402-425a-923c-34f7cfe39c1b"},
+				Name:         "private",
+				AdminStateUp: true,
+				TenantID:     "26a7980765d0414dbc1fc1f88cdb7e6e",
+				Shared:       true,
+				ID:           "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "network": {
+        "status": "ACTIVE",
+        "subnets": [
+            "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+        ],
+        "name": "private-network",
+        "admin_state_up": true,
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "shared": true,
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
+    }
+}
+			`)
+	})
+
+	n, err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Status, "ACTIVE")
+	th.AssertDeepEquals(t, n.Subnets, []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"})
+	th.AssertEquals(t, n.Name, "private-network")
+	th.AssertEquals(t, n.AdminStateUp, true)
+	th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869")
+	th.AssertEquals(t, n.Shared, true)
+	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "network": {
+        "name": "sample_network",
+        "admin_state_up": true
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "network": {
+        "status": "ACTIVE",
+        "subnets": [],
+        "name": "net1",
+        "admin_state_up": true,
+        "tenant_id": "9bacb3c5d39d41a79512987f338cf177",
+        "shared": false,
+        "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c"
+    }
+}
+		`)
+	})
+
+	iTrue := true
+	options := networks.CreateOpts{Name: "sample_network", AdminStateUp: &iTrue}
+	n, err := networks.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Status, "ACTIVE")
+	th.AssertDeepEquals(t, n.Subnets, []string{})
+	th.AssertEquals(t, n.Name, "net1")
+	th.AssertEquals(t, n.AdminStateUp, true)
+	th.AssertEquals(t, n.TenantID, "9bacb3c5d39d41a79512987f338cf177")
+	th.AssertEquals(t, n.Shared, false)
+	th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
+}
+
+func TestCreateWithOptionalFields(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+	"network": {
+			"name": "sample_network",
+			"admin_state_up": true,
+			"shared": true,
+			"tenant_id": "12345"
+	}
+}
+		`)
+
+		w.WriteHeader(http.StatusCreated)
+		fmt.Fprintf(w, `{}`)
+	})
+
+	iTrue := true
+	options := networks.CreateOpts{Name: "sample_network", AdminStateUp: &iTrue, Shared: &iTrue, TenantID: "12345"}
+	_, err := networks.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+		"network": {
+				"name": "new_network_name",
+				"admin_state_up": false,
+				"shared": true
+		}
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "network": {
+        "status": "ACTIVE",
+        "subnets": [],
+        "name": "new_network_name",
+        "admin_state_up": false,
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "shared": true,
+        "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c"
+    }
+}
+		`)
+	})
+
+	iTrue, iFalse := true, false
+	options := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: &iFalse, Shared: &iTrue}
+	n, err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Name, "new_network_name")
+	th.AssertEquals(t, n.AdminStateUp, false)
+	th.AssertEquals(t, n.Shared, true)
+	th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := networks.Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/networks/urls.go b/openstack/networking/v2/networks/urls.go
index a9eecc5..4a8fb1d 100644
--- a/openstack/networking/v2/networks/urls.go
+++ b/openstack/networking/v2/networks/urls.go
@@ -1,6 +1,6 @@
 package networks
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func resourceURL(c *gophercloud.ServiceClient, id string) string {
 	return c.ServiceURL("networks", id)
diff --git a/openstack/networking/v2/networks/urls_test.go b/openstack/networking/v2/networks/urls_test.go
deleted file mode 100644
index caf77db..0000000
--- a/openstack/networking/v2/networks/urls_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package networks
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint, ResourceBase: endpoint + "v2.0/"}
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "v2.0/networks/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "v2.0/networks"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestListURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "v2.0/networks"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "foo")
-	expected := endpoint + "v2.0/networks/foo"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/networking/v2/ports/errors.go b/openstack/networking/v2/ports/errors.go
deleted file mode 100644
index 111d977..0000000
--- a/openstack/networking/v2/ports/errors.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package ports
-
-import "fmt"
-
-func err(str string) error {
-	return fmt.Errorf("%s", str)
-}
-
-var (
-	errNetworkIDRequired = err("A Network ID is required")
-)
diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go
index e73e10a..2a53202 100644
--- a/openstack/networking/v2/ports/requests.go
+++ b/openstack/networking/v2/ports/requests.go
@@ -1,23 +1,8 @@
 package ports
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -50,10 +35,7 @@
 // ToPortListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToPortListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -72,17 +54,15 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return PortPage{pagination.LinkedPageBase{PageResult: r}}
 	})
 }
 
 // Get retrieves a specific port based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(getURL(c, id), &r.Body, nil)
+	return
 }
 
 // CreateOptsBuilder is the interface options structs have to satisfy in order
@@ -95,71 +75,33 @@
 
 // CreateOpts represents the attributes used when creating a new port.
 type CreateOpts struct {
-	NetworkID           string
-	Name                string
-	AdminStateUp        *bool
-	MACAddress          string
-	FixedIPs            interface{}
-	DeviceID            string
-	DeviceOwner         string
-	TenantID            string
-	SecurityGroups      []string
-	AllowedAddressPairs []AddressPair
+	NetworkID           string        `json:"network_id" required:"true"`
+	Name                string        `json:"name,omitempty"`
+	AdminStateUp        *bool         `json:"admin_state_up,omitempty"`
+	MACAddress          string        `json:"mac_address,omitempty"`
+	FixedIPs            interface{}   `json:"fixed_ips,omitempty"`
+	DeviceID            string        `json:"device_id,omitempty"`
+	DeviceOwner         string        `json:"device_owner,omitempty"`
+	TenantID            string        `json:"tenant_id,omitempty"`
+	SecurityGroups      []string      `json:"security_groups,omitempty"`
+	AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"`
 }
 
 // ToPortCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
-	p := make(map[string]interface{})
-
-	if opts.NetworkID == "" {
-		return nil, errNetworkIDRequired
-	}
-	p["network_id"] = opts.NetworkID
-
-	if opts.DeviceID != "" {
-		p["device_id"] = opts.DeviceID
-	}
-	if opts.DeviceOwner != "" {
-		p["device_owner"] = opts.DeviceOwner
-	}
-	if opts.FixedIPs != nil {
-		p["fixed_ips"] = opts.FixedIPs
-	}
-	if opts.SecurityGroups != nil {
-		p["security_groups"] = opts.SecurityGroups
-	}
-	if opts.TenantID != "" {
-		p["tenant_id"] = opts.TenantID
-	}
-	if opts.AdminStateUp != nil {
-		p["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		p["name"] = opts.Name
-	}
-	if opts.MACAddress != "" {
-		p["mac_address"] = opts.MACAddress
-	}
-	if opts.AllowedAddressPairs != nil {
-		p["allowed_address_pairs"] = opts.AllowedAddressPairs
-	}
-
-	return map[string]interface{}{"port": p}, nil
+	return gophercloud.BuildRequestBody(opts, "port")
 }
 
 // Create accepts a CreateOpts struct and creates a new network using the values
 // provided. You must remember to provide a NetworkID value.
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToPortCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToPortCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(createURL(c), b, &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -172,97 +114,67 @@
 
 // UpdateOpts represents the attributes used when updating an existing port.
 type UpdateOpts struct {
-	Name                string
-	AdminStateUp        *bool
-	FixedIPs            interface{}
-	DeviceID            string
-	DeviceOwner         string
-	SecurityGroups      []string
-	AllowedAddressPairs []AddressPair
+	Name                string        `json:"name,omitempty"`
+	AdminStateUp        *bool         `json:"admin_state_up,omitempty"`
+	FixedIPs            interface{}   `json:"fixed_ips,omitempty"`
+	DeviceID            string        `json:"device_id,omitempty"`
+	DeviceOwner         string        `json:"device_owner,omitempty"`
+	SecurityGroups      []string      `json:"security_groups,omitempty"`
+	AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"`
 }
 
 // ToPortUpdateMap casts an UpdateOpts struct to a map.
 func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) {
-	p := make(map[string]interface{})
-
-	if opts.DeviceID != "" {
-		p["device_id"] = opts.DeviceID
-	}
-	if opts.DeviceOwner != "" {
-		p["device_owner"] = opts.DeviceOwner
-	}
-	if opts.FixedIPs != nil {
-		p["fixed_ips"] = opts.FixedIPs
-	}
-	if opts.SecurityGroups != nil {
-		p["security_groups"] = opts.SecurityGroups
-	}
-	if opts.AdminStateUp != nil {
-		p["admin_state_up"] = &opts.AdminStateUp
-	}
-	if opts.Name != "" {
-		p["name"] = opts.Name
-	}
-	if opts.AllowedAddressPairs != nil {
-		p["allowed_address_pairs"] = opts.AllowedAddressPairs
-	}
-
-	return map[string]interface{}{"port": p}, nil
+	return gophercloud.BuildRequestBody(opts, "port")
 }
 
 // Update accepts a UpdateOpts struct and updates an existing port using the
 // values provided.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToPortUpdateMap()
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToPortUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Put(updateURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201},
 	})
-	return res
+	return
 }
 
 // Delete accepts a unique ID and deletes the port associated with it.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(deleteURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(deleteURL(c, id), nil)
+	return
 }
 
 // IDFromName is a convenience function that returns a port's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
-	portCount := 0
-	portID := ""
-	if name == "" {
-		return "", fmt.Errorf("A port name must be provided.")
+	count := 0
+	id := ""
+	pages, err := List(client, nil).AllPages()
+	if err != nil {
+		return "", err
 	}
-	pager := List(client, nil)
-	pager.EachPage(func(page pagination.Page) (bool, error) {
-		portList, err := ExtractPorts(page)
-		if err != nil {
-			return false, err
-		}
 
-		for _, p := range portList {
-			if p.Name == name {
-				portCount++
-				portID = p.ID
-			}
-		}
-		return true, nil
-	})
+	all, err := ExtractPorts(pages)
+	if err != nil {
+		return "", err
+	}
 
-	switch portCount {
+	for _, s := range all {
+		if s.Name == name {
+			count++
+			id = s.ID
+		}
+	}
+
+	switch count {
 	case 0:
-		return "", fmt.Errorf("Unable to find port: %s", name)
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "port"}
 	case 1:
-		return portID, nil
+		return id, nil
 	default:
-		return "", fmt.Errorf("Found %d ports matching %s", portCount, name)
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "port"}
 	}
 }
diff --git a/openstack/networking/v2/ports/requests_test.go b/openstack/networking/v2/ports/requests_test.go
deleted file mode 100644
index b442996..0000000
--- a/openstack/networking/v2/ports/requests_test.go
+++ /dev/null
@@ -1,356 +0,0 @@
-package ports
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "ports": [
-        {
-            "status": "ACTIVE",
-            "binding:host_id": "devstack",
-            "name": "",
-            "admin_state_up": true,
-            "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3",
-            "tenant_id": "",
-            "device_owner": "network:router_gateway",
-            "mac_address": "fa:16:3e:58:42:ed",
-            "fixed_ips": [
-                {
-                    "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062",
-                    "ip_address": "172.24.4.2"
-                }
-            ],
-            "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
-            "security_groups": [],
-            "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824"
-        }
-    ]
-}
-      `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractPorts(page)
-		if err != nil {
-			t.Errorf("Failed to extract subnets: %v", err)
-			return false, nil
-		}
-
-		expected := []Port{
-			Port{
-				Status:       "ACTIVE",
-				Name:         "",
-				AdminStateUp: true,
-				NetworkID:    "70c1db1f-b701-45bd-96e0-a313ee3430b3",
-				TenantID:     "",
-				DeviceOwner:  "network:router_gateway",
-				MACAddress:   "fa:16:3e:58:42:ed",
-				FixedIPs: []IP{
-					IP{
-						SubnetID:  "008ba151-0b8c-4a67-98b5-0d2b87666062",
-						IPAddress: "172.24.4.2",
-					},
-				},
-				ID:             "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
-				SecurityGroups: []string{},
-				DeviceID:       "9ae135f4-b6e0-4dad-9e91-3c223e385824",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "port": {
-        "status": "ACTIVE",
-        "name": "",
-        "admin_state_up": true,
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "tenant_id": "7e02058126cc4950b75f9970368ba177",
-        "device_owner": "network:router_interface",
-        "mac_address": "fa:16:3e:23:fd:d7",
-        "fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.1"
-            }
-        ],
-        "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2",
-        "security_groups": [],
-        "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e"
-    }
-}
-			`)
-	})
-
-	n, err := Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertEquals(t, n.Name, "")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
-	th.AssertEquals(t, n.TenantID, "7e02058126cc4950b75f9970368ba177")
-	th.AssertEquals(t, n.DeviceOwner, "network:router_interface")
-	th.AssertEquals(t, n.MACAddress, "fa:16:3e:23:fd:d7")
-	th.AssertDeepEquals(t, n.FixedIPs, []IP{
-		IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"},
-	})
-	th.AssertEquals(t, n.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2")
-	th.AssertDeepEquals(t, n.SecurityGroups, []string{})
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertEquals(t, n.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "port": {
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "name": "private-port",
-        "admin_state_up": true,
-				"fixed_ips": [
-						{
-								"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-								"ip_address": "10.0.0.2"
-						}
-				],
-				"security_groups": ["foo"],
-        "allowed_address_pairs": [
-          {
-            "ip_address": "10.0.0.4",
-            "mac_address": "fa:16:3e:c9:cb:f0"
-          }
-        ]
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "port": {
-        "status": "DOWN",
-        "name": "private-port",
-        "admin_state_up": true,
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
-        "device_owner": "",
-        "mac_address": "fa:16:3e:c9:cb:f0",
-        "fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.2"
-            }
-        ],
-        "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
-        "security_groups": [
-            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
-        ],
-        "allowed_address_pairs": [
-          {
-            "ip_address": "10.0.0.4",
-            "mac_address": "fa:16:3e:c9:cb:f0"
-          }
-        ],
-        "device_id": ""
-    }
-}
-		`)
-	})
-
-	asu := true
-	options := CreateOpts{
-		Name:         "private-port",
-		AdminStateUp: &asu,
-		NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-		FixedIPs: []IP{
-			IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
-		},
-		SecurityGroups: []string{"foo"},
-		AllowedAddressPairs: []AddressPair{
-			AddressPair{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
-		},
-	}
-	n, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Status, "DOWN")
-	th.AssertEquals(t, n.Name, "private-port")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
-	th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
-	th.AssertEquals(t, n.DeviceOwner, "")
-	th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0")
-	th.AssertDeepEquals(t, n.FixedIPs, []IP{
-		IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
-	})
-	th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
-	th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
-	th.AssertDeepEquals(t, n.AllowedAddressPairs, []AddressPair{
-		AddressPair{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
-	})
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), CreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-		"port": {
-				"name": "new_port_name",
-				"fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.3"
-            }
-        ],
-        "allowed_address_pairs": [
-          {
-            "ip_address": "10.0.0.4",
-            "mac_address": "fa:16:3e:c9:cb:f0"
-          }
-        ],
-				"security_groups": [
-            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
-        ]
-		}
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "port": {
-        "status": "DOWN",
-        "name": "new_port_name",
-        "admin_state_up": true,
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
-        "device_owner": "",
-        "mac_address": "fa:16:3e:c9:cb:f0",
-        "fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.3"
-            }
-        ],
-        "allowed_address_pairs": [
-          {
-            "ip_address": "10.0.0.4",
-            "mac_address": "fa:16:3e:c9:cb:f0"
-          }
-        ],
-        "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
-        "security_groups": [
-            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
-        ],
-        "device_id": ""
-    }
-}
-		`)
-	})
-
-	options := UpdateOpts{
-		Name: "new_port_name",
-		FixedIPs: []IP{
-			IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
-		},
-		SecurityGroups: []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"},
-		AllowedAddressPairs: []AddressPair{
-			AddressPair{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
-		},
-	}
-
-	s, err := Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.Name, "new_port_name")
-	th.AssertDeepEquals(t, s.FixedIPs, []IP{
-		IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
-	})
-	th.AssertDeepEquals(t, s.AllowedAddressPairs, []AddressPair{
-		AddressPair{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
-	})
-	th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go
index 1f7eea1..57a1765 100644
--- a/openstack/networking/v2/ports/results.go
+++ b/openstack/networking/v2/ports/results.go
@@ -1,9 +1,8 @@
 package ports
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 type commonResult struct {
@@ -12,16 +11,11 @@
 
 // Extract is a function that accepts a result and extracts a port resource.
 func (r commonResult) Extract() (*Port, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Port *Port `json:"port"`
 	}
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Port, err
+	err := r.ExtractInto(&s)
+	return s.Port, err
 }
 
 // CreateResult represents the result of a create operation.
@@ -46,44 +40,45 @@
 
 // IP is a sub-struct that represents an individual IP.
 type IP struct {
-	SubnetID  string `mapstructure:"subnet_id" json:"subnet_id"`
-	IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"`
+	SubnetID  string `json:"subnet_id"`
+	IPAddress string `json:"ip_address,omitempty"`
 }
 
+// AddressPair contains the IP Address and the MAC address.
 type AddressPair struct {
-	IPAddress  string `mapstructure:"ip_address" json:"ip_address,omitempty"`
-	MACAddress string `mapstructure:"mac_address" json:"mac_address,omitempty"`
+	IPAddress  string `json:"ip_address,omitempty"`
+	MACAddress string `json:"mac_address,omitempty"`
 }
 
 // Port represents a Neutron port. See package documentation for a top-level
 // description of what this is.
 type Port struct {
 	// UUID for the port.
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 	// Network that this port is associated with.
-	NetworkID string `mapstructure:"network_id" json:"network_id"`
+	NetworkID string `json:"network_id"`
 	// Human-readable name for the port. Might not be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 	// Administrative state of port. If false (down), port does not forward packets.
-	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+	AdminStateUp bool `json:"admin_state_up"`
 	// Indicates whether network is currently operational. Possible values include
 	// `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values.
-	Status string `mapstructure:"status" json:"status"`
+	Status string `json:"status"`
 	// Mac address to use on this port.
-	MACAddress string `mapstructure:"mac_address" json:"mac_address"`
+	MACAddress string `json:"mac_address"`
 	// Specifies IP addresses for the port thus associating the port itself with
 	// the subnets where the IP addresses are picked from
-	FixedIPs []IP `mapstructure:"fixed_ips" json:"fixed_ips"`
+	FixedIPs []IP `json:"fixed_ips"`
 	// Owner of network. Only admin users can specify a tenant_id other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 	// Identifies the entity (e.g.: dhcp agent) using this port.
-	DeviceOwner string `mapstructure:"device_owner" json:"device_owner"`
+	DeviceOwner string `json:"device_owner"`
 	// Specifies the IDs of any security groups associated with a port.
-	SecurityGroups []string `mapstructure:"security_groups" json:"security_groups"`
+	SecurityGroups []string `json:"security_groups"`
 	// Identifies the device (e.g., virtual server) using this port.
-	DeviceID string `mapstructure:"device_id" json:"device_id"`
+	DeviceID string `json:"device_id"`
 	// Identifies the list of IP addresses the port will recognize/accept
-	AllowedAddressPairs []AddressPair `mapstructure:"allowed_address_pairs" json:"allowed_address_pairs"`
+	AllowedAddressPairs []AddressPair `json:"allowed_address_pairs"`
 }
 
 // PortPage is the page returned by a pager when traversing over a collection
@@ -95,38 +90,30 @@
 // NextPageURL is invoked when a paginated collection of ports has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p PortPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"ports_links"`
+func (r PortPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"ports_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a PortPage struct is empty.
-func (p PortPage) IsEmpty() (bool, error) {
-	is, err := ExtractPorts(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r PortPage) IsEmpty() (bool, error) {
+	is, err := ExtractPorts(r)
+	return len(is) == 0, err
 }
 
 // ExtractPorts accepts a Page struct, specifically a PortPage struct,
 // and extracts the elements into a slice of Port structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractPorts(page pagination.Page) ([]Port, error) {
-	var resp struct {
-		Ports []Port `mapstructure:"ports" json:"ports"`
+func ExtractPorts(r pagination.Page) ([]Port, error) {
+	var s struct {
+		Ports []Port `json:"ports"`
 	}
-
-	err := mapstructure.Decode(page.(PortPage).Body, &resp)
-
-	return resp.Ports, err
+	err := (r.(PortPage)).ExtractInto(&s)
+	return s.Ports, err
 }
diff --git a/openstack/networking/v2/ports/testing/doc.go b/openstack/networking/v2/ports/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/ports/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go
new file mode 100644
index 0000000..007dc5a
--- /dev/null
+++ b/openstack/networking/v2/ports/testing/requests_test.go
@@ -0,0 +1,357 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "ports": [
+        {
+            "status": "ACTIVE",
+            "binding:host_id": "devstack",
+            "name": "",
+            "admin_state_up": true,
+            "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3",
+            "tenant_id": "",
+            "device_owner": "network:router_gateway",
+            "mac_address": "fa:16:3e:58:42:ed",
+            "fixed_ips": [
+                {
+                    "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062",
+                    "ip_address": "172.24.4.2"
+                }
+            ],
+            "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
+            "security_groups": [],
+            "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824"
+        }
+    ]
+}
+      `)
+	})
+
+	count := 0
+
+	ports.List(fake.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := ports.ExtractPorts(page)
+		if err != nil {
+			t.Errorf("Failed to extract subnets: %v", err)
+			return false, nil
+		}
+
+		expected := []ports.Port{
+			{
+				Status:       "ACTIVE",
+				Name:         "",
+				AdminStateUp: true,
+				NetworkID:    "70c1db1f-b701-45bd-96e0-a313ee3430b3",
+				TenantID:     "",
+				DeviceOwner:  "network:router_gateway",
+				MACAddress:   "fa:16:3e:58:42:ed",
+				FixedIPs: []ports.IP{
+					{
+						SubnetID:  "008ba151-0b8c-4a67-98b5-0d2b87666062",
+						IPAddress: "172.24.4.2",
+					},
+				},
+				ID:             "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
+				SecurityGroups: []string{},
+				DeviceID:       "9ae135f4-b6e0-4dad-9e91-3c223e385824",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "port": {
+        "status": "ACTIVE",
+        "name": "",
+        "admin_state_up": true,
+        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+        "tenant_id": "7e02058126cc4950b75f9970368ba177",
+        "device_owner": "network:router_interface",
+        "mac_address": "fa:16:3e:23:fd:d7",
+        "fixed_ips": [
+            {
+                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+                "ip_address": "10.0.0.1"
+            }
+        ],
+        "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2",
+        "security_groups": [],
+        "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e"
+    }
+}
+			`)
+	})
+
+	n, err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Status, "ACTIVE")
+	th.AssertEquals(t, n.Name, "")
+	th.AssertEquals(t, n.AdminStateUp, true)
+	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
+	th.AssertEquals(t, n.TenantID, "7e02058126cc4950b75f9970368ba177")
+	th.AssertEquals(t, n.DeviceOwner, "network:router_interface")
+	th.AssertEquals(t, n.MACAddress, "fa:16:3e:23:fd:d7")
+	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
+		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"},
+	})
+	th.AssertEquals(t, n.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2")
+	th.AssertDeepEquals(t, n.SecurityGroups, []string{})
+	th.AssertEquals(t, n.Status, "ACTIVE")
+	th.AssertEquals(t, n.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e")
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "port": {
+        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+        "name": "private-port",
+        "admin_state_up": true,
+				"fixed_ips": [
+						{
+								"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+								"ip_address": "10.0.0.2"
+						}
+				],
+				"security_groups": ["foo"],
+        "allowed_address_pairs": [
+          {
+            "ip_address": "10.0.0.4",
+            "mac_address": "fa:16:3e:c9:cb:f0"
+          }
+        ]
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "port": {
+        "status": "DOWN",
+        "name": "private-port",
+        "admin_state_up": true,
+        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+        "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
+        "device_owner": "",
+        "mac_address": "fa:16:3e:c9:cb:f0",
+        "fixed_ips": [
+            {
+                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+                "ip_address": "10.0.0.2"
+            }
+        ],
+        "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
+        "security_groups": [
+            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
+        ],
+        "allowed_address_pairs": [
+          {
+            "ip_address": "10.0.0.4",
+            "mac_address": "fa:16:3e:c9:cb:f0"
+          }
+        ],
+        "device_id": ""
+    }
+}
+		`)
+	})
+
+	asu := true
+	options := ports.CreateOpts{
+		Name:         "private-port",
+		AdminStateUp: &asu,
+		NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+		FixedIPs: []ports.IP{
+			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
+		},
+		SecurityGroups: []string{"foo"},
+		AllowedAddressPairs: []ports.AddressPair{
+			{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
+		},
+	}
+	n, err := ports.Create(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Status, "DOWN")
+	th.AssertEquals(t, n.Name, "private-port")
+	th.AssertEquals(t, n.AdminStateUp, true)
+	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
+	th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
+	th.AssertEquals(t, n.DeviceOwner, "")
+	th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0")
+	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
+		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
+	})
+	th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
+	th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
+	th.AssertDeepEquals(t, n.AllowedAddressPairs, []ports.AddressPair{
+		{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
+	})
+}
+
+func TestRequiredCreateOpts(t *testing.T) {
+	res := ports.Create(fake.ServiceClient(), ports.CreateOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+		"port": {
+				"name": "new_port_name",
+				"fixed_ips": [
+            {
+                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+                "ip_address": "10.0.0.3"
+            }
+        ],
+        "allowed_address_pairs": [
+          {
+            "ip_address": "10.0.0.4",
+            "mac_address": "fa:16:3e:c9:cb:f0"
+          }
+        ],
+				"security_groups": [
+            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
+        ]
+		}
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "port": {
+        "status": "DOWN",
+        "name": "new_port_name",
+        "admin_state_up": true,
+        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+        "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
+        "device_owner": "",
+        "mac_address": "fa:16:3e:c9:cb:f0",
+        "fixed_ips": [
+            {
+                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+                "ip_address": "10.0.0.3"
+            }
+        ],
+        "allowed_address_pairs": [
+          {
+            "ip_address": "10.0.0.4",
+            "mac_address": "fa:16:3e:c9:cb:f0"
+          }
+        ],
+        "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
+        "security_groups": [
+            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
+        ],
+        "device_id": ""
+    }
+}
+		`)
+	})
+
+	options := ports.UpdateOpts{
+		Name: "new_port_name",
+		FixedIPs: []ports.IP{
+			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
+		},
+		SecurityGroups: []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"},
+		AllowedAddressPairs: []ports.AddressPair{
+			{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
+		},
+	}
+
+	s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, s.Name, "new_port_name")
+	th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
+		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
+	})
+	th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{
+		{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
+	})
+	th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := ports.Delete(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/ports/urls.go b/openstack/networking/v2/ports/urls.go
index 6d0572f..600d6f2 100644
--- a/openstack/networking/v2/ports/urls.go
+++ b/openstack/networking/v2/ports/urls.go
@@ -1,6 +1,6 @@
 package ports
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func resourceURL(c *gophercloud.ServiceClient, id string) string {
 	return c.ServiceURL("ports", id)
diff --git a/openstack/networking/v2/ports/urls_test.go b/openstack/networking/v2/ports/urls_test.go
deleted file mode 100644
index 7fadd4d..0000000
--- a/openstack/networking/v2/ports/urls_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package ports
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint, ResourceBase: endpoint + "v2.0/"}
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient())
-	expected := endpoint + "v2.0/ports"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "v2.0/ports/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "v2.0/ports"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient(), "foo")
-	expected := endpoint + "v2.0/ports/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "foo")
-	expected := endpoint + "v2.0/ports/foo"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/networking/v2/subnets/errors.go b/openstack/networking/v2/subnets/errors.go
deleted file mode 100644
index d2f7b46..0000000
--- a/openstack/networking/v2/subnets/errors.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package subnets
-
-import "fmt"
-
-func err(str string) error {
-	return fmt.Errorf("%s", str)
-}
-
-var (
-	errNetworkIDRequired    = err("A network ID is required")
-	errCIDRRequired         = err("A valid CIDR is required")
-	errInvalidIPType        = err("An IP type must either be 4 or 6")
-	errInvalidGatewayConfig = err("Both disabling the gateway and specifying a gateway is not allowed")
-)
diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go
index 8fa1e6d..769474d 100644
--- a/openstack/networking/v2/subnets/requests.go
+++ b/openstack/networking/v2/subnets/requests.go
@@ -1,23 +1,8 @@
 package subnets
 
 import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// AdminState gives users a solid type to work with for create and update
-// operations. It is recommended that users use the `Up` and `Down` enums.
-type AdminState *bool
-
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -49,10 +34,7 @@
 // ToSubnetListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToSubnetListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -71,25 +53,17 @@
 		}
 		url += query
 	}
-
 	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return SubnetPage{pagination.LinkedPageBase{PageResult: r}}
 	})
 }
 
 // Get retrieves a specific subnet based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
+	_, r.Err = c.Get(getURL(c, id), &r.Body, nil)
+	return
 }
 
-// Valid IP types
-const (
-	IPv4 = 4
-	IPv6 = 6
-)
-
 // CreateOptsBuilder is the interface options structs have to satisfy in order
 // to be used in the main Create operation in this package. Since many
 // extensions decorate or modify the common logic, it is useful for them to
@@ -100,86 +74,33 @@
 
 // CreateOpts represents the attributes used when creating a new subnet.
 type CreateOpts struct {
-	// Required
-	NetworkID string
-	CIDR      string
-	// Optional
-	Name            string
-	TenantID        string
-	AllocationPools []AllocationPool
-	GatewayIP       string
-	NoGateway       bool
-	IPVersion       int
-	EnableDHCP      *bool
-	DNSNameservers  []string
-	HostRoutes      []HostRoute
+	NetworkID       string                `json:"network_id" required:"true"`
+	CIDR            string                `json:"cidr" required:"true"`
+	Name            string                `json:"name,omitempty"`
+	TenantID        string                `json:"tenant_id,omitempty"`
+	AllocationPools []AllocationPool      `json:"allocation_pools,omitempty"`
+	GatewayIP       *string               `json:"gateway_ip"`
+	IPVersion       gophercloud.IPVersion `json:"ip_version,omitempty"`
+	EnableDHCP      *bool                 `json:"enable_dhcp,omitempty"`
+	DNSNameservers  []string              `json:"dns_nameservers,omitempty"`
+	HostRoutes      []HostRoute           `json:"host_routes,omitempty"`
 }
 
 // ToSubnetCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-
-	if opts.NetworkID == "" {
-		return nil, errNetworkIDRequired
-	}
-	if opts.CIDR == "" {
-		return nil, errCIDRRequired
-	}
-	if opts.IPVersion != 0 && opts.IPVersion != IPv4 && opts.IPVersion != IPv6 {
-		return nil, errInvalidIPType
-	}
-
-	// Both GatewayIP and NoGateway should not be set
-	if opts.GatewayIP != "" && opts.NoGateway {
-		return nil, errInvalidGatewayConfig
-	}
-
-	s["network_id"] = opts.NetworkID
-	s["cidr"] = opts.CIDR
-
-	if opts.EnableDHCP != nil {
-		s["enable_dhcp"] = &opts.EnableDHCP
-	}
-	if opts.Name != "" {
-		s["name"] = opts.Name
-	}
-	if opts.GatewayIP != "" {
-		s["gateway_ip"] = opts.GatewayIP
-	} else if opts.NoGateway {
-		s["gateway_ip"] = nil
-	}
-	if opts.TenantID != "" {
-		s["tenant_id"] = opts.TenantID
-	}
-	if opts.IPVersion != 0 {
-		s["ip_version"] = opts.IPVersion
-	}
-	if len(opts.AllocationPools) != 0 {
-		s["allocation_pools"] = opts.AllocationPools
-	}
-	if len(opts.DNSNameservers) != 0 {
-		s["dns_nameservers"] = opts.DNSNameservers
-	}
-	if len(opts.HostRoutes) != 0 {
-		s["host_routes"] = opts.HostRoutes
-	}
-
-	return map[string]interface{}{"subnet": s}, nil
+	return gophercloud.BuildRequestBody(opts, "subnet")
 }
 
 // Create accepts a CreateOpts struct and creates a new subnet using the values
 // provided. You must remember to provide a valid NetworkID, CIDR and IP version.
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToSubnetCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToSubnetCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(createURL(c), b, &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder allows extensions to add additional parameters to the
@@ -190,98 +111,65 @@
 
 // UpdateOpts represents the attributes used when updating an existing subnet.
 type UpdateOpts struct {
-	Name           string
-	GatewayIP      string
-	NoGateway      bool
-	DNSNameservers []string
-	HostRoutes     []HostRoute
-	EnableDHCP     *bool
+	Name           string      `json:"name,omitempty"`
+	GatewayIP      string      `json:"gateway_ip,omitempty"`
+	DNSNameservers []string    `json:"dns_nameservers,omitempty"`
+	HostRoutes     []HostRoute `json:"host_routes,omitempty"`
+	EnableDHCP     *bool       `json:"enable_dhcp,omitempty"`
 }
 
 // ToSubnetUpdateMap casts an UpdateOpts struct to a map.
 func (opts UpdateOpts) ToSubnetUpdateMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-
-	// Both GatewayIP and NoGateway should not be set
-	if opts.GatewayIP != "" && opts.NoGateway {
-		return nil, errInvalidGatewayConfig
-	}
-
-	if opts.EnableDHCP != nil {
-		s["enable_dhcp"] = &opts.EnableDHCP
-	}
-	if opts.Name != "" {
-		s["name"] = opts.Name
-	}
-	if opts.GatewayIP != "" {
-		s["gateway_ip"] = opts.GatewayIP
-	} else if opts.NoGateway {
-		s["gateway_ip"] = nil
-	}
-	if opts.DNSNameservers != nil {
-		s["dns_nameservers"] = opts.DNSNameservers
-	}
-	if opts.HostRoutes != nil {
-		s["host_routes"] = opts.HostRoutes
-	}
-
-	return map[string]interface{}{"subnet": s}, nil
+	return gophercloud.BuildRequestBody(opts, "subnet")
 }
 
 // Update accepts a UpdateOpts struct and updates an existing subnet using the
 // values provided.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToSubnetUpdateMap()
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToSubnetUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Put(updateURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201},
 	})
-
-	return res
+	return
 }
 
 // Delete accepts a unique ID and deletes the subnet associated with it.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(deleteURL(c, id), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
+	_, r.Err = c.Delete(deleteURL(c, id), nil)
+	return
 }
 
 // IDFromName is a convenience function that returns a subnet's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
-	subnetCount := 0
-	subnetID := ""
-	if name == "" {
-		return "", fmt.Errorf("A subnet name must be provided.")
+	count := 0
+	id := ""
+	pages, err := List(client, nil).AllPages()
+	if err != nil {
+		return "", err
 	}
-	pager := List(client, nil)
-	pager.EachPage(func(page pagination.Page) (bool, error) {
-		subnetList, err := ExtractSubnets(page)
-		if err != nil {
-			return false, err
-		}
 
-		for _, s := range subnetList {
-			if s.Name == name {
-				subnetCount++
-				subnetID = s.ID
-			}
-		}
-		return true, nil
-	})
+	all, err := ExtractSubnets(pages)
+	if err != nil {
+		return "", err
+	}
 
-	switch subnetCount {
+	for _, s := range all {
+		if s.Name == name {
+			count++
+			id = s.ID
+		}
+	}
+
+	switch count {
 	case 0:
-		return "", fmt.Errorf("Unable to find subnet: %s", name)
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "subnet"}
 	case 1:
-		return subnetID, nil
+		return id, nil
 	default:
-		return "", fmt.Errorf("Found %d subnets matching %s", subnetCount, name)
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "subnet"}
 	}
 }
diff --git a/openstack/networking/v2/subnets/requests_test.go b/openstack/networking/v2/subnets/requests_test.go
deleted file mode 100644
index 4a67a13..0000000
--- a/openstack/networking/v2/subnets/requests_test.go
+++ /dev/null
@@ -1,537 +0,0 @@
-package subnets
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "subnets": [
-        {
-            "name": "private-subnet",
-            "enable_dhcp": true,
-            "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-            "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
-            "dns_nameservers": [],
-            "allocation_pools": [
-                {
-                    "start": "10.0.0.2",
-                    "end": "10.0.0.254"
-                }
-            ],
-            "host_routes": [],
-            "ip_version": 4,
-            "gateway_ip": "10.0.0.1",
-            "cidr": "10.0.0.0/24",
-            "id": "08eae331-0402-425a-923c-34f7cfe39c1b"
-        },
-        {
-            "name": "my_subnet",
-            "enable_dhcp": true,
-            "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-            "dns_nameservers": [],
-            "allocation_pools": [
-                {
-                    "start": "192.0.0.2",
-                    "end": "192.255.255.254"
-                }
-            ],
-            "host_routes": [],
-            "ip_version": 4,
-            "gateway_ip": "192.0.0.1",
-            "cidr": "192.0.0.0/8",
-            "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-        },
-        {
-            "name": "my_gatewayless_subnet",
-            "enable_dhcp": true,
-            "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23",
-            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-            "dns_nameservers": [],
-            "allocation_pools": [
-                {
-                    "start": "192.168.1.2",
-                    "end": "192.168.1.254"
-                }
-            ],
-            "host_routes": [],
-            "ip_version": 4,
-            "gateway_ip": null,
-            "cidr": "192.168.1.0/24",
-            "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c"
-        }
-    ]
-}
-      `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractSubnets(page)
-		if err != nil {
-			t.Errorf("Failed to extract subnets: %v", err)
-			return false, nil
-		}
-
-		expected := []Subnet{
-			Subnet{
-				Name:           "private-subnet",
-				EnableDHCP:     true,
-				NetworkID:      "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-				TenantID:       "26a7980765d0414dbc1fc1f88cdb7e6e",
-				DNSNameservers: []string{},
-				AllocationPools: []AllocationPool{
-					AllocationPool{
-						Start: "10.0.0.2",
-						End:   "10.0.0.254",
-					},
-				},
-				HostRoutes: []HostRoute{},
-				IPVersion:  4,
-				GatewayIP:  "10.0.0.1",
-				CIDR:       "10.0.0.0/24",
-				ID:         "08eae331-0402-425a-923c-34f7cfe39c1b",
-			},
-			Subnet{
-				Name:           "my_subnet",
-				EnableDHCP:     true,
-				NetworkID:      "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-				TenantID:       "4fd44f30292945e481c7b8a0c8908869",
-				DNSNameservers: []string{},
-				AllocationPools: []AllocationPool{
-					AllocationPool{
-						Start: "192.0.0.2",
-						End:   "192.255.255.254",
-					},
-				},
-				HostRoutes: []HostRoute{},
-				IPVersion:  4,
-				GatewayIP:  "192.0.0.1",
-				CIDR:       "192.0.0.0/8",
-				ID:         "54d6f61d-db07-451c-9ab3-b9609b6b6f0b",
-			},
-			Subnet{
-				Name:           "my_gatewayless_subnet",
-				EnableDHCP:     true,
-				NetworkID:      "d32019d3-bc6e-4319-9c1d-6722fc136a23",
-				TenantID:       "4fd44f30292945e481c7b8a0c8908869",
-				DNSNameservers: []string{},
-				AllocationPools: []AllocationPool{
-					AllocationPool{
-						Start: "192.168.1.2",
-						End:   "192.168.1.254",
-					},
-				},
-				HostRoutes: []HostRoute{},
-				IPVersion:  4,
-				GatewayIP:  "",
-				CIDR:       "192.168.1.0/24",
-				ID:         "54d6f61d-db07-451c-9ab3-b9609b6b6f0c",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/subnets/54d6f61d-db07-451c-9ab3-b9609b6b6f0b", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "subnet": {
-        "name": "my_subnet",
-        "enable_dhcp": true,
-        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "dns_nameservers": [],
-        "allocation_pools": [
-            {
-                "start": "192.0.0.2",
-                "end": "192.255.255.254"
-            }
-        ],
-        "host_routes": [],
-        "ip_version": 4,
-        "gateway_ip": "192.0.0.1",
-        "cidr": "192.0.0.0/8",
-        "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-    }
-}
-			`)
-	})
-
-	s, err := Get(fake.ServiceClient(), "54d6f61d-db07-451c-9ab3-b9609b6b6f0b").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.Name, "my_subnet")
-	th.AssertEquals(t, s.EnableDHCP, true)
-	th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869")
-	th.AssertDeepEquals(t, s.DNSNameservers, []string{})
-	th.AssertDeepEquals(t, s.AllocationPools, []AllocationPool{
-		AllocationPool{
-			Start: "192.0.0.2",
-			End:   "192.255.255.254",
-		},
-	})
-	th.AssertDeepEquals(t, s.HostRoutes, []HostRoute{})
-	th.AssertEquals(t, s.IPVersion, 4)
-	th.AssertEquals(t, s.GatewayIP, "192.0.0.1")
-	th.AssertEquals(t, s.CIDR, "192.0.0.0/8")
-	th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0b")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "subnet": {
-        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-        "ip_version": 4,
-        "cidr": "192.168.199.0/24",
-				"dns_nameservers": ["foo"],
-				"allocation_pools": [
-						{
-								"start": "192.168.199.2",
-								"end": "192.168.199.254"
-						}
-				],
-				"host_routes": [{"destination":"","nexthop": "bar"}]
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "subnet": {
-        "name": "",
-        "enable_dhcp": true,
-        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "dns_nameservers": [],
-        "allocation_pools": [
-            {
-                "start": "192.168.199.2",
-                "end": "192.168.199.254"
-            }
-        ],
-        "host_routes": [],
-        "ip_version": 4,
-        "gateway_ip": "192.168.199.1",
-        "cidr": "192.168.199.0/24",
-        "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126"
-    }
-}
-		`)
-	})
-
-	opts := CreateOpts{
-		NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-		IPVersion: 4,
-		CIDR:      "192.168.199.0/24",
-		AllocationPools: []AllocationPool{
-			AllocationPool{
-				Start: "192.168.199.2",
-				End:   "192.168.199.254",
-			},
-		},
-		DNSNameservers: []string{"foo"},
-		HostRoutes: []HostRoute{
-			HostRoute{NextHop: "bar"},
-		},
-	}
-	s, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.Name, "")
-	th.AssertEquals(t, s.EnableDHCP, true)
-	th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869")
-	th.AssertDeepEquals(t, s.DNSNameservers, []string{})
-	th.AssertDeepEquals(t, s.AllocationPools, []AllocationPool{
-		AllocationPool{
-			Start: "192.168.199.2",
-			End:   "192.168.199.254",
-		},
-	})
-	th.AssertDeepEquals(t, s.HostRoutes, []HostRoute{})
-	th.AssertEquals(t, s.IPVersion, 4)
-	th.AssertEquals(t, s.GatewayIP, "192.168.199.1")
-	th.AssertEquals(t, s.CIDR, "192.168.199.0/24")
-	th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126")
-}
-
-func TestCreateNoGateway(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "subnet": {
-        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23",
-        "ip_version": 4,
-        "cidr": "192.168.1.0/24",
-				"gateway_ip": null,
-				"allocation_pools": [
-						{
-								"start": "192.168.1.2",
-								"end": "192.168.1.254"
-						}
-				]
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "subnet": {
-        "name": "",
-        "enable_dhcp": true,
-        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23",
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "allocation_pools": [
-            {
-                "start": "192.168.1.2",
-                "end": "192.168.1.254"
-            }
-        ],
-        "host_routes": [],
-        "ip_version": 4,
-        "gateway_ip": null,
-        "cidr": "192.168.1.0/24",
-        "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c"
-    }
-}
-		`)
-	})
-
-	opts := CreateOpts{
-		NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23",
-		IPVersion: 4,
-		CIDR:      "192.168.1.0/24",
-		NoGateway: true,
-		AllocationPools: []AllocationPool{
-			AllocationPool{
-				Start: "192.168.1.2",
-				End:   "192.168.1.254",
-			},
-		},
-		DNSNameservers: []string{},
-	}
-	s, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.Name, "")
-	th.AssertEquals(t, s.EnableDHCP, true)
-	th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a23")
-	th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869")
-	th.AssertDeepEquals(t, s.AllocationPools, []AllocationPool{
-		AllocationPool{
-			Start: "192.168.1.2",
-			End:   "192.168.1.254",
-		},
-	})
-	th.AssertDeepEquals(t, s.HostRoutes, []HostRoute{})
-	th.AssertEquals(t, s.IPVersion, 4)
-	th.AssertEquals(t, s.GatewayIP, "")
-	th.AssertEquals(t, s.CIDR, "192.168.1.0/24")
-	th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0c")
-}
-
-func TestCreateInvalidGatewayConfig(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "subnet": {
-        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23",
-        "ip_version": 4,
-        "cidr": "192.168.1.0/24",
-				"gateway_ip": "192.168.1.1",
-				"allocation_pools": [
-						{
-								"start": "192.168.1.2",
-								"end": "192.168.1.254"
-						}
-				]
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-	})
-
-	opts := CreateOpts{
-		NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23",
-		IPVersion: 4,
-		CIDR:      "192.168.1.0/24",
-		NoGateway: true,
-		GatewayIP: "192.168.1.1",
-		AllocationPools: []AllocationPool{
-			AllocationPool{
-				Start: "192.168.1.2",
-				End:   "192.168.1.254",
-			},
-		},
-		DNSNameservers: []string{},
-	}
-	_, err := Create(fake.ServiceClient(), opts).Extract()
-	if err == nil {
-		t.Fatalf("Expected an error, got none")
-	}
-
-	if err != errInvalidGatewayConfig {
-		t.Fatalf("Exected errInvalidGateway but got: %s", err)
-	}
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), CreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-
-	res = Create(fake.ServiceClient(), CreateOpts{NetworkID: "foo"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-
-	res = Create(fake.ServiceClient(), CreateOpts{NetworkID: "foo", CIDR: "bar", IPVersion: 40})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "subnet": {
-        "name": "my_new_subnet",
-				"dns_nameservers": ["foo"],
-				"host_routes": [{"destination":"","nexthop": "bar"}]
-    }
-}
-		`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "subnet": {
-        "name": "my_new_subnet",
-        "enable_dhcp": true,
-        "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-        "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
-        "dns_nameservers": [],
-        "allocation_pools": [
-            {
-                "start": "10.0.0.2",
-                "end": "10.0.0.254"
-            }
-        ],
-        "host_routes": [],
-        "ip_version": 4,
-        "gateway_ip": "10.0.0.1",
-        "cidr": "10.0.0.0/24",
-        "id": "08eae331-0402-425a-923c-34f7cfe39c1b"
-    }
-}
-	`)
-	})
-
-	opts := UpdateOpts{
-		Name:           "my_new_subnet",
-		DNSNameservers: []string{"foo"},
-		HostRoutes: []HostRoute{
-			HostRoute{NextHop: "bar"},
-		},
-	}
-	s, err := Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.Name, "my_new_subnet")
-	th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b")
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go
index 77b956a..ab5cce1 100644
--- a/openstack/networking/v2/subnets/results.go
+++ b/openstack/networking/v2/subnets/results.go
@@ -1,9 +1,8 @@
 package subnets
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 type commonResult struct {
@@ -12,17 +11,11 @@
 
 // Extract is a function that accepts a result and extracts a subnet resource.
 func (r commonResult) Extract() (*Subnet, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
+	var s struct {
 		Subnet *Subnet `json:"subnet"`
 	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Subnet, err
+	err := r.ExtractInto(&s)
+	return s.Subnet, err
 }
 
 // CreateResult represents the result of a create operation.
@@ -55,35 +48,35 @@
 // HostRoute represents a route that should be used by devices with IPs from
 // a subnet (not including local subnet route).
 type HostRoute struct {
-	DestinationCIDR string `mapstructure:"destination" json:"destination"`
-	NextHop         string `mapstructure:"nexthop" json:"nexthop"`
+	DestinationCIDR string `json:"destination"`
+	NextHop         string `json:"nexthop"`
 }
 
 // Subnet represents a subnet. See package documentation for a top-level
 // description of what this is.
 type Subnet struct {
 	// UUID representing the subnet
-	ID string `mapstructure:"id" json:"id"`
+	ID string `json:"id"`
 	// UUID of the parent network
-	NetworkID string `mapstructure:"network_id" json:"network_id"`
+	NetworkID string `json:"network_id"`
 	// Human-readable name for the subnet. Might not be unique.
-	Name string `mapstructure:"name" json:"name"`
+	Name string `json:"name"`
 	// IP version, either `4' or `6'
-	IPVersion int `mapstructure:"ip_version" json:"ip_version"`
+	IPVersion int `json:"ip_version"`
 	// CIDR representing IP range for this subnet, based on IP version
-	CIDR string `mapstructure:"cidr" json:"cidr"`
+	CIDR string `json:"cidr"`
 	// Default gateway used by devices in this subnet
-	GatewayIP string `mapstructure:"gateway_ip" json:"gateway_ip"`
+	GatewayIP string `json:"gateway_ip"`
 	// DNS name servers used by hosts in this subnet.
-	DNSNameservers []string `mapstructure:"dns_nameservers" json:"dns_nameservers"`
+	DNSNameservers []string `json:"dns_nameservers"`
 	// Sub-ranges of CIDR available for dynamic allocation to ports. See AllocationPool.
-	AllocationPools []AllocationPool `mapstructure:"allocation_pools" json:"allocation_pools"`
+	AllocationPools []AllocationPool `json:"allocation_pools"`
 	// Routes that should be used by devices with IPs from this subnet (not including local subnet route).
-	HostRoutes []HostRoute `mapstructure:"host_routes" json:"host_routes"`
+	HostRoutes []HostRoute `json:"host_routes"`
 	// Specifies whether DHCP is enabled for this subnet or not.
-	EnableDHCP bool `mapstructure:"enable_dhcp" json:"enable_dhcp"`
+	EnableDHCP bool `json:"enable_dhcp"`
 	// Owner of network. Only admin users can specify a tenant_id other than its own.
-	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+	TenantID string `json:"tenant_id"`
 }
 
 // SubnetPage is the page returned by a pager when traversing over a collection
@@ -95,38 +88,30 @@
 // NextPageURL is invoked when a paginated collection of subnets has reached
 // the end of a page and the pager seeks to traverse over a new one. In order
 // to do this, it needs to construct the next page's URL.
-func (p SubnetPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"subnets_links"`
+func (r SubnetPage) NextPageURL() (string, error) {
+	var s struct {
+		Links []gophercloud.Link `json:"subnets_links"`
 	}
-
-	var r resp
-	err := mapstructure.Decode(p.Body, &r)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-
-	return gophercloud.ExtractNextURL(r.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
 // IsEmpty checks whether a SubnetPage struct is empty.
-func (p SubnetPage) IsEmpty() (bool, error) {
-	is, err := ExtractSubnets(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
+func (r SubnetPage) IsEmpty() (bool, error) {
+	is, err := ExtractSubnets(r)
+	return len(is) == 0, err
 }
 
 // ExtractSubnets accepts a Page struct, specifically a SubnetPage struct,
 // and extracts the elements into a slice of Subnet structs. In other words,
 // a generic collection is mapped into a relevant slice.
-func ExtractSubnets(page pagination.Page) ([]Subnet, error) {
-	var resp struct {
-		Subnets []Subnet `mapstructure:"subnets" json:"subnets"`
+func ExtractSubnets(r pagination.Page) ([]Subnet, error) {
+	var s struct {
+		Subnets []Subnet `json:"subnets"`
 	}
-
-	err := mapstructure.Decode(page.(SubnetPage).Body, &resp)
-
-	return resp.Subnets, err
+	err := (r.(SubnetPage)).ExtractInto(&s)
+	return s.Subnets, err
 }
diff --git a/openstack/networking/v2/subnets/results_test.go b/openstack/networking/v2/subnets/results_test.go
deleted file mode 100644
index d404838..0000000
--- a/openstack/networking/v2/subnets/results_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package subnets
-
-import (
-	"encoding/json"
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"testing"
-)
-
-func TestHostRoute(t *testing.T) {
-	sejson := []byte(`
-    {"subnet": {
-      "name": "test-subnet",
-      "enable_dhcp": false,
-      "network_id": "3e66c41e-cbbd-4019-9aab-740b7e4150a0",
-      "tenant_id": "f86e123198cf42d19c8854c5f80c2f06",
-      "dns_nameservers": [],
-      "gateway_ip": "172.16.0.1",
-      "ipv6_ra_mode": null,
-      "allocation_pools": [
-        {
-          "start": "172.16.0.2",
-          "end": "172.16.255.254"
-        }
-      ],
-      "host_routes": [
-        {
-          "destination": "172.20.1.0/24",
-		  "nexthop": "172.16.0.2"
-        }
-      ],
-      "ip_version": 4,
-      "ipv6_address_mode": null,
-      "cidr": "172.16.0.0/16",
-      "id": "6dcaa873-7115-41af-9ef5-915f73636e43",
-      "subnetpool_id": null
-  }}
-`)
-
-	var dejson interface{}
-	err := json.Unmarshal(sejson, &dejson)
-	if err != nil {
-		t.Fatalf("%s", err)
-	}
-
-	resp := commonResult{gophercloud.Result{Body: dejson}}
-	subnet, err := resp.Extract()
-	if err != nil {
-		t.Fatalf("%s", err)
-	}
-	route := subnet.HostRoutes[0]
-	th.AssertEquals(t, route.NextHop, "172.16.0.2")
-	th.AssertEquals(t, route.DestinationCIDR, "172.20.1.0/24")
-}
diff --git a/openstack/networking/v2/subnets/testing/doc.go b/openstack/networking/v2/subnets/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/networking/v2/subnets/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go
new file mode 100644
index 0000000..13fa9df
--- /dev/null
+++ b/openstack/networking/v2/subnets/testing/requests_test.go
@@ -0,0 +1,484 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "subnets": [
+        {
+            "name": "private-subnet",
+            "enable_dhcp": true,
+            "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+            "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
+            "dns_nameservers": [],
+            "allocation_pools": [
+                {
+                    "start": "10.0.0.2",
+                    "end": "10.0.0.254"
+                }
+            ],
+            "host_routes": [],
+            "ip_version": 4,
+            "gateway_ip": "10.0.0.1",
+            "cidr": "10.0.0.0/24",
+            "id": "08eae331-0402-425a-923c-34f7cfe39c1b"
+        },
+        {
+            "name": "my_subnet",
+            "enable_dhcp": true,
+            "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+            "dns_nameservers": [],
+            "allocation_pools": [
+                {
+                    "start": "192.0.0.2",
+                    "end": "192.255.255.254"
+                }
+            ],
+            "host_routes": [],
+            "ip_version": 4,
+            "gateway_ip": "192.0.0.1",
+            "cidr": "192.0.0.0/8",
+            "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+        },
+        {
+            "name": "my_gatewayless_subnet",
+            "enable_dhcp": true,
+            "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23",
+            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+            "dns_nameservers": [],
+            "allocation_pools": [
+                {
+                    "start": "192.168.1.2",
+                    "end": "192.168.1.254"
+                }
+            ],
+            "host_routes": [],
+            "ip_version": 4,
+            "gateway_ip": null,
+            "cidr": "192.168.1.0/24",
+            "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c"
+        }
+    ]
+}
+      `)
+	})
+
+	count := 0
+
+	subnets.List(fake.ServiceClient(), subnets.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := subnets.ExtractSubnets(page)
+		if err != nil {
+			t.Errorf("Failed to extract subnets: %v", err)
+			return false, nil
+		}
+
+		expected := []subnets.Subnet{
+			{
+				Name:           "private-subnet",
+				EnableDHCP:     true,
+				NetworkID:      "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+				TenantID:       "26a7980765d0414dbc1fc1f88cdb7e6e",
+				DNSNameservers: []string{},
+				AllocationPools: []subnets.AllocationPool{
+					{
+						Start: "10.0.0.2",
+						End:   "10.0.0.254",
+					},
+				},
+				HostRoutes: []subnets.HostRoute{},
+				IPVersion:  4,
+				GatewayIP:  "10.0.0.1",
+				CIDR:       "10.0.0.0/24",
+				ID:         "08eae331-0402-425a-923c-34f7cfe39c1b",
+			},
+			{
+				Name:           "my_subnet",
+				EnableDHCP:     true,
+				NetworkID:      "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+				TenantID:       "4fd44f30292945e481c7b8a0c8908869",
+				DNSNameservers: []string{},
+				AllocationPools: []subnets.AllocationPool{
+					{
+						Start: "192.0.0.2",
+						End:   "192.255.255.254",
+					},
+				},
+				HostRoutes: []subnets.HostRoute{},
+				IPVersion:  4,
+				GatewayIP:  "192.0.0.1",
+				CIDR:       "192.0.0.0/8",
+				ID:         "54d6f61d-db07-451c-9ab3-b9609b6b6f0b",
+			},
+			subnets.Subnet{
+				Name:           "my_gatewayless_subnet",
+				EnableDHCP:     true,
+				NetworkID:      "d32019d3-bc6e-4319-9c1d-6722fc136a23",
+				TenantID:       "4fd44f30292945e481c7b8a0c8908869",
+				DNSNameservers: []string{},
+				AllocationPools: []subnets.AllocationPool{
+					{
+						Start: "192.168.1.2",
+						End:   "192.168.1.254",
+					},
+				},
+				HostRoutes: []subnets.HostRoute{},
+				IPVersion:  4,
+				GatewayIP:  "",
+				CIDR:       "192.168.1.0/24",
+				ID:         "54d6f61d-db07-451c-9ab3-b9609b6b6f0c",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/subnets/54d6f61d-db07-451c-9ab3-b9609b6b6f0b", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "subnet": {
+        "name": "my_subnet",
+        "enable_dhcp": true,
+        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "dns_nameservers": [],
+        "allocation_pools": [
+            {
+                "start": "192.0.0.2",
+                "end": "192.255.255.254"
+            }
+        ],
+        "host_routes": [],
+        "ip_version": 4,
+        "gateway_ip": "192.0.0.1",
+        "cidr": "192.0.0.0/8",
+        "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+    }
+}
+			`)
+	})
+
+	s, err := subnets.Get(fake.ServiceClient(), "54d6f61d-db07-451c-9ab3-b9609b6b6f0b").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, s.Name, "my_subnet")
+	th.AssertEquals(t, s.EnableDHCP, true)
+	th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869")
+	th.AssertDeepEquals(t, s.DNSNameservers, []string{})
+	th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{
+		{
+			Start: "192.0.0.2",
+			End:   "192.255.255.254",
+		},
+	})
+	th.AssertDeepEquals(t, s.HostRoutes, []subnets.HostRoute{})
+	th.AssertEquals(t, s.IPVersion, 4)
+	th.AssertEquals(t, s.GatewayIP, "192.0.0.1")
+	th.AssertEquals(t, s.CIDR, "192.0.0.0/8")
+	th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0b")
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "subnet": {
+        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+        "ip_version": 4,
+				"gateway_ip": null,
+        "cidr": "192.168.199.0/24",
+				"dns_nameservers": ["foo"],
+				"allocation_pools": [
+						{
+								"start": "192.168.199.2",
+								"end": "192.168.199.254"
+						}
+				],
+				"host_routes": [{"destination":"","nexthop": "bar"}]
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "subnet": {
+        "name": "",
+        "enable_dhcp": true,
+        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "dns_nameservers": [],
+        "allocation_pools": [
+            {
+                "start": "192.168.199.2",
+                "end": "192.168.199.254"
+            }
+        ],
+        "host_routes": [],
+        "ip_version": 4,
+        "gateway_ip": "192.168.199.1",
+        "cidr": "192.168.199.0/24",
+        "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126"
+    }
+}
+		`)
+	})
+
+	opts := subnets.CreateOpts{
+		NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+		IPVersion: 4,
+		CIDR:      "192.168.199.0/24",
+		AllocationPools: []subnets.AllocationPool{
+			{
+				Start: "192.168.199.2",
+				End:   "192.168.199.254",
+			},
+		},
+		DNSNameservers: []string{"foo"},
+		HostRoutes: []subnets.HostRoute{
+			{NextHop: "bar"},
+		},
+	}
+	s, err := subnets.Create(fake.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, s.Name, "")
+	th.AssertEquals(t, s.EnableDHCP, true)
+	th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869")
+	th.AssertDeepEquals(t, s.DNSNameservers, []string{})
+	th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{
+		{
+			Start: "192.168.199.2",
+			End:   "192.168.199.254",
+		},
+	})
+	th.AssertDeepEquals(t, s.HostRoutes, []subnets.HostRoute{})
+	th.AssertEquals(t, s.IPVersion, 4)
+	th.AssertEquals(t, s.GatewayIP, "192.168.199.1")
+	th.AssertEquals(t, s.CIDR, "192.168.199.0/24")
+	th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126")
+}
+
+func TestCreateNoGateway(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "subnet": {
+        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23",
+        "ip_version": 4,
+        "cidr": "192.168.1.0/24",
+				"gateway_ip": null,
+				"allocation_pools": [
+						{
+								"start": "192.168.1.2",
+								"end": "192.168.1.254"
+						}
+				]
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "subnet": {
+        "name": "",
+        "enable_dhcp": true,
+        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23",
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "allocation_pools": [
+            {
+                "start": "192.168.1.2",
+                "end": "192.168.1.254"
+            }
+        ],
+        "host_routes": [],
+        "ip_version": 4,
+        "gateway_ip": null,
+        "cidr": "192.168.1.0/24",
+        "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c"
+    }
+}
+		`)
+	})
+
+	opts := subnets.CreateOpts{
+		NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23",
+		IPVersion: 4,
+		CIDR:      "192.168.1.0/24",
+		AllocationPools: []subnets.AllocationPool{
+			{
+				Start: "192.168.1.2",
+				End:   "192.168.1.254",
+			},
+		},
+		DNSNameservers: []string{},
+	}
+	s, err := subnets.Create(fake.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, s.Name, "")
+	th.AssertEquals(t, s.EnableDHCP, true)
+	th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a23")
+	th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869")
+	th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{
+		{
+			Start: "192.168.1.2",
+			End:   "192.168.1.254",
+		},
+	})
+	th.AssertDeepEquals(t, s.HostRoutes, []subnets.HostRoute{})
+	th.AssertEquals(t, s.IPVersion, 4)
+	th.AssertEquals(t, s.GatewayIP, "")
+	th.AssertEquals(t, s.CIDR, "192.168.1.0/24")
+	th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0c")
+}
+
+func TestRequiredCreateOpts(t *testing.T) {
+	res := subnets.Create(fake.ServiceClient(), subnets.CreateOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+
+	res = subnets.Create(fake.ServiceClient(), subnets.CreateOpts{NetworkID: "foo"})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+
+	res = subnets.Create(fake.ServiceClient(), subnets.CreateOpts{NetworkID: "foo", CIDR: "bar", IPVersion: 40})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "subnet": {
+        "name": "my_new_subnet",
+				"dns_nameservers": ["foo"],
+				"host_routes": [{"destination":"","nexthop": "bar"}]
+    }
+}
+		`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "subnet": {
+        "name": "my_new_subnet",
+        "enable_dhcp": true,
+        "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+        "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
+        "dns_nameservers": [],
+        "allocation_pools": [
+            {
+                "start": "10.0.0.2",
+                "end": "10.0.0.254"
+            }
+        ],
+        "host_routes": [],
+        "ip_version": 4,
+        "gateway_ip": "10.0.0.1",
+        "cidr": "10.0.0.0/24",
+        "id": "08eae331-0402-425a-923c-34f7cfe39c1b"
+    }
+}
+	`)
+	})
+
+	opts := subnets.UpdateOpts{
+		Name:           "my_new_subnet",
+		DNSNameservers: []string{"foo"},
+		HostRoutes: []subnets.HostRoute{
+			{NextHop: "bar"},
+		},
+	}
+	s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, s.Name, "my_new_subnet")
+	th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b")
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := subnets.Delete(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/subnets/testing/results_test.go b/openstack/networking/v2/subnets/testing/results_test.go
new file mode 100644
index 0000000..a227ccd
--- /dev/null
+++ b/openstack/networking/v2/subnets/testing/results_test.go
@@ -0,0 +1,59 @@
+package testing
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestHostRoute(t *testing.T) {
+	sejson := []byte(`
+    {"subnet": {
+      "name": "test-subnet",
+      "enable_dhcp": false,
+      "network_id": "3e66c41e-cbbd-4019-9aab-740b7e4150a0",
+      "tenant_id": "f86e123198cf42d19c8854c5f80c2f06",
+      "dns_nameservers": [],
+      "gateway_ip": "172.16.0.1",
+      "ipv6_ra_mode": null,
+      "allocation_pools": [
+        {
+          "start": "172.16.0.2",
+          "end": "172.16.255.254"
+        }
+      ],
+      "host_routes": [
+        {
+          "destination": "172.20.1.0/24",
+		  		"nexthop": "172.16.0.2"
+        }
+      ],
+      "ip_version": 4,
+      "ipv6_address_mode": null,
+      "cidr": "172.16.0.0/16",
+      "id": "6dcaa873-7115-41af-9ef5-915f73636e43",
+      "subnetpool_id": null
+  }}
+`)
+
+	var dejson interface{}
+	err := json.Unmarshal(sejson, &dejson)
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+
+	resp := gophercloud.Result{Body: dejson}
+	var subnetWrapper struct {
+		Subnet subnets.Subnet `json:"subnet"`
+	}
+	err = resp.ExtractInto(&subnetWrapper)
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+	route := subnetWrapper.Subnet.HostRoutes[0]
+	th.AssertEquals(t, route.NextHop, "172.16.0.2")
+	th.AssertEquals(t, route.DestinationCIDR, "172.20.1.0/24")
+}
diff --git a/openstack/networking/v2/subnets/urls.go b/openstack/networking/v2/subnets/urls.go
index 0d02368..7a4f2f7 100644
--- a/openstack/networking/v2/subnets/urls.go
+++ b/openstack/networking/v2/subnets/urls.go
@@ -1,6 +1,6 @@
 package subnets
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func resourceURL(c *gophercloud.ServiceClient, id string) string {
 	return c.ServiceURL("subnets", id)
diff --git a/openstack/networking/v2/subnets/urls_test.go b/openstack/networking/v2/subnets/urls_test.go
deleted file mode 100644
index aeeddf3..0000000
--- a/openstack/networking/v2/subnets/urls_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package subnets
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint, ResourceBase: endpoint + "v2.0/"}
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient())
-	expected := endpoint + "v2.0/subnets"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "v2.0/subnets/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "v2.0/subnets"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient(), "foo")
-	expected := endpoint + "v2.0/subnets/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "foo")
-	expected := endpoint + "v2.0/subnets/foo"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/openstack/objectstorage/v1/accounts/fixtures.go b/openstack/objectstorage/v1/accounts/fixtures.go
deleted file mode 100644
index f22b687..0000000
--- a/openstack/objectstorage/v1/accounts/fixtures.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// +build fixtures
-
-package accounts
-
-import (
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// HandleGetAccountSuccessfully creates an HTTP handler at `/` on the test handler mux that
-// responds with a `Get` response.
-func HandleGetAccountSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "HEAD")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Set("X-Account-Container-Count", "2")
-		w.Header().Set("X-Account-Bytes-Used", "14")
-		w.Header().Set("X-Account-Meta-Subject", "books")
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleUpdateAccountSuccessfully creates an HTTP handler at `/` on the test handler mux that
-// responds with a `Update` response.
-func HandleUpdateAccountSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "X-Account-Meta-Gophercloud-Test", "accounts")
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
diff --git a/openstack/objectstorage/v1/accounts/requests.go b/openstack/objectstorage/v1/accounts/requests.go
index 79eff60..b5beef2 100644
--- a/openstack/objectstorage/v1/accounts/requests.go
+++ b/openstack/objectstorage/v1/accounts/requests.go
@@ -1,6 +1,6 @@
 package accounts
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 // GetOptsBuilder allows extensions to add additional headers to the Get
 // request.
@@ -23,31 +23,27 @@
 // custom metadata, call the ExtractMetadata method on the GetResult. To extract
 // all the headers that are returned (including the metadata), call the
 // ExtractHeader method on the GetResult.
-func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) GetResult {
-	var res GetResult
+func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) {
 	h := make(map[string]string)
-
 	if opts != nil {
 		headers, err := opts.ToAccountGetMap()
 		if err != nil {
-			res.Err = err
-			return res
+			r.Err = err
+			return
 		}
-
 		for k, v := range headers {
 			h[k] = v
 		}
 	}
-
-	resp, err := c.Request("HEAD", getURL(c), gophercloud.RequestOpts{
+	resp, err := c.Request("HEAD", getURL(c), &gophercloud.RequestOpts{
 		MoreHeaders: h,
 		OkCodes:     []int{204},
 	})
 	if resp != nil {
-		res.Header = resp.Header
+		r.Header = resp.Header
 	}
-	res.Err = err
-	return res
+	r.Err = err
+	return
 }
 
 // UpdateOptsBuilder allows extensions to add additional headers to the Update
@@ -80,28 +76,25 @@
 
 // Update is a function that creates, updates, or deletes an account's metadata.
 // To extract the headers returned, call the Extract method on the UpdateResult.
-func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
+func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) (r UpdateResult) {
 	h := make(map[string]string)
-
 	if opts != nil {
 		headers, err := opts.ToAccountUpdateMap()
 		if err != nil {
-			res.Err = err
-			return res
+			r.Err = err
+			return
 		}
 		for k, v := range headers {
 			h[k] = v
 		}
 	}
-
-	resp, err := c.Request("POST", updateURL(c), gophercloud.RequestOpts{
+	resp, err := c.Request("POST", updateURL(c), &gophercloud.RequestOpts{
 		MoreHeaders: h,
 		OkCodes:     []int{201, 202, 204},
 	})
 	if resp != nil {
-		res.Header = resp.Header
+		r.Header = resp.Header
 	}
-	res.Err = err
-	return res
+	r.Err = err
+	return
 }
diff --git a/openstack/objectstorage/v1/accounts/requests_test.go b/openstack/objectstorage/v1/accounts/requests_test.go
deleted file mode 100644
index 6454c0a..0000000
--- a/openstack/objectstorage/v1/accounts/requests_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package accounts
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestUpdateAccount(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleUpdateAccountSuccessfully(t)
-
-	options := &UpdateOpts{Metadata: map[string]string{"gophercloud-test": "accounts"}}
-	res := Update(fake.ServiceClient(), options)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestGetAccount(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetAccountSuccessfully(t)
-
-	expectedMetadata := map[string]string{"Subject": "books"}
-	res := Get(fake.ServiceClient(), &GetOpts{})
-	th.AssertNoErr(t, res.Err)
-	actualMetadata, _ := res.ExtractMetadata()
-	th.CheckDeepEquals(t, expectedMetadata, actualMetadata)
-	//headers, err := res.Extract()
-	//th.AssertNoErr(t, err)
-}
diff --git a/openstack/objectstorage/v1/accounts/results.go b/openstack/objectstorage/v1/accounts/results.go
index 6ab1a23..f9e5fcd 100644
--- a/openstack/objectstorage/v1/accounts/results.go
+++ b/openstack/objectstorage/v1/accounts/results.go
@@ -2,9 +2,8 @@
 
 import (
 	"strings"
-	"time"
 
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 // UpdateResult is returned from a call to the Update function.
@@ -14,46 +13,31 @@
 
 // UpdateHeader represents the headers returned in the response from an Update request.
 type UpdateHeader struct {
-	ContentLength string    `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // Extract will return a struct of headers returned from a call to Get. To obtain
 // a map of headers, call the ExtractHeader method on the GetResult.
-func (ur UpdateResult) Extract() (UpdateHeader, error) {
-	var uh UpdateHeader
-	if ur.Err != nil {
-		return uh, ur.Err
-	}
-
-	if err := gophercloud.DecodeHeader(ur.Header, &uh); err != nil {
-		return uh, err
-	}
-
-	if date, ok := ur.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, ur.Header["Date"][0])
-		if err != nil {
-			return uh, err
-		}
-		uh.Date = t
-	}
-
-	return uh, nil
+func (ur UpdateResult) Extract() (*UpdateHeader, error) {
+	var uh *UpdateHeader
+	err := ur.ExtractInto(&uh)
+	return uh, err
 }
 
 // GetHeader represents the headers returned in the response from a Get request.
 type GetHeader struct {
-	BytesUsed      int64     `mapstructure:"X-Account-Bytes-Used"`
-	ContainerCount int       `mapstructure:"X-Account-Container-Count"`
-	ContentLength  int64     `mapstructure:"Content-Length"`
-	ContentType    string    `mapstructure:"Content-Type"`
-	Date           time.Time `mapstructure:"-"`
-	ObjectCount    int64     `mapstructure:"X-Account-Object-Count"`
-	TransID        string    `mapstructure:"X-Trans-Id"`
-	TempURLKey     string    `mapstructure:"X-Account-Meta-Temp-URL-Key"`
-	TempURLKey2    string    `mapstructure:"X-Account-Meta-Temp-URL-Key-2"`
+	BytesUsed      string                  `json:"X-Account-Bytes-Used"`
+	ContainerCount string                  `json:"X-Account-Container-Count"`
+	ContentLength  string                  `json:"Content-Length"`
+	ContentType    string                  `json:"Content-Type"`
+	Date           gophercloud.JSONRFC1123 `json:"Date"`
+	ObjectCount    string                  `json:"X-Account-Object-Count"`
+	TransID        string                  `json:"X-Trans-Id"`
+	TempURLKey     string                  `json:"X-Account-Meta-Temp-URL-Key"`
+	TempURLKey2    string                  `json:"X-Account-Meta-Temp-URL-Key-2"`
 }
 
 // GetResult is returned from a call to the Get function.
@@ -63,36 +47,21 @@
 
 // Extract will return a struct of headers returned from a call to Get. To obtain
 // a map of headers, call the ExtractHeader method on the GetResult.
-func (gr GetResult) Extract() (GetHeader, error) {
-	var gh GetHeader
-	if gr.Err != nil {
-		return gh, gr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(gr.Header, &gh); err != nil {
-		return gh, err
-	}
-
-	if date, ok := gr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, gr.Header["Date"][0])
-		if err != nil {
-			return gh, err
-		}
-		gh.Date = t
-	}
-
-	return gh, nil
+func (r GetResult) Extract() (*GetHeader, error) {
+	var s *GetHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // ExtractMetadata is a function that takes a GetResult (of type *http.Response)
 // and returns the custom metatdata associated with the account.
-func (gr GetResult) ExtractMetadata() (map[string]string, error) {
-	if gr.Err != nil {
-		return nil, gr.Err
+func (r GetResult) ExtractMetadata() (map[string]string, error) {
+	if r.Err != nil {
+		return nil, r.Err
 	}
 
 	metadata := make(map[string]string)
-	for k, v := range gr.Header {
+	for k, v := range r.Header {
 		if strings.HasPrefix(k, "X-Account-Meta-") {
 			key := strings.TrimPrefix(k, "X-Account-Meta-")
 			metadata[key] = v[0]
diff --git a/openstack/objectstorage/v1/accounts/testing/doc.go b/openstack/objectstorage/v1/accounts/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/objectstorage/v1/accounts/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/objectstorage/v1/accounts/testing/fixtures.go b/openstack/objectstorage/v1/accounts/testing/fixtures.go
new file mode 100644
index 0000000..a265199
--- /dev/null
+++ b/openstack/objectstorage/v1/accounts/testing/fixtures.go
@@ -0,0 +1,38 @@
+package testing
+
+import (
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// HandleGetAccountSuccessfully creates an HTTP handler at `/` on the test handler mux that
+// responds with a `Get` response.
+func HandleGetAccountSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "HEAD")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Set("X-Account-Container-Count", "2")
+		w.Header().Set("X-Account-Bytes-Used", "14")
+		w.Header().Set("X-Account-Meta-Subject", "books")
+		w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT")
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleUpdateAccountSuccessfully creates an HTTP handler at `/` on the test handler mux that
+// responds with a `Update` response.
+func HandleUpdateAccountSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "X-Account-Meta-Gophercloud-Test", "accounts")
+
+		w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT")
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
diff --git a/openstack/objectstorage/v1/accounts/testing/requests_test.go b/openstack/objectstorage/v1/accounts/testing/requests_test.go
new file mode 100644
index 0000000..cf3fe05
--- /dev/null
+++ b/openstack/objectstorage/v1/accounts/testing/requests_test.go
@@ -0,0 +1,33 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestUpdateAccount(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleUpdateAccountSuccessfully(t)
+
+	options := &accounts.UpdateOpts{Metadata: map[string]string{"gophercloud-test": "accounts"}}
+	_, err := accounts.Update(fake.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+}
+
+func TestGetAccount(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetAccountSuccessfully(t)
+
+	expectedMetadata := map[string]string{"Subject": "books"}
+	res := accounts.Get(fake.ServiceClient(), &accounts.GetOpts{})
+	th.AssertNoErr(t, res.Err)
+	actualMetadata, _ := res.ExtractMetadata()
+	th.CheckDeepEquals(t, expectedMetadata, actualMetadata)
+	_, err := res.Extract()
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/objectstorage/v1/accounts/urls.go b/openstack/objectstorage/v1/accounts/urls.go
index 9952fe4..71540b1 100644
--- a/openstack/objectstorage/v1/accounts/urls.go
+++ b/openstack/objectstorage/v1/accounts/urls.go
@@ -1,6 +1,6 @@
 package accounts
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func getURL(c *gophercloud.ServiceClient) string {
 	return c.Endpoint
diff --git a/openstack/objectstorage/v1/accounts/urls_test.go b/openstack/objectstorage/v1/accounts/urls_test.go
deleted file mode 100644
index 074d52d..0000000
--- a/openstack/objectstorage/v1/accounts/urls_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package accounts
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient())
-	expected := endpoint
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient())
-	expected := endpoint
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/openstack/objectstorage/v1/containers/fixtures.go b/openstack/objectstorage/v1/containers/fixtures.go
deleted file mode 100644
index e607352..0000000
--- a/openstack/objectstorage/v1/containers/fixtures.go
+++ /dev/null
@@ -1,143 +0,0 @@
-// +build fixtures
-
-package containers
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// ExpectedListInfo is the result expected from a call to `List` when full
-// info is requested.
-var ExpectedListInfo = []Container{
-	Container{
-		Count: 0,
-		Bytes: 0,
-		Name:  "janeausten",
-	},
-	Container{
-		Count: 1,
-		Bytes: 14,
-		Name:  "marktwain",
-	},
-}
-
-// ExpectedListNames is the result expected from a call to `List` when just
-// container names are requested.
-var ExpectedListNames = []string{"janeausten", "marktwain"}
-
-// HandleListContainerInfoSuccessfully creates an HTTP handler at `/` on the test handler mux that
-// responds with a `List` response when full info is requested.
-func HandleListContainerInfoSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, `[
-        {
-          "count": 0,
-          "bytes": 0,
-          "name": "janeausten"
-        },
-        {
-          "count": 1,
-          "bytes": 14,
-          "name": "marktwain"
-        }
-      ]`)
-		case "janeausten":
-			fmt.Fprintf(w, `[
-				{
-					"count": 1,
-					"bytes": 14,
-					"name": "marktwain"
-				}
-			]`)
-		case "marktwain":
-			fmt.Fprintf(w, `[]`)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-}
-
-// HandleListContainerNamesSuccessfully creates an HTTP handler at `/` on the test handler mux that
-// responds with a `ListNames` response when only container names are requested.
-func HandleListContainerNamesSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "text/plain")
-
-		w.Header().Set("Content-Type", "text/plain")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, "janeausten\nmarktwain\n")
-		case "janeausten":
-			fmt.Fprintf(w, "marktwain\n")
-		case "marktwain":
-			fmt.Fprintf(w, ``)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-}
-
-// HandleCreateContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
-// responds with a `Create` response.
-func HandleCreateContainerSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Add("X-Container-Meta-Foo", "bar")
-		w.Header().Add("X-Trans-Id", "1234567")
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleDeleteContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
-// responds with a `Delete` response.
-func HandleDeleteContainerSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleUpdateContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
-// responds with a `Update` response.
-func HandleUpdateContainerSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleGetContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
-// responds with a `Get` response.
-func HandleGetContainerSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "HEAD")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go
index cd8e82b..a668673 100644
--- a/openstack/objectstorage/v1/containers/requests.go
+++ b/openstack/objectstorage/v1/containers/requests.go
@@ -1,8 +1,8 @@
 package containers
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the List
@@ -26,10 +26,7 @@
 // representing whether to list complete information for each container.
 func (opts ListOpts) ToContainerListParams() (bool, string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return false, "", err
-	}
-	return opts.Full, q.String(), nil
+	return opts.Full, q.String(), err
 }
 
 // List is a function that retrieves containers associated with the account as
@@ -51,13 +48,11 @@
 		}
 	}
 
-	createPage := func(r pagination.PageResult) pagination.Page {
+	pager := pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		p := ContainerPage{pagination.MarkerPageBase{PageResult: r}}
 		p.MarkerPageBase.Owner = p
 		return p
-	}
-
-	pager := pagination.NewPager(c, url, createPage)
+	})
 	pager.Headers = headers
 	return pager
 }
@@ -94,38 +89,33 @@
 }
 
 // Create is a function that creates a new container.
-func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
+func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsBuilder) (r CreateResult) {
 	h := make(map[string]string)
-
 	if opts != nil {
 		headers, err := opts.ToContainerCreateMap()
 		if err != nil {
-			res.Err = err
-			return res
+			r.Err = err
+			return
 		}
-
 		for k, v := range headers {
 			h[k] = v
 		}
 	}
-
-	resp, err := c.Request("PUT", createURL(c, containerName), gophercloud.RequestOpts{
+	resp, err := c.Request("PUT", createURL(c, containerName), &gophercloud.RequestOpts{
 		MoreHeaders: h,
 		OkCodes:     []int{201, 202, 204},
 	})
 	if resp != nil {
-		res.Header = resp.Header
+		r.Header = resp.Header
 	}
-	res.Err = err
-	return res
+	r.Err = err
+	return
 }
 
 // Delete is a function that deletes a container.
-func Delete(c *gophercloud.ServiceClient, containerName string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(deleteURL(c, containerName), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, containerName string) (r DeleteResult) {
+	_, r.Err = c.Delete(deleteURL(c, containerName), nil)
+	return
 }
 
 // UpdateOptsBuilder allows extensions to add additional parameters to the
@@ -162,44 +152,40 @@
 
 // Update is a function that creates, updates, or deletes a container's
 // metadata.
-func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
+func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsBuilder) (r UpdateResult) {
 	h := make(map[string]string)
-
 	if opts != nil {
 		headers, err := opts.ToContainerUpdateMap()
 		if err != nil {
-			res.Err = err
-			return res
+			r.Err = err
+			return
 		}
 
 		for k, v := range headers {
 			h[k] = v
 		}
 	}
-
-	resp, err := c.Request("POST", updateURL(c, containerName), gophercloud.RequestOpts{
+	resp, err := c.Request("POST", updateURL(c, containerName), &gophercloud.RequestOpts{
 		MoreHeaders: h,
 		OkCodes:     []int{201, 202, 204},
 	})
 	if resp != nil {
-		res.Header = resp.Header
+		r.Header = resp.Header
 	}
-	res.Err = err
-	return res
+	r.Err = err
+	return
 }
 
 // Get is a function that retrieves the metadata of a container. To extract just
 // the custom metadata, pass the GetResult response to the ExtractMetadata
 // function.
-func Get(c *gophercloud.ServiceClient, containerName string) GetResult {
-	var res GetResult
-	resp, err := c.Request("HEAD", getURL(c, containerName), gophercloud.RequestOpts{
+func Get(c *gophercloud.ServiceClient, containerName string) (r GetResult) {
+	resp, err := c.Request("HEAD", getURL(c, containerName), &gophercloud.RequestOpts{
 		OkCodes: []int{200, 204},
 	})
 	if resp != nil {
-		res.Header = resp.Header
+		r.Header = resp.Header
 	}
-	res.Err = err
-	return res
+	r.Err = err
+	return
 }
diff --git a/openstack/objectstorage/v1/containers/requests_test.go b/openstack/objectstorage/v1/containers/requests_test.go
deleted file mode 100644
index 0ccd5a7..0000000
--- a/openstack/objectstorage/v1/containers/requests_test.go
+++ /dev/null
@@ -1,117 +0,0 @@
-package containers
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-var metadata = map[string]string{"gophercloud-test": "containers"}
-
-func TestListContainerInfo(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListContainerInfoSuccessfully(t)
-
-	count := 0
-	err := List(fake.ServiceClient(), &ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractInfo(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, ExpectedListInfo, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestListAllContainerInfo(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListContainerInfoSuccessfully(t)
-
-	allPages, err := List(fake.ServiceClient(), &ListOpts{Full: true}).AllPages()
-	th.AssertNoErr(t, err)
-	actual, err := ExtractInfo(allPages)
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, ExpectedListInfo, actual)
-}
-
-func TestListContainerNames(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListContainerNamesSuccessfully(t)
-
-	count := 0
-	err := List(fake.ServiceClient(), &ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNames(page)
-		if err != nil {
-			t.Errorf("Failed to extract container names: %v", err)
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, ExpectedListNames, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestListAllContainerNames(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListContainerNamesSuccessfully(t)
-
-	allPages, err := List(fake.ServiceClient(), &ListOpts{Full: false}).AllPages()
-	th.AssertNoErr(t, err)
-	actual, err := ExtractNames(allPages)
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, ExpectedListNames, actual)
-}
-
-func TestCreateContainer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreateContainerSuccessfully(t)
-
-	options := CreateOpts{ContentType: "application/json", Metadata: map[string]string{"foo": "bar"}}
-	res := Create(fake.ServiceClient(), "testContainer", options)
-	c, err := res.Extract()
-	th.CheckNoErr(t, err)
-	th.CheckEquals(t, "bar", res.Header["X-Container-Meta-Foo"][0])
-	th.CheckEquals(t, "1234567", c.TransID)
-}
-
-func TestDeleteContainer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDeleteContainerSuccessfully(t)
-
-	res := Delete(fake.ServiceClient(), "testContainer")
-	th.CheckNoErr(t, res.Err)
-}
-
-func TestUpateContainer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleUpdateContainerSuccessfully(t)
-
-	options := &UpdateOpts{Metadata: map[string]string{"foo": "bar"}}
-	res := Update(fake.ServiceClient(), "testContainer", options)
-	th.CheckNoErr(t, res.Err)
-}
-
-func TestGetContainer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetContainerSuccessfully(t)
-
-	_, err := Get(fake.ServiceClient(), "testContainer").ExtractMetadata()
-	th.CheckNoErr(t, err)
-}
diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go
index e682b8d..9eec3f4 100644
--- a/openstack/objectstorage/v1/containers/results.go
+++ b/openstack/objectstorage/v1/containers/results.go
@@ -3,24 +3,21 @@
 import (
 	"fmt"
 	"strings"
-	"time"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Container represents a container resource.
 type Container struct {
 	// The total number of bytes stored in the container.
-	Bytes int `json:"bytes" mapstructure:"bytes"`
+	Bytes int `json:"bytes"`
 
 	// The total number of objects stored in the container.
-	Count int `json:"count" mapstructure:"count"`
+	Count int `json:"count"`
 
 	// The name of the container.
-	Name string `json:"name" mapstructure:"name"`
+	Name string `json:"name"`
 }
 
 // ContainerPage is the page returned by a pager when traversing over a
@@ -29,13 +26,10 @@
 	pagination.MarkerPageBase
 }
 
-// IsEmpty returns true if a ListResult contains no container names.
+//IsEmpty returns true if a ListResult contains no container names.
 func (r ContainerPage) IsEmpty() (bool, error) {
 	names, err := ExtractNames(r)
-	if err != nil {
-		return true, err
-	}
-	return len(names) == 0, nil
+	return len(names) == 0, err
 }
 
 // LastMarker returns the last container name in a ListResult.
@@ -51,17 +45,10 @@
 }
 
 // ExtractInfo is a function that takes a ListResult and returns the containers' information.
-func ExtractInfo(page pagination.Page) ([]Container, error) {
-	untyped := page.(ContainerPage).Body.([]interface{})
-	results := make([]Container, len(untyped))
-	for index, each := range untyped {
-		container := each.(map[string]interface{})
-		err := mapstructure.Decode(container, &results[index])
-		if err != nil {
-			return results, err
-		}
-	}
-	return results, nil
+func ExtractInfo(r pagination.Page) ([]Container, error) {
+	var s []Container
+	err := (r.(ContainerPage)).ExtractInto(&s)
+	return s, err
 }
 
 // ExtractNames is a function that takes a ListResult and returns the containers' names.
@@ -99,16 +86,16 @@
 
 // GetHeader represents the headers returned in the response from a Get request.
 type GetHeader struct {
-	AcceptRanges     string    `mapstructure:"Accept-Ranges"`
-	BytesUsed        int64     `mapstructure:"X-Account-Bytes-Used"`
-	ContentLength    int64     `mapstructure:"Content-Length"`
-	ContentType      string    `mapstructure:"Content-Type"`
-	Date             time.Time `mapstructure:"-"`
-	ObjectCount      int64     `mapstructure:"X-Container-Object-Count"`
-	Read             string    `mapstructure:"X-Container-Read"`
-	TransID          string    `mapstructure:"X-Trans-Id"`
-	VersionsLocation string    `mapstructure:"X-Versions-Location"`
-	Write            string    `mapstructure:"X-Container-Write"`
+	AcceptRanges     string                  `json:"Accept-Ranges"`
+	BytesUsed        string                  `json:"X-Account-Bytes-Used"`
+	ContentLength    string                  `json:"Content-Length"`
+	ContentType      string                  `json:"Content-Type"`
+	Date             gophercloud.JSONRFC1123 `json:"Date"`
+	ObjectCount      string                  `json:"X-Container-Object-Count"`
+	Read             string                  `json:"X-Container-Read"`
+	TransID          string                  `json:"X-Trans-Id"`
+	VersionsLocation string                  `json:"X-Versions-Location"`
+	Write            string                  `json:"X-Container-Write"`
 }
 
 // GetResult represents the result of a get operation.
@@ -118,35 +105,20 @@
 
 // Extract will return a struct of headers returned from a call to Get. To obtain
 // a map of headers, call the ExtractHeader method on the GetResult.
-func (gr GetResult) Extract() (GetHeader, error) {
-	var gh GetHeader
-	if gr.Err != nil {
-		return gh, gr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(gr.Header, &gh); err != nil {
-		return gh, err
-	}
-
-	if date, ok := gr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, gr.Header["Date"][0])
-		if err != nil {
-			return gh, err
-		}
-		gh.Date = t
-	}
-
-	return gh, nil
+func (r GetResult) Extract() (*GetHeader, error) {
+	var s *GetHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // ExtractMetadata is a function that takes a GetResult (of type *http.Response)
 // and returns the custom metadata associated with the container.
-func (gr GetResult) ExtractMetadata() (map[string]string, error) {
-	if gr.Err != nil {
-		return nil, gr.Err
+func (r GetResult) ExtractMetadata() (map[string]string, error) {
+	if r.Err != nil {
+		return nil, r.Err
 	}
 	metadata := make(map[string]string)
-	for k, v := range gr.Header {
+	for k, v := range r.Header {
 		if strings.HasPrefix(k, "X-Container-Meta-") {
 			key := strings.TrimPrefix(k, "X-Container-Meta-")
 			metadata[key] = v[0]
@@ -157,10 +129,10 @@
 
 // CreateHeader represents the headers returned in the response from a Create request.
 type CreateHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // CreateResult represents the result of a create operation. To extract the
@@ -172,33 +144,18 @@
 
 // Extract will return a struct of headers returned from a call to Create. To obtain
 // a map of headers, call the ExtractHeader method on the CreateResult.
-func (cr CreateResult) Extract() (CreateHeader, error) {
-	var ch CreateHeader
-	if cr.Err != nil {
-		return ch, cr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(cr.Header, &ch); err != nil {
-		return ch, err
-	}
-
-	if date, ok := cr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["Date"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.Date = t
-	}
-
-	return ch, nil
+func (r CreateResult) Extract() (*CreateHeader, error) {
+	var s *CreateHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // UpdateHeader represents the headers returned in the response from a Update request.
 type UpdateHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // UpdateResult represents the result of an update operation. To extract the
@@ -210,33 +167,18 @@
 
 // Extract will return a struct of headers returned from a call to Update. To obtain
 // a map of headers, call the ExtractHeader method on the UpdateResult.
-func (ur UpdateResult) Extract() (UpdateHeader, error) {
-	var uh UpdateHeader
-	if ur.Err != nil {
-		return uh, ur.Err
-	}
-
-	if err := gophercloud.DecodeHeader(ur.Header, &uh); err != nil {
-		return uh, err
-	}
-
-	if date, ok := ur.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, ur.Header["Date"][0])
-		if err != nil {
-			return uh, err
-		}
-		uh.Date = t
-	}
-
-	return uh, nil
+func (r UpdateResult) Extract() (*UpdateHeader, error) {
+	var s *UpdateHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // DeleteHeader represents the headers returned in the response from a Delete request.
 type DeleteHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // DeleteResult represents the result of a delete operation. To extract the
@@ -248,23 +190,8 @@
 
 // Extract will return a struct of headers returned from a call to Delete. To obtain
 // a map of headers, call the ExtractHeader method on the DeleteResult.
-func (dr DeleteResult) Extract() (DeleteHeader, error) {
-	var dh DeleteHeader
-	if dr.Err != nil {
-		return dh, dr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(dr.Header, &dh); err != nil {
-		return dh, err
-	}
-
-	if date, ok := dr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, dr.Header["Date"][0])
-		if err != nil {
-			return dh, err
-		}
-		dh.Date = t
-	}
-
-	return dh, nil
+func (r DeleteResult) Extract() (*DeleteHeader, error) {
+	var s *DeleteHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
diff --git a/openstack/objectstorage/v1/containers/testing/doc.go b/openstack/objectstorage/v1/containers/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/objectstorage/v1/containers/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go
new file mode 100644
index 0000000..fe579d8
--- /dev/null
+++ b/openstack/objectstorage/v1/containers/testing/fixtures.go
@@ -0,0 +1,142 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// ExpectedListInfo is the result expected from a call to `List` when full
+// info is requested.
+var ExpectedListInfo = []containers.Container{
+	{
+		Count: 0,
+		Bytes: 0,
+		Name:  "janeausten",
+	},
+	{
+		Count: 1,
+		Bytes: 14,
+		Name:  "marktwain",
+	},
+}
+
+// ExpectedListNames is the result expected from a call to `List` when just
+// container names are requested.
+var ExpectedListNames = []string{"janeausten", "marktwain"}
+
+// HandleListContainerInfoSuccessfully creates an HTTP handler at `/` on the test handler mux that
+// responds with a `List` response when full info is requested.
+func HandleListContainerInfoSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, `[
+        {
+          "count": 0,
+          "bytes": 0,
+          "name": "janeausten"
+        },
+        {
+          "count": 1,
+          "bytes": 14,
+          "name": "marktwain"
+        }
+      ]`)
+		case "janeausten":
+			fmt.Fprintf(w, `[
+				{
+					"count": 1,
+					"bytes": 14,
+					"name": "marktwain"
+				}
+			]`)
+		case "marktwain":
+			fmt.Fprintf(w, `[]`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+}
+
+// HandleListContainerNamesSuccessfully creates an HTTP handler at `/` on the test handler mux that
+// responds with a `ListNames` response when only container names are requested.
+func HandleListContainerNamesSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "text/plain")
+
+		w.Header().Set("Content-Type", "text/plain")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, "janeausten\nmarktwain\n")
+		case "janeausten":
+			fmt.Fprintf(w, "marktwain\n")
+		case "marktwain":
+			fmt.Fprintf(w, ``)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+}
+
+// HandleCreateContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
+// responds with a `Create` response.
+func HandleCreateContainerSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Add("X-Container-Meta-Foo", "bar")
+		w.Header().Add("X-Trans-Id", "1234567")
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleDeleteContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
+// responds with a `Delete` response.
+func HandleDeleteContainerSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleUpdateContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
+// responds with a `Update` response.
+func HandleUpdateContainerSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleGetContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
+// responds with a `Get` response.
+func HandleGetContainerSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "HEAD")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go
new file mode 100644
index 0000000..0d32882
--- /dev/null
+++ b/openstack/objectstorage/v1/containers/testing/requests_test.go
@@ -0,0 +1,118 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+var metadata = map[string]string{"gophercloud-test": "containers"}
+
+func TestListContainerInfo(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListContainerInfoSuccessfully(t)
+
+	count := 0
+	err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := containers.ExtractInfo(page)
+		th.AssertNoErr(t, err)
+
+		th.CheckDeepEquals(t, ExpectedListInfo, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, count, 1)
+}
+
+func TestListAllContainerInfo(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListContainerInfoSuccessfully(t)
+
+	allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: true}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := containers.ExtractInfo(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, ExpectedListInfo, actual)
+}
+
+func TestListContainerNames(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListContainerNamesSuccessfully(t)
+
+	count := 0
+	err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := containers.ExtractNames(page)
+		if err != nil {
+			t.Errorf("Failed to extract container names: %v", err)
+			return false, err
+		}
+
+		th.CheckDeepEquals(t, ExpectedListNames, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, count, 1)
+}
+
+func TestListAllContainerNames(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListContainerNamesSuccessfully(t)
+
+	allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: false}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := containers.ExtractNames(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, ExpectedListNames, actual)
+}
+
+func TestCreateContainer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleCreateContainerSuccessfully(t)
+
+	options := containers.CreateOpts{ContentType: "application/json", Metadata: map[string]string{"foo": "bar"}}
+	res := containers.Create(fake.ServiceClient(), "testContainer", options)
+	c, err := res.Extract()
+	th.CheckNoErr(t, err)
+	th.CheckEquals(t, "bar", res.Header["X-Container-Meta-Foo"][0])
+	th.CheckEquals(t, "1234567", c.TransID)
+}
+
+func TestDeleteContainer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDeleteContainerSuccessfully(t)
+
+	res := containers.Delete(fake.ServiceClient(), "testContainer")
+	th.CheckNoErr(t, res.Err)
+}
+
+func TestUpateContainer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleUpdateContainerSuccessfully(t)
+
+	options := &containers.UpdateOpts{Metadata: map[string]string{"foo": "bar"}}
+	res := containers.Update(fake.ServiceClient(), "testContainer", options)
+	th.CheckNoErr(t, res.Err)
+}
+
+func TestGetContainer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetContainerSuccessfully(t)
+
+	_, err := containers.Get(fake.ServiceClient(), "testContainer").ExtractMetadata()
+	th.CheckNoErr(t, err)
+}
diff --git a/openstack/objectstorage/v1/containers/urls.go b/openstack/objectstorage/v1/containers/urls.go
index f864f84..9b38047 100644
--- a/openstack/objectstorage/v1/containers/urls.go
+++ b/openstack/objectstorage/v1/containers/urls.go
@@ -1,6 +1,6 @@
 package containers
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func listURL(c *gophercloud.ServiceClient) string {
 	return c.Endpoint
diff --git a/openstack/objectstorage/v1/containers/urls_test.go b/openstack/objectstorage/v1/containers/urls_test.go
deleted file mode 100644
index d043a2a..0000000
--- a/openstack/objectstorage/v1/containers/urls_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package containers
-
-import (
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"testing"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient())
-	expected := endpoint
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient(), "foo")
-	expected := endpoint + "foo"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "foo"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "foo")
-	expected := endpoint + "foo"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient(), "foo")
-	expected := endpoint + "foo"
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/openstack/objectstorage/v1/objects/errors.go b/openstack/objectstorage/v1/objects/errors.go
new file mode 100644
index 0000000..5c4ae44
--- /dev/null
+++ b/openstack/objectstorage/v1/objects/errors.go
@@ -0,0 +1,13 @@
+package objects
+
+import "github.com/gophercloud/gophercloud"
+
+// ErrWrongChecksum is the error when the checksum generated for an object
+// doesn't match the ETAG header.
+type ErrWrongChecksum struct {
+	gophercloud.BaseError
+}
+
+func (e ErrWrongChecksum) Error() string {
+	return "Local checksum does not match API ETag header"
+}
diff --git a/openstack/objectstorage/v1/objects/fixtures.go b/openstack/objectstorage/v1/objects/fixtures.go
deleted file mode 100644
index d2ea18c..0000000
--- a/openstack/objectstorage/v1/objects/fixtures.go
+++ /dev/null
@@ -1,213 +0,0 @@
-// +build fixtures
-
-package objects
-
-import (
-	"crypto/md5"
-	"fmt"
-	"io"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// HandleDownloadObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
-// responds with a `Download` response.
-func HandleDownloadObjectSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, "Successful download with Gophercloud")
-	})
-}
-
-// ExpectedListInfo is the result expected from a call to `List` when full
-// info is requested.
-var ExpectedListInfo = []Object{
-	Object{
-		Hash:         "451e372e48e0f6b1114fa0724aa79fa1",
-		LastModified: "2009-11-10 23:00:00 +0000 UTC",
-		Bytes:        14,
-		Name:         "goodbye",
-		ContentType:  "application/octet-stream",
-	},
-	Object{
-		Hash:         "451e372e48e0f6b1114fa0724aa79fa1",
-		LastModified: "2009-11-10 23:00:00 +0000 UTC",
-		Bytes:        14,
-		Name:         "hello",
-		ContentType:  "application/octet-stream",
-	},
-}
-
-// ExpectedListNames is the result expected from a call to `List` when just
-// object names are requested.
-var ExpectedListNames = []string{"hello", "goodbye"}
-
-// HandleListObjectsInfoSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
-// responds with a `List` response when full info is requested.
-func HandleListObjectsInfoSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, `[
-      {
-        "hash": "451e372e48e0f6b1114fa0724aa79fa1",
-        "last_modified": "2009-11-10 23:00:00 +0000 UTC",
-        "bytes": 14,
-        "name": "goodbye",
-        "content_type": "application/octet-stream"
-      },
-      {
-        "hash": "451e372e48e0f6b1114fa0724aa79fa1",
-        "last_modified": "2009-11-10 23:00:00 +0000 UTC",
-        "bytes": 14,
-        "name": "hello",
-        "content_type": "application/octet-stream"
-      }
-    ]`)
-		case "hello":
-			fmt.Fprintf(w, `[]`)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-}
-
-// HandleListObjectNamesSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
-// responds with a `List` response when only object names are requested.
-func HandleListObjectNamesSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "text/plain")
-
-		w.Header().Set("Content-Type", "text/plain")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, "hello\ngoodbye\n")
-		case "goodbye":
-			fmt.Fprintf(w, "")
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-}
-
-// HandleCreateTextObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux
-// that responds with a `Create` response. A Content-Type of "text/plain" is expected.
-func HandleCreateTextObjectSuccessfully(t *testing.T, content string) {
-	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "text/plain")
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		hash := md5.New()
-		io.WriteString(hash, content)
-		localChecksum := hash.Sum(nil)
-
-		w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum))
-		w.WriteHeader(http.StatusCreated)
-	})
-}
-
-// HandleCreateTextWithCacheControlSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler
-// mux that responds with a `Create` response. A Cache-Control of `max-age="3600", public` is expected.
-func HandleCreateTextWithCacheControlSuccessfully(t *testing.T, content string) {
-	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Cache-Control", `max-age="3600", public`)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		hash := md5.New()
-		io.WriteString(hash, content)
-		localChecksum := hash.Sum(nil)
-
-		w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum))
-		w.WriteHeader(http.StatusCreated)
-	})
-}
-
-// HandleCreateTypelessObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler
-// mux that responds with a `Create` response. No Content-Type header may be present in the request, so that server-
-// side content-type detection will be triggered properly.
-func HandleCreateTypelessObjectSuccessfully(t *testing.T, content string) {
-	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		if contentType, present := r.Header["Content-Type"]; present {
-			t.Errorf("Expected Content-Type header to be omitted, but was %#v", contentType)
-		}
-
-		hash := md5.New()
-		io.WriteString(hash, content)
-		localChecksum := hash.Sum(nil)
-
-		w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum))
-		w.WriteHeader(http.StatusCreated)
-	})
-}
-
-// HandleCopyObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
-// responds with a `Copy` response.
-func HandleCopyObjectSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "COPY")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestHeader(t, r, "Destination", "/newTestContainer/newTestObject")
-		w.WriteHeader(http.StatusCreated)
-	})
-}
-
-// HandleDeleteObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
-// responds with a `Delete` response.
-func HandleDeleteObjectSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleUpdateObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
-// responds with a `Update` response.
-func HandleUpdateObjectSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestHeader(t, r, "X-Object-Meta-Gophercloud-Test", "objects")
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-// HandleGetObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
-// responds with a `Get` response.
-func HandleGetObjectSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "HEAD")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		w.Header().Add("X-Object-Meta-Gophercloud-Test", "objects")
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go
index a2b96ed..0ab5e17 100644
--- a/openstack/objectstorage/v1/objects/requests.go
+++ b/openstack/objectstorage/v1/objects/requests.go
@@ -1,19 +1,18 @@
 package objects
 
 import (
-	"bufio"
+	"bytes"
 	"crypto/hmac"
 	"crypto/md5"
 	"crypto/sha1"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"strings"
 	"time"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListOptsBuilder allows extensions to add additional parameters to the List
@@ -42,10 +41,7 @@
 // representing whether to list complete information for each object.
 func (opts ListOpts) ToObjectListParams() (bool, string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return false, "", err
-	}
-	return opts.Full, q.String(), nil
+	return opts.Full, q.String(), err
 }
 
 // List is a function that retrieves all objects in a container. It also returns the details
@@ -67,13 +63,11 @@
 		}
 	}
 
-	createPage := func(r pagination.PageResult) pagination.Page {
+	pager := pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		p := ObjectPage{pagination.MarkerPageBase{PageResult: r}}
 		p.MarkerPageBase.Owner = p
 		return p
-	}
-
-	pager := pagination.NewPager(c, url, createPage)
+	})
 	pager.Headers = headers
 	return pager
 }
@@ -113,47 +107,42 @@
 // Download is a function that retrieves the content and metadata for an object.
 // To extract just the content, pass the DownloadResult response to the
 // ExtractContent function.
-func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) DownloadResult {
-	var res DownloadResult
-
+func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) (r DownloadResult) {
 	url := downloadURL(c, containerName, objectName)
 	h := make(map[string]string)
-
 	if opts != nil {
 		headers, query, err := opts.ToObjectDownloadParams()
 		if err != nil {
-			res.Err = err
-			return res
+			r.Err = err
+			return
 		}
-
 		for k, v := range headers {
 			h[k] = v
 		}
-
 		url += query
 	}
 
-	resp, err := c.Request("GET", url, gophercloud.RequestOpts{
+	resp, err := c.Get(url, nil, &gophercloud.RequestOpts{
 		MoreHeaders: h,
 		OkCodes:     []int{200, 304},
 	})
 	if resp != nil {
-		res.Header = resp.Header
-		res.Body = resp.Body
+		r.Header = resp.Header
+		r.Body = resp.Body
 	}
-	res.Err = err
-
-	return res
+	r.Err = err
+	return
 }
 
 // CreateOptsBuilder allows extensions to add additional parameters to the
 // Create request.
 type CreateOptsBuilder interface {
-	ToObjectCreateParams() (map[string]string, string, error)
+	ToObjectCreateParams() (io.Reader, map[string]string, string, error)
 }
 
 // CreateOpts is a structure that holds parameters for creating an object.
 type CreateOpts struct {
+	Content            io.Reader
 	Metadata           map[string]string
 	CacheControl       string `h:"Cache-Control"`
 	ContentDisposition string `h:"Content-Disposition"`
@@ -175,78 +164,60 @@
 
 // ToObjectCreateParams formats a CreateOpts into a query string and map of
 // headers.
-func (opts CreateOpts) ToObjectCreateParams() (map[string]string, string, error) {
+func (opts CreateOpts) ToObjectCreateParams() (io.Reader, map[string]string, string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
 	if err != nil {
-		return nil, "", err
+		return nil, nil, "", err
 	}
 	h, err := gophercloud.BuildHeaders(opts)
 	if err != nil {
-		return nil, q.String(), err
+		return nil, nil, "", err
 	}
 
 	for k, v := range opts.Metadata {
 		h["X-Object-Meta-"+k] = v
 	}
 
-	return h, q.String(), nil
+	hash := md5.New()
+	buf := bytes.NewBuffer([]byte{})
+	_, err = io.Copy(io.MultiWriter(hash, buf), opts.Content)
+	if err != nil {
+		return nil, nil, "", err
+	}
+	localChecksum := fmt.Sprintf("%x", hash.Sum(nil))
+	h["ETag"] = localChecksum
+
+	return buf, h, q.String(), nil
 }
 
 // Create is a function that creates a new object or replaces an existing object. If the returned response's ETag
 // header fails to match the local checksum, the failed request will automatically be retried up to a maximum of 3 times.
-func Create(c *gophercloud.ServiceClient, containerName, objectName string, content io.ReadSeeker, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
+func Create(c *gophercloud.ServiceClient, containerName, objectName string, opts CreateOptsBuilder) (r CreateResult) {
 	url := createURL(c, containerName, objectName)
 	h := make(map[string]string)
-
+	var b io.Reader
 	if opts != nil {
-		headers, query, err := opts.ToObjectCreateParams()
+		tmpB, headers, query, err := opts.ToObjectCreateParams()
 		if err != nil {
-			res.Err = err
-			return res
+			r.Err = err
+			return
 		}
-
 		for k, v := range headers {
 			h[k] = v
 		}
-
 		url += query
+		b = tmpB
 	}
 
-	hash := md5.New()
-	bufioReader := bufio.NewReader(io.TeeReader(content, hash))
-	io.Copy(ioutil.Discard, bufioReader)
-	localChecksum := hash.Sum(nil)
-
-	h["ETag"] = fmt.Sprintf("%x", localChecksum)
-
-	_, err := content.Seek(0, 0)
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	ropts := gophercloud.RequestOpts{
-		RawBody:     content,
+	resp, err := c.Put(url, nil, nil, &gophercloud.RequestOpts{
+		RawBody:     b,
 		MoreHeaders: h,
-	}
-
-	resp, err := c.Request("PUT", url, ropts)
-	if err != nil {
-		res.Err = err
-		return res
-	}
+	})
+	r.Err = err
 	if resp != nil {
-		res.Header = resp.Header
-		if resp.Header.Get("ETag") == fmt.Sprintf("%x", localChecksum) {
-			res.Err = err
-			return res
-		}
-		res.Err = fmt.Errorf("Local checksum does not match API ETag header")
+		r.Header = resp.Header
 	}
-
-	return res
+	return
 }
 
 // CopyOptsBuilder allows extensions to add additional parameters to the
@@ -262,14 +233,11 @@
 	ContentDisposition string `h:"Content-Disposition"`
 	ContentEncoding    string `h:"Content-Encoding"`
 	ContentType        string `h:"Content-Type"`
-	Destination        string `h:"Destination,required"`
+	Destination        string `h:"Destination" required:"true"`
 }
 
 // ToObjectCopyMap formats a CopyOpts into a map of headers.
 func (opts CopyOpts) ToObjectCopyMap() (map[string]string, error) {
-	if opts.Destination == "" {
-		return nil, fmt.Errorf("Required CopyOpts field 'Destination' not set.")
-	}
 	h, err := gophercloud.BuildHeaders(opts)
 	if err != nil {
 		return nil, err
@@ -281,14 +249,12 @@
 }
 
 // Copy is a function that copies one object to another.
-func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) CopyResult {
-	var res CopyResult
+func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) (r CopyResult) {
 	h := make(map[string]string)
-
 	headers, err := opts.ToObjectCopyMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
 
 	for k, v := range headers {
@@ -296,15 +262,15 @@
 	}
 
 	url := copyURL(c, containerName, objectName)
-	resp, err := c.Request("COPY", url, gophercloud.RequestOpts{
+	resp, err := c.Request("COPY", url, &gophercloud.RequestOpts{
 		MoreHeaders: h,
 		OkCodes:     []int{201},
 	})
 	if resp != nil {
-		res.Header = resp.Header
+		r.Header = resp.Header
 	}
-	res.Err = err
-	return res
+	r.Err = err
+	return
 }
 
 // DeleteOptsBuilder allows extensions to add additional parameters to the
@@ -321,32 +287,26 @@
 // ToObjectDeleteQuery formats a DeleteOpts into a query string.
 func (opts DeleteOpts) ToObjectDeleteQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // Delete is a function that deletes an object.
-func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts DeleteOptsBuilder) DeleteResult {
-	var res DeleteResult
+func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts DeleteOptsBuilder) (r DeleteResult) {
 	url := deleteURL(c, containerName, objectName)
-
 	if opts != nil {
 		query, err := opts.ToObjectDeleteQuery()
 		if err != nil {
-			res.Err = err
-			return res
+			r.Err = err
+			return
 		}
 		url += query
 	}
-
 	resp, err := c.Delete(url, nil)
 	if resp != nil {
-		res.Header = resp.Header
+		r.Header = resp.Header
 	}
-	res.Err = err
-	return res
+	r.Err = err
+	return
 }
 
 // GetOptsBuilder allows extensions to add additional parameters to the
@@ -364,35 +324,29 @@
 // ToObjectGetQuery formats a GetOpts into a query string.
 func (opts GetOpts) ToObjectGetQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // Get is a function that retrieves the metadata of an object. To extract just the custom
 // metadata, pass the GetResult response to the ExtractMetadata function.
-func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) GetResult {
-	var res GetResult
+func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) (r GetResult) {
 	url := getURL(c, containerName, objectName)
-
 	if opts != nil {
 		query, err := opts.ToObjectGetQuery()
 		if err != nil {
-			res.Err = err
-			return res
+			r.Err = err
+			return
 		}
 		url += query
 	}
-
-	resp, err := c.Request("HEAD", url, gophercloud.RequestOpts{
+	resp, err := c.Request("HEAD", url, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 204},
 	})
 	if resp != nil {
-		res.Header = resp.Header
+		r.Header = resp.Header
 	}
-	res.Err = err
-	return res
+	r.Err = err
+	return
 }
 
 // UpdateOptsBuilder allows extensions to add additional parameters to the
@@ -426,31 +380,28 @@
 }
 
 // Update is a function that creates, updates, or deletes an object's metadata.
-func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
+func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts UpdateOptsBuilder) (r UpdateResult) {
 	h := make(map[string]string)
-
 	if opts != nil {
 		headers, err := opts.ToObjectUpdateMap()
 		if err != nil {
-			res.Err = err
-			return res
+			r.Err = err
+			return
 		}
 
 		for k, v := range headers {
 			h[k] = v
 		}
 	}
-
 	url := updateURL(c, containerName, objectName)
-	resp, err := c.Request("POST", url, gophercloud.RequestOpts{
+	resp, err := c.Post(url, nil, nil, &gophercloud.RequestOpts{
 		MoreHeaders: h,
 	})
 	if resp != nil {
-		res.Header = resp.Header
+		r.Header = resp.Header
 	}
-	res.Err = err
-	return res
+	r.Err = err
+	return
 }
 
 // HTTPMethod represents an HTTP method string (e.g. "GET").
diff --git a/openstack/objectstorage/v1/objects/requests_test.go b/openstack/objectstorage/v1/objects/requests_test.go
deleted file mode 100644
index 3a9d098..0000000
--- a/openstack/objectstorage/v1/objects/requests_test.go
+++ /dev/null
@@ -1,178 +0,0 @@
-package objects
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-	"net/http"
-	"strings"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestDownloadReader(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDownloadObjectSuccessfully(t)
-
-	response := Download(fake.ServiceClient(), "testContainer", "testObject", nil)
-	defer response.Body.Close()
-
-	// Check reader
-	buf := bytes.NewBuffer(make([]byte, 0))
-	io.CopyN(buf, response.Body, 10)
-	th.CheckEquals(t, "Successful", string(buf.Bytes()))
-}
-
-func TestDownloadExtraction(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDownloadObjectSuccessfully(t)
-
-	response := Download(fake.ServiceClient(), "testContainer", "testObject", nil)
-
-	// Check []byte extraction
-	bytes, err := response.ExtractContent()
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, "Successful download with Gophercloud", string(bytes))
-}
-
-func TestListObjectInfo(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListObjectsInfoSuccessfully(t)
-
-	count := 0
-	options := &ListOpts{Full: true}
-	err := List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractInfo(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, ExpectedListInfo, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestListObjectNames(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListObjectNamesSuccessfully(t)
-
-	count := 0
-	options := &ListOpts{Full: false}
-	err := List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNames(page)
-		if err != nil {
-			t.Errorf("Failed to extract container names: %v", err)
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, ExpectedListNames, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestCreateObject(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	content := "Did gyre and gimble in the wabe"
-
-	HandleCreateTextObjectSuccessfully(t, content)
-
-	options := &CreateOpts{ContentType: "text/plain"}
-	res := Create(fake.ServiceClient(), "testContainer", "testObject", strings.NewReader(content), options)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestCreateObjectWithCacheControl(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	content := "All mimsy were the borogoves"
-
-	HandleCreateTextWithCacheControlSuccessfully(t, content)
-
-	options := &CreateOpts{CacheControl: `max-age="3600", public`}
-	res := Create(fake.ServiceClient(), "testContainer", "testObject", strings.NewReader(content), options)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestCreateObjectWithoutContentType(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	content := "The sky was the color of television, tuned to a dead channel."
-
-	HandleCreateTypelessObjectSuccessfully(t, content)
-
-	res := Create(fake.ServiceClient(), "testContainer", "testObject", strings.NewReader(content), &CreateOpts{})
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestErrorIsRaisedForChecksumMismatch(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Set("ETag", "acbd18db4cc2f85cedef654fccc4a4d8")
-		w.WriteHeader(http.StatusCreated)
-	})
-
-	content := strings.NewReader("The sky was the color of television, tuned to a dead channel.")
-	res := Create(fake.ServiceClient(), "testContainer", "testObject", content, &CreateOpts{})
-
-	err := fmt.Errorf("Local checksum does not match API ETag header")
-	th.AssertDeepEquals(t, err, res.Err)
-}
-
-func TestCopyObject(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCopyObjectSuccessfully(t)
-
-	options := &CopyOpts{Destination: "/newTestContainer/newTestObject"}
-	res := Copy(fake.ServiceClient(), "testContainer", "testObject", options)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestDeleteObject(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDeleteObjectSuccessfully(t)
-
-	res := Delete(fake.ServiceClient(), "testContainer", "testObject", nil)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestUpateObjectMetadata(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleUpdateObjectSuccessfully(t)
-
-	options := &UpdateOpts{Metadata: map[string]string{"Gophercloud-Test": "objects"}}
-	res := Update(fake.ServiceClient(), "testContainer", "testObject", options)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestGetObject(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetObjectSuccessfully(t)
-
-	expected := map[string]string{"Gophercloud-Test": "objects"}
-	actual, err := Get(fake.ServiceClient(), "testContainer", "testObject", nil).ExtractMetadata()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, expected, actual)
-}
diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go
index ecb2c54..3cfe1f4 100644
--- a/openstack/objectstorage/v1/objects/results.go
+++ b/openstack/objectstorage/v1/objects/results.go
@@ -4,34 +4,30 @@
 	"fmt"
 	"io"
 	"io/ioutil"
-	"strconv"
 	"strings"
-	"time"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Object is a structure that holds information related to a storage object.
 type Object struct {
 	// Bytes is the total number of bytes that comprise the object.
-	Bytes int64 `json:"bytes" mapstructure:"bytes"`
+	Bytes int64 `json:"bytes"`
 
 	// ContentType is the content type of the object.
-	ContentType string `json:"content_type" mapstructure:"content_type"`
+	ContentType string `json:"content_type"`
 
 	// Hash represents the MD5 checksum value of the object's content.
-	Hash string `json:"hash" mapstructure:"hash"`
+	Hash string `json:"hash"`
 
 	// LastModified is the RFC3339Milli time the object was last modified, represented
 	// as a string. For any given object (obj), this value may be parsed to a time.Time:
 	// lastModified, err := time.Parse(gophercloud.RFC3339Milli, obj.LastModified)
-	LastModified string `json:"last_modified" mapstructure:"last_modified"`
+	LastModified string `json:"last_modified"`
 
 	// Name is the unique name for the object.
-	Name string `json:"name" mapstructure:"name"`
+	Name string `json:"name"`
 }
 
 // ObjectPage is a single page of objects that is returned from a call to the
@@ -43,10 +39,7 @@
 // IsEmpty returns true if a ListResult contains no object names.
 func (r ObjectPage) IsEmpty() (bool, error) {
 	names, err := ExtractNames(r)
-	if err != nil {
-		return true, err
-	}
-	return len(names) == 0, nil
+	return len(names) == 0, err
 }
 
 // LastMarker returns the last object name in a ListResult.
@@ -62,26 +55,19 @@
 }
 
 // ExtractInfo is a function that takes a page of objects and returns their full information.
-func ExtractInfo(page pagination.Page) ([]Object, error) {
-	untyped := page.(ObjectPage).Body.([]interface{})
-	results := make([]Object, len(untyped))
-	for index, each := range untyped {
-		object := each.(map[string]interface{})
-		err := mapstructure.Decode(object, &results[index])
-		if err != nil {
-			return results, err
-		}
-	}
-	return results, nil
+func ExtractInfo(r pagination.Page) ([]Object, error) {
+	var s []Object
+	err := (r.(ObjectPage)).ExtractInto(&s)
+	return s, err
 }
 
 // ExtractNames is a function that takes a page of objects and returns only their names.
-func ExtractNames(page pagination.Page) ([]string, error) {
-	casted := page.(ObjectPage)
+func ExtractNames(r pagination.Page) ([]string, error) {
+	casted := r.(ObjectPage)
 	ct := casted.Header.Get("Content-Type")
 	switch {
 	case strings.HasPrefix(ct, "application/json"):
-		parsed, err := ExtractInfo(page)
+		parsed, err := ExtractInfo(r)
 		if err != nil {
 			return nil, err
 		}
@@ -95,7 +81,7 @@
 	case strings.HasPrefix(ct, "text/plain"):
 		names := make([]string, 0, 50)
 
-		body := string(page.(ObjectPage).Body.([]uint8))
+		body := string(r.(ObjectPage).Body.([]uint8))
 		for _, name := range strings.Split(body, "\n") {
 			if len(name) > 0 {
 				names = append(names, name)
@@ -112,18 +98,18 @@
 
 // DownloadHeader represents the headers returned in the response from a Download request.
 type DownloadHeader struct {
-	AcceptRanges       string    `mapstructure:"Accept-Ranges"`
-	ContentDisposition string    `mapstructure:"Content-Disposition"`
-	ContentEncoding    string    `mapstructure:"Content-Encoding"`
-	ContentLength      int64     `mapstructure:"Content-Length"`
-	ContentType        string    `mapstructure:"Content-Type"`
-	Date               time.Time `mapstructure:"-"`
-	DeleteAt           time.Time `mapstructure:"-"`
-	ETag               string    `mapstructure:"Etag"`
-	LastModified       time.Time `mapstructure:"-"`
-	ObjectManifest     string    `mapstructure:"X-Object-Manifest"`
-	StaticLargeObject  bool      `mapstructure:"X-Static-Large-Object"`
-	TransID            string    `mapstructure:"X-Trans-Id"`
+	AcceptRanges       string                  `json:"Accept-Ranges"`
+	ContentDisposition string                  `json:"Content-Disposition"`
+	ContentEncoding    string                  `json:"Content-Encoding"`
+	ContentLength      string                  `json:"Content-Length"`
+	ContentType        string                  `json:"Content-Type"`
+	Date               gophercloud.JSONRFC1123 `json:"Date"`
+	DeleteAt           gophercloud.JSONUnix    `json:"X-Delete-At"`
+	ETag               string                  `json:"Etag"`
+	LastModified       gophercloud.JSONRFC1123 `json:"Last-Modified"`
+	ObjectManifest     string                  `json:"X-Object-Manifest"`
+	StaticLargeObject  bool                    `json:"X-Static-Large-Object"`
+	TransID            string                  `json:"X-Trans-Id"`
 }
 
 // DownloadResult is a *http.Response that is returned from a call to the Download function.
@@ -134,41 +120,10 @@
 
 // Extract will return a struct of headers returned from a call to Download. To obtain
 // a map of headers, call the ExtractHeader method on the DownloadResult.
-func (dr DownloadResult) Extract() (DownloadHeader, error) {
-	var dh DownloadHeader
-	if dr.Err != nil {
-		return dh, dr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(dr.Header, &dh); err != nil {
-		return dh, err
-	}
-
-	if date, ok := dr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, date[0])
-		if err != nil {
-			return dh, err
-		}
-		dh.Date = t
-	}
-
-	if date, ok := dr.Header["Last-Modified"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, date[0])
-		if err != nil {
-			return dh, err
-		}
-		dh.LastModified = t
-	}
-
-	if date, ok := dr.Header["X-Delete-At"]; ok && len(date) > 0 {
-		unix, err := strconv.ParseInt(date[0], 10, 64)
-		if err != nil {
-			return dh, err
-		}
-		dh.DeleteAt = time.Unix(unix, 0)
-	}
-
-	return dh, nil
+func (r DownloadResult) Extract() (*DownloadHeader, error) {
+	var s *DownloadHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // ExtractContent is a function that takes a DownloadResult's io.Reader body
@@ -176,31 +131,32 @@
 // the nature of io.Reader is forward-only - meaning that it can only be read
 // once and not rewound. You can recreate a reader from the output of this
 // function by using bytes.NewReader(downloadBytes)
-func (dr DownloadResult) ExtractContent() ([]byte, error) {
-	if dr.Err != nil {
-		return nil, dr.Err
+func (r *DownloadResult) ExtractContent() ([]byte, error) {
+	if r.Err != nil {
+		return nil, r.Err
 	}
-	body, err := ioutil.ReadAll(dr.Body)
+	defer r.Body.Close()
+	body, err := ioutil.ReadAll(r.Body)
 	if err != nil {
 		return nil, err
 	}
-	dr.Body.Close()
+	r.Body.Close()
 	return body, nil
 }
 
 // GetHeader represents the headers returned in the response from a Get request.
 type GetHeader struct {
-	ContentDisposition string    `mapstructure:"Content-Disposition"`
-	ContentEncoding    string    `mapstructure:"Content-Encoding"`
-	ContentLength      int64     `mapstructure:"Content-Length"`
-	ContentType        string    `mapstructure:"Content-Type"`
-	Date               time.Time `mapstructure:"-"`
-	DeleteAt           time.Time `mapstructure:"-"`
-	ETag               string    `mapstructure:"Etag"`
-	LastModified       time.Time `mapstructure:"-"`
-	ObjectManifest     string    `mapstructure:"X-Object-Manifest"`
-	StaticLargeObject  bool      `mapstructure:"X-Static-Large-Object"`
-	TransID            string    `mapstructure:"X-Trans-Id"`
+	ContentDisposition string                  `json:"Content-Disposition"`
+	ContentEncoding    string                  `json:"Content-Encoding"`
+	ContentLength      string                  `json:"Content-Length"`
+	ContentType        string                  `json:"Content-Type"`
+	Date               gophercloud.JSONRFC1123 `json:"Date"`
+	DeleteAt           gophercloud.JSONUnix    `json:"X-Delete-At"`
+	ETag               string                  `json:"Etag"`
+	LastModified       gophercloud.JSONRFC1123 `json:"Last-Modified"`
+	ObjectManifest     string                  `json:"X-Object-Manifest"`
+	StaticLargeObject  bool                    `json:"X-Static-Large-Object"`
+	TransID            string                  `json:"X-Trans-Id"`
 }
 
 // GetResult is a *http.Response that is returned from a call to the Get function.
@@ -210,51 +166,20 @@
 
 // Extract will return a struct of headers returned from a call to Get. To obtain
 // a map of headers, call the ExtractHeader method on the GetResult.
-func (gr GetResult) Extract() (GetHeader, error) {
-	var gh GetHeader
-	if gr.Err != nil {
-		return gh, gr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(gr.Header, &gh); err != nil {
-		return gh, err
-	}
-
-	if date, ok := gr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, gr.Header["Date"][0])
-		if err != nil {
-			return gh, err
-		}
-		gh.Date = t
-	}
-
-	if date, ok := gr.Header["Last-Modified"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, gr.Header["Last-Modified"][0])
-		if err != nil {
-			return gh, err
-		}
-		gh.LastModified = t
-	}
-
-	if date, ok := gr.Header["X-Delete-At"]; ok && len(date) > 0 {
-		unix, err := strconv.ParseInt(date[0], 10, 64)
-		if err != nil {
-			return gh, err
-		}
-		gh.DeleteAt = time.Unix(unix, 0)
-	}
-
-	return gh, nil
+func (r GetResult) Extract() (*GetHeader, error) {
+	var s *GetHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // ExtractMetadata is a function that takes a GetResult (of type *http.Response)
 // and returns the custom metadata associated with the object.
-func (gr GetResult) ExtractMetadata() (map[string]string, error) {
-	if gr.Err != nil {
-		return nil, gr.Err
+func (r GetResult) ExtractMetadata() (map[string]string, error) {
+	if r.Err != nil {
+		return nil, r.Err
 	}
 	metadata := make(map[string]string)
-	for k, v := range gr.Header {
+	for k, v := range r.Header {
 		if strings.HasPrefix(k, "X-Object-Meta-") {
 			key := strings.TrimPrefix(k, "X-Object-Meta-")
 			metadata[key] = v[0]
@@ -265,56 +190,37 @@
 
 // CreateHeader represents the headers returned in the response from a Create request.
 type CreateHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	ETag          string    `mapstructure:"Etag"`
-	LastModified  time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	ETag          string                  `json:"Etag"`
+	LastModified  gophercloud.JSONRFC1123 `json:"Last-Modified"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // CreateResult represents the result of a create operation.
 type CreateResult struct {
+	checksum string
 	gophercloud.HeaderResult
 }
 
 // Extract will return a struct of headers returned from a call to Create. To obtain
 // a map of headers, call the ExtractHeader method on the CreateResult.
-func (cr CreateResult) Extract() (CreateHeader, error) {
-	var ch CreateHeader
-	if cr.Err != nil {
-		return ch, cr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(cr.Header, &ch); err != nil {
-		return ch, err
-	}
-
-	if date, ok := cr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["Date"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.Date = t
-	}
-
-	if date, ok := cr.Header["Last-Modified"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["Last-Modified"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.LastModified = t
-	}
-
-	return ch, nil
+func (r CreateResult) Extract() (*CreateHeader, error) {
+	//if r.Header.Get("ETag") != fmt.Sprintf("%x", localChecksum) {
+	//	return nil, ErrWrongChecksum{}
+	//}
+	var s *CreateHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // UpdateHeader represents the headers returned in the response from a Update request.
 type UpdateHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // UpdateResult represents the result of an update operation.
@@ -324,33 +230,18 @@
 
 // Extract will return a struct of headers returned from a call to Update. To obtain
 // a map of headers, call the ExtractHeader method on the UpdateResult.
-func (ur UpdateResult) Extract() (UpdateHeader, error) {
-	var uh UpdateHeader
-	if ur.Err != nil {
-		return uh, ur.Err
-	}
-
-	if err := gophercloud.DecodeHeader(ur.Header, &uh); err != nil {
-		return uh, err
-	}
-
-	if date, ok := ur.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, ur.Header["Date"][0])
-		if err != nil {
-			return uh, err
-		}
-		uh.Date = t
-	}
-
-	return uh, nil
+func (r UpdateResult) Extract() (*UpdateHeader, error) {
+	var s *UpdateHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // DeleteHeader represents the headers returned in the response from a Delete request.
 type DeleteHeader struct {
-	ContentLength int64     `mapstructure:"Content-Length"`
-	ContentType   string    `mapstructure:"Content-Type"`
-	Date          time.Time `mapstructure:"-"`
-	TransID       string    `mapstructure:"X-Trans-Id"`
+	ContentLength string                  `json:"Content-Length"`
+	ContentType   string                  `json:"Content-Type"`
+	Date          gophercloud.JSONRFC1123 `json:"Date"`
+	TransID       string                  `json:"X-Trans-Id"`
 }
 
 // DeleteResult represents the result of a delete operation.
@@ -360,37 +251,22 @@
 
 // Extract will return a struct of headers returned from a call to Delete. To obtain
 // a map of headers, call the ExtractHeader method on the DeleteResult.
-func (dr DeleteResult) Extract() (DeleteHeader, error) {
-	var dh DeleteHeader
-	if dr.Err != nil {
-		return dh, dr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(dr.Header, &dh); err != nil {
-		return dh, err
-	}
-
-	if date, ok := dr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, dr.Header["Date"][0])
-		if err != nil {
-			return dh, err
-		}
-		dh.Date = t
-	}
-
-	return dh, nil
+func (r DeleteResult) Extract() (*DeleteHeader, error) {
+	var s *DeleteHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // CopyHeader represents the headers returned in the response from a Copy request.
 type CopyHeader struct {
-	ContentLength          int64     `mapstructure:"Content-Length"`
-	ContentType            string    `mapstructure:"Content-Type"`
-	CopiedFrom             string    `mapstructure:"X-Copied-From"`
-	CopiedFromLastModified time.Time `mapstructure:"-"`
-	Date                   time.Time `mapstructure:"-"`
-	ETag                   string    `mapstructure:"Etag"`
-	LastModified           time.Time `mapstructure:"-"`
-	TransID                string    `mapstructure:"X-Trans-Id"`
+	ContentLength          int64                   `json:"Content-Length"`
+	ContentType            string                  `json:"Content-Type"`
+	CopiedFrom             string                  `json:"X-Copied-From"`
+	CopiedFromLastModified gophercloud.JSONRFC1123 `json:"X-Copied-From-Last-Modified"`
+	Date                   gophercloud.JSONRFC1123 `json:"Date"`
+	ETag                   string                  `json:"Etag"`
+	LastModified           gophercloud.JSONRFC1123 `json:"Last-Modified"`
+	TransID                string                  `json:"X-Trans-Id"`
 }
 
 // CopyResult represents the result of a copy operation.
@@ -400,39 +276,8 @@
 
 // Extract will return a struct of headers returned from a call to Copy. To obtain
 // a map of headers, call the ExtractHeader method on the CopyResult.
-func (cr CopyResult) Extract() (CopyHeader, error) {
-	var ch CopyHeader
-	if cr.Err != nil {
-		return ch, cr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(cr.Header, &ch); err != nil {
-		return ch, err
-	}
-
-	if date, ok := cr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["Date"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.Date = t
-	}
-
-	if date, ok := cr.Header["Last-Modified"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["Last-Modified"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.LastModified = t
-	}
-
-	if date, ok := cr.Header["X-Copied-From-Last-Modified"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, cr.Header["X-Copied-From-Last-Modified"][0])
-		if err != nil {
-			return ch, err
-		}
-		ch.CopiedFromLastModified = t
-	}
-
-	return ch, nil
+func (r CopyResult) Extract() (*CopyHeader, error) {
+	var s *CopyHeader
+	err := r.ExtractInto(&s)
+	return s, err
 }
diff --git a/openstack/objectstorage/v1/objects/testing/doc.go b/openstack/objectstorage/v1/objects/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/objectstorage/v1/objects/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go
new file mode 100644
index 0000000..cf1d6c5
--- /dev/null
+++ b/openstack/objectstorage/v1/objects/testing/fixtures.go
@@ -0,0 +1,212 @@
+package testing
+
+import (
+	"crypto/md5"
+	"fmt"
+	"io"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// HandleDownloadObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
+// responds with a `Download` response.
+func HandleDownloadObjectSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, "Successful download with Gophercloud")
+	})
+}
+
+// ExpectedListInfo is the result expected from a call to `List` when full
+// info is requested.
+var ExpectedListInfo = []objects.Object{
+	{
+		Hash:         "451e372e48e0f6b1114fa0724aa79fa1",
+		LastModified: "2009-11-10 23:00:00 +0000 UTC",
+		Bytes:        14,
+		Name:         "goodbye",
+		ContentType:  "application/octet-stream",
+	},
+	{
+		Hash:         "451e372e48e0f6b1114fa0724aa79fa1",
+		LastModified: "2009-11-10 23:00:00 +0000 UTC",
+		Bytes:        14,
+		Name:         "hello",
+		ContentType:  "application/octet-stream",
+	},
+}
+
+// ExpectedListNames is the result expected from a call to `List` when just
+// object names are requested.
+var ExpectedListNames = []string{"hello", "goodbye"}
+
+// HandleListObjectsInfoSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
+// responds with a `List` response when full info is requested.
+func HandleListObjectsInfoSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, `[
+      {
+        "hash": "451e372e48e0f6b1114fa0724aa79fa1",
+        "last_modified": "2009-11-10 23:00:00 +0000 UTC",
+        "bytes": 14,
+        "name": "goodbye",
+        "content_type": "application/octet-stream"
+      },
+      {
+        "hash": "451e372e48e0f6b1114fa0724aa79fa1",
+        "last_modified": "2009-11-10 23:00:00 +0000 UTC",
+        "bytes": 14,
+        "name": "hello",
+        "content_type": "application/octet-stream"
+      }
+    ]`)
+		case "hello":
+			fmt.Fprintf(w, `[]`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+}
+
+// HandleListObjectNamesSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that
+// responds with a `List` response when only object names are requested.
+func HandleListObjectNamesSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "text/plain")
+
+		w.Header().Set("Content-Type", "text/plain")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, "hello\ngoodbye\n")
+		case "goodbye":
+			fmt.Fprintf(w, "")
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+}
+
+// HandleCreateTextObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux
+// that responds with a `Create` response. A Content-Type of "text/plain" is expected.
+func HandleCreateTextObjectSuccessfully(t *testing.T, content string) {
+	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Content-Type", "text/plain")
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		hash := md5.New()
+		io.WriteString(hash, content)
+		localChecksum := hash.Sum(nil)
+
+		w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum))
+		w.WriteHeader(http.StatusCreated)
+	})
+}
+
+// HandleCreateTextWithCacheControlSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler
+// mux that responds with a `Create` response. A Cache-Control of `max-age="3600", public` is expected.
+func HandleCreateTextWithCacheControlSuccessfully(t *testing.T, content string) {
+	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Cache-Control", `max-age="3600", public`)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		hash := md5.New()
+		io.WriteString(hash, content)
+		localChecksum := hash.Sum(nil)
+
+		w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum))
+		w.WriteHeader(http.StatusCreated)
+	})
+}
+
+// HandleCreateTypelessObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler
+// mux that responds with a `Create` response. No Content-Type header may be present in the request, so that server-
+// side content-type detection will be triggered properly.
+func HandleCreateTypelessObjectSuccessfully(t *testing.T, content string) {
+	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		if contentType, present := r.Header["Content-Type"]; present {
+			t.Errorf("Expected Content-Type header to be omitted, but was %#v", contentType)
+		}
+
+		hash := md5.New()
+		io.WriteString(hash, content)
+		localChecksum := hash.Sum(nil)
+
+		w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum))
+		w.WriteHeader(http.StatusCreated)
+	})
+}
+
+// HandleCopyObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
+// responds with a `Copy` response.
+func HandleCopyObjectSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "COPY")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Destination", "/newTestContainer/newTestObject")
+		w.WriteHeader(http.StatusCreated)
+	})
+}
+
+// HandleDeleteObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
+// responds with a `Delete` response.
+func HandleDeleteObjectSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleUpdateObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
+// responds with a `Update` response.
+func HandleUpdateObjectSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "X-Object-Meta-Gophercloud-Test", "objects")
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+// HandleGetObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
+// responds with a `Get` response.
+func HandleGetObjectSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "HEAD")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		w.Header().Add("X-Object-Meta-Gophercloud-Test", "objects")
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go
new file mode 100644
index 0000000..2b9306f
--- /dev/null
+++ b/openstack/objectstorage/v1/objects/testing/requests_test.go
@@ -0,0 +1,182 @@
+package testing
+
+import (
+	"bytes"
+	"io"
+	"strings"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestDownloadReader(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDownloadObjectSuccessfully(t)
+
+	response := objects.Download(fake.ServiceClient(), "testContainer", "testObject", nil)
+	defer response.Body.Close()
+
+	// Check reader
+	buf := bytes.NewBuffer(make([]byte, 0))
+	io.CopyN(buf, response.Body, 10)
+	th.CheckEquals(t, "Successful", string(buf.Bytes()))
+}
+
+func TestDownloadExtraction(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDownloadObjectSuccessfully(t)
+
+	response := objects.Download(fake.ServiceClient(), "testContainer", "testObject", nil)
+
+	// Check []byte extraction
+	bytes, err := response.ExtractContent()
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, "Successful download with Gophercloud", string(bytes))
+}
+
+func TestListObjectInfo(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListObjectsInfoSuccessfully(t)
+
+	count := 0
+	options := &objects.ListOpts{Full: true}
+	err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := objects.ExtractInfo(page)
+		th.AssertNoErr(t, err)
+
+		th.CheckDeepEquals(t, ExpectedListInfo, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, count, 1)
+}
+
+func TestListObjectNames(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListObjectNamesSuccessfully(t)
+
+	count := 0
+	options := &objects.ListOpts{Full: false}
+	err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := objects.ExtractNames(page)
+		if err != nil {
+			t.Errorf("Failed to extract container names: %v", err)
+			return false, err
+		}
+
+		th.CheckDeepEquals(t, ExpectedListNames, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, count, 1)
+}
+
+func TestCreateObject(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	content := "Did gyre and gimble in the wabe"
+
+	HandleCreateTextObjectSuccessfully(t, content)
+
+	options := &objects.CreateOpts{ContentType: "text/plain", Content: strings.NewReader(content)}
+	res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", options)
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestCreateObjectWithCacheControl(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	content := "All mimsy were the borogoves"
+
+	HandleCreateTextWithCacheControlSuccessfully(t, content)
+
+	options := &objects.CreateOpts{
+		CacheControl: `max-age="3600", public`,
+		Content:      strings.NewReader(content),
+	}
+	res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", options)
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestCreateObjectWithoutContentType(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	content := "The sky was the color of television, tuned to a dead channel."
+
+	HandleCreateTypelessObjectSuccessfully(t, content)
+
+	res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", &objects.CreateOpts{Content: strings.NewReader(content)})
+	th.AssertNoErr(t, res.Err)
+}
+
+/*
+func TestErrorIsRaisedForChecksumMismatch(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("ETag", "acbd18db4cc2f85cedef654fccc4a4d8")
+		w.WriteHeader(http.StatusCreated)
+	})
+
+	content := strings.NewReader("The sky was the color of television, tuned to a dead channel.")
+	res := Create(fake.ServiceClient(), "testContainer", "testObject", &CreateOpts{Content: content})
+
+	err := fmt.Errorf("Local checksum does not match API ETag header")
+	th.AssertDeepEquals(t, err, res.Err)
+}
+*/
+
+func TestCopyObject(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleCopyObjectSuccessfully(t)
+
+	options := &objects.CopyOpts{Destination: "/newTestContainer/newTestObject"}
+	res := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options)
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestDeleteObject(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDeleteObjectSuccessfully(t)
+
+	res := objects.Delete(fake.ServiceClient(), "testContainer", "testObject", nil)
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestUpateObjectMetadata(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleUpdateObjectSuccessfully(t)
+
+	options := &objects.UpdateOpts{Metadata: map[string]string{"Gophercloud-Test": "objects"}}
+	res := objects.Update(fake.ServiceClient(), "testContainer", "testObject", options)
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestGetObject(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetObjectSuccessfully(t)
+
+	expected := map[string]string{"Gophercloud-Test": "objects"}
+	actual, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", nil).ExtractMetadata()
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, expected, actual)
+}
diff --git a/openstack/objectstorage/v1/objects/urls.go b/openstack/objectstorage/v1/objects/urls.go
index d2ec62c..b3ac304 100644
--- a/openstack/objectstorage/v1/objects/urls.go
+++ b/openstack/objectstorage/v1/objects/urls.go
@@ -1,7 +1,7 @@
 package objects
 
 import (
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 func listURL(c *gophercloud.ServiceClient, container string) string {
diff --git a/openstack/objectstorage/v1/objects/urls_test.go b/openstack/objectstorage/v1/objects/urls_test.go
deleted file mode 100644
index 1dcfe35..0000000
--- a/openstack/objectstorage/v1/objects/urls_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package objects
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient(), "foo")
-	expected := endpoint + "foo"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestCopyURL(t *testing.T) {
-	actual := copyURL(endpointClient(), "foo", "bar")
-	expected := endpoint + "foo/bar"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient(), "foo", "bar")
-	expected := endpoint + "foo/bar"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo", "bar")
-	expected := endpoint + "foo/bar"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "foo", "bar")
-	expected := endpoint + "foo/bar"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestDownloadURL(t *testing.T) {
-	actual := downloadURL(endpointClient(), "foo", "bar")
-	expected := endpoint + "foo/bar"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient(), "foo", "bar")
-	expected := endpoint + "foo/bar"
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/openstack/orchestration/v1/apiversions/requests.go b/openstack/orchestration/v1/apiversions/requests.go
index f6454c8..ff383cf 100644
--- a/openstack/orchestration/v1/apiversions/requests.go
+++ b/openstack/orchestration/v1/apiversions/requests.go
@@ -1,8 +1,8 @@
 package apiversions
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // ListVersions lists all the Neutron API versions available to end-users
diff --git a/openstack/orchestration/v1/apiversions/requests_test.go b/openstack/orchestration/v1/apiversions/requests_test.go
deleted file mode 100644
index a2fc980..0000000
--- a/openstack/orchestration/v1/apiversions/requests_test.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package apiversions
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListVersions(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "versions": [
-        {
-            "status": "CURRENT",
-            "id": "v1.0",
-            "links": [
-                {
-                    "href": "http://23.253.228.211:8000/v1",
-                    "rel": "self"
-                }
-            ]
-        }
-    ]
-}`)
-	})
-
-	count := 0
-
-	ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractAPIVersions(page)
-		if err != nil {
-			t.Errorf("Failed to extract API versions: %v", err)
-			return false, err
-		}
-
-		expected := []APIVersion{
-			APIVersion{
-				Status: "CURRENT",
-				ID:     "v1.0",
-				Links: []gophercloud.Link{
-					gophercloud.Link{
-						Href: "http://23.253.228.211:8000/v1",
-						Rel:  "self",
-					},
-				},
-			},
-		}
-
-		th.AssertDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestNonJSONCannotBeExtractedIntoAPIVersions(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		w.WriteHeader(http.StatusOK)
-	})
-
-	ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		if _, err := ExtractAPIVersions(page); err == nil {
-			t.Fatalf("Expected error, got nil")
-		}
-		return true, nil
-	})
-}
diff --git a/openstack/orchestration/v1/apiversions/results.go b/openstack/orchestration/v1/apiversions/results.go
index 0700ab0..a7c22a2 100644
--- a/openstack/orchestration/v1/apiversions/results.go
+++ b/openstack/orchestration/v1/apiversions/results.go
@@ -1,17 +1,16 @@
 package apiversions
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // APIVersion represents an API version for Neutron. It contains the status of
 // the API, and its unique ID.
 type APIVersion struct {
-	Status string             `mapstructure:"status"`
-	ID     string             `mapstructure:"id"`
-	Links  []gophercloud.Link `mapstructure:"links"`
+	Status string             `json:"status"`
+	ID     string             `json:"id"`
+	Links  []gophercloud.Link `json:"links"`
 }
 
 // APIVersionPage is the page returned by a pager when traversing over a
@@ -23,20 +22,15 @@
 // IsEmpty checks whether an APIVersionPage struct is empty.
 func (r APIVersionPage) IsEmpty() (bool, error) {
 	is, err := ExtractAPIVersions(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractAPIVersions takes a collection page, extracts all of the elements,
 // and returns them a slice of APIVersion structs. It is effectively a cast.
-func ExtractAPIVersions(page pagination.Page) ([]APIVersion, error) {
-	var resp struct {
-		Versions []APIVersion `mapstructure:"versions"`
+func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) {
+	var s struct {
+		APIVersions []APIVersion `json:"versions"`
 	}
-
-	err := mapstructure.Decode(page.(APIVersionPage).Body, &resp)
-
-	return resp.Versions, err
+	err := (r.(APIVersionPage)).ExtractInto(&s)
+	return s.APIVersions, err
 }
diff --git a/openstack/orchestration/v1/apiversions/testing/doc.go b/openstack/orchestration/v1/apiversions/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/orchestration/v1/apiversions/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/orchestration/v1/apiversions/testing/requests_test.go b/openstack/orchestration/v1/apiversions/testing/requests_test.go
new file mode 100644
index 0000000..ac59b6c
--- /dev/null
+++ b/openstack/orchestration/v1/apiversions/testing/requests_test.go
@@ -0,0 +1,90 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/apiversions"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestListVersions(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "versions": [
+        {
+            "status": "CURRENT",
+            "id": "v1.0",
+            "links": [
+                {
+                    "href": "http://23.253.228.211:8000/v1",
+                    "rel": "self"
+                }
+            ]
+        }
+    ]
+}`)
+	})
+
+	count := 0
+
+	apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := apiversions.ExtractAPIVersions(page)
+		if err != nil {
+			t.Errorf("Failed to extract API versions: %v", err)
+			return false, err
+		}
+
+		expected := []apiversions.APIVersion{
+			{
+				Status: "CURRENT",
+				ID:     "v1.0",
+				Links: []gophercloud.Link{
+					gophercloud.Link{
+						Href: "http://23.253.228.211:8000/v1",
+						Rel:  "self",
+					},
+				},
+			},
+		}
+
+		th.AssertDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestNonJSONCannotBeExtractedIntoAPIVersions(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(http.StatusOK)
+	})
+
+	apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		if _, err := apiversions.ExtractAPIVersions(page); err == nil {
+			t.Fatalf("Expected error, got nil")
+		}
+		return true, nil
+	})
+}
diff --git a/openstack/orchestration/v1/apiversions/urls.go b/openstack/orchestration/v1/apiversions/urls.go
index 55d6e0e..0205405 100644
--- a/openstack/orchestration/v1/apiversions/urls.go
+++ b/openstack/orchestration/v1/apiversions/urls.go
@@ -1,6 +1,6 @@
 package apiversions
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func apiVersionsURL(c *gophercloud.ServiceClient) string {
 	return c.Endpoint
diff --git a/openstack/orchestration/v1/buildinfo/fixtures.go b/openstack/orchestration/v1/buildinfo/fixtures.go
deleted file mode 100644
index d1240b8..0000000
--- a/openstack/orchestration/v1/buildinfo/fixtures.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// +build fixtures
-
-package buildinfo
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// GetExpected represents the expected object from a Get request.
-var GetExpected = &BuildInfo{
-	API: Revision{
-		Revision: "2.4.5",
-	},
-	Engine: Revision{
-		Revision: "1.2.1",
-	},
-}
-
-// GetOutput represents the response body from a Get request.
-const GetOutput = `
-{
-  "api": {
-    "revision": "2.4.5"
-  },
-  "engine": {
-    "revision": "1.2.1"
-  }
-}`
-
-// HandleGetSuccessfully creates an HTTP handler at `/build_info`
-// on the test handler mux that responds with a `Get` response.
-func HandleGetSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/build_info", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
diff --git a/openstack/orchestration/v1/buildinfo/requests.go b/openstack/orchestration/v1/buildinfo/requests.go
index 9e03e5c..32f6032 100644
--- a/openstack/orchestration/v1/buildinfo/requests.go
+++ b/openstack/orchestration/v1/buildinfo/requests.go
@@ -1,10 +1,9 @@
 package buildinfo
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 // Get retreives data for the given stack template.
-func Get(c *gophercloud.ServiceClient) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient) (r GetResult) {
+	_, r.Err = c.Get(getURL(c), &r.Body, nil)
+	return
 }
diff --git a/openstack/orchestration/v1/buildinfo/requests_test.go b/openstack/orchestration/v1/buildinfo/requests_test.go
deleted file mode 100644
index 1e0fe23..0000000
--- a/openstack/orchestration/v1/buildinfo/requests_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package buildinfo
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestGetTemplate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t, GetOutput)
-
-	actual, err := Get(fake.ServiceClient()).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := GetExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/openstack/orchestration/v1/buildinfo/results.go b/openstack/orchestration/v1/buildinfo/results.go
index 683a434..c3d2cdb 100644
--- a/openstack/orchestration/v1/buildinfo/results.go
+++ b/openstack/orchestration/v1/buildinfo/results.go
@@ -1,19 +1,18 @@
 package buildinfo
 
 import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 // Revision represents the API/Engine revision of a Heat deployment.
 type Revision struct {
-	Revision string `mapstructure:"revision"`
+	Revision string `json:"revision"`
 }
 
 // BuildInfo represents the build information for a Heat deployment.
 type BuildInfo struct {
-	API    Revision `mapstructure:"api"`
-	Engine Revision `mapstructure:"engine"`
+	API    Revision `json:"api"`
+	Engine Revision `json:"engine"`
 }
 
 // GetResult represents the result of a Get operation.
@@ -24,14 +23,7 @@
 // Extract returns a pointer to a BuildInfo object and is called after a
 // Get operation.
 func (r GetResult) Extract() (*BuildInfo, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res BuildInfo
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return &res, nil
+	var s *BuildInfo
+	err := r.ExtractInto(&s)
+	return s, err
 }
diff --git a/openstack/orchestration/v1/buildinfo/testing/doc.go b/openstack/orchestration/v1/buildinfo/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/orchestration/v1/buildinfo/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/orchestration/v1/buildinfo/testing/fixtures.go b/openstack/orchestration/v1/buildinfo/testing/fixtures.go
new file mode 100644
index 0000000..c240d5f
--- /dev/null
+++ b/openstack/orchestration/v1/buildinfo/testing/fixtures.go
@@ -0,0 +1,46 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/buildinfo"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// GetExpected represents the expected object from a Get request.
+var GetExpected = &buildinfo.BuildInfo{
+	API: buildinfo.Revision{
+		Revision: "2.4.5",
+	},
+	Engine: buildinfo.Revision{
+		Revision: "1.2.1",
+	},
+}
+
+// GetOutput represents the response body from a Get request.
+const GetOutput = `
+{
+  "api": {
+    "revision": "2.4.5"
+  },
+  "engine": {
+    "revision": "1.2.1"
+  }
+}`
+
+// HandleGetSuccessfully creates an HTTP handler at `/build_info`
+// on the test handler mux that responds with a `Get` response.
+func HandleGetSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/build_info", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
diff --git a/openstack/orchestration/v1/buildinfo/testing/requests_test.go b/openstack/orchestration/v1/buildinfo/testing/requests_test.go
new file mode 100644
index 0000000..bd2e164
--- /dev/null
+++ b/openstack/orchestration/v1/buildinfo/testing/requests_test.go
@@ -0,0 +1,21 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/buildinfo"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestGetTemplate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t, GetOutput)
+
+	actual, err := buildinfo.Get(fake.ServiceClient()).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := GetExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
diff --git a/openstack/orchestration/v1/buildinfo/urls.go b/openstack/orchestration/v1/buildinfo/urls.go
index 2c873d0..28a2128 100644
--- a/openstack/orchestration/v1/buildinfo/urls.go
+++ b/openstack/orchestration/v1/buildinfo/urls.go
@@ -1,6 +1,6 @@
 package buildinfo
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func getURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("build_info")
diff --git a/openstack/orchestration/v1/stackevents/fixtures.go b/openstack/orchestration/v1/stackevents/fixtures.go
deleted file mode 100644
index 1004702..0000000
--- a/openstack/orchestration/v1/stackevents/fixtures.go
+++ /dev/null
@@ -1,448 +0,0 @@
-// +build fixtures
-
-package stackevents
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// FindExpected represents the expected object from a Find request.
-var FindExpected = []Event{
-	Event{
-		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
-				Rel:  "self",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-				Rel:  "resource",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-				Rel:  "stack",
-			},
-		},
-		LogicalResourceID:    "hello_world",
-		ResourceStatusReason: "state changed",
-		ResourceStatus:       "CREATE_IN_PROGRESS",
-		PhysicalResourceID:   "",
-		ID:                   "06feb26f-9298-4a9b-8749-9d770e5d577a",
-	},
-	Event{
-		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC),
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
-				Rel:  "self",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-				Rel:  "resource",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-				Rel:  "stack",
-			},
-		},
-		LogicalResourceID:    "hello_world",
-		ResourceStatusReason: "state changed",
-		ResourceStatus:       "CREATE_COMPLETE",
-		PhysicalResourceID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
-		ID:                   "93940999-7d40-44ae-8de4-19624e7b8d18",
-	},
-}
-
-// FindOutput represents the response body from a Find request.
-const FindOutput = `
-{
-  "events": [
-  {
-    "resource_name": "hello_world",
-    "event_time": "2015-02-05T21:33:11",
-    "links": [
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
-      "rel": "self"
-    },
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-      "rel": "resource"
-    },
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-      "rel": "stack"
-    }
-    ],
-    "logical_resource_id": "hello_world",
-    "resource_status_reason": "state changed",
-    "resource_status": "CREATE_IN_PROGRESS",
-    "physical_resource_id": null,
-    "id": "06feb26f-9298-4a9b-8749-9d770e5d577a"
-    },
-    {
-      "resource_name": "hello_world",
-      "event_time": "2015-02-05T21:33:27",
-      "links": [
-      {
-        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
-        "rel": "self"
-      },
-      {
-        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-        "rel": "resource"
-      },
-      {
-        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-        "rel": "stack"
-      }
-      ],
-      "logical_resource_id": "hello_world",
-      "resource_status_reason": "state changed",
-      "resource_status": "CREATE_COMPLETE",
-      "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
-      "id": "93940999-7d40-44ae-8de4-19624e7b8d18"
-    }
-  ]
-}`
-
-// HandleFindSuccessfully creates an HTTP handler at `/stacks/postman_stack/events`
-// on the test handler mux that responds with a `Find` response.
-func HandleFindSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/postman_stack/events", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
-
-// ListExpected represents the expected object from a List request.
-var ListExpected = []Event{
-	Event{
-		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
-				Rel:  "self",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-				Rel:  "resource",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-				Rel:  "stack",
-			},
-		},
-		LogicalResourceID:    "hello_world",
-		ResourceStatusReason: "state changed",
-		ResourceStatus:       "CREATE_IN_PROGRESS",
-		PhysicalResourceID:   "",
-		ID:                   "06feb26f-9298-4a9b-8749-9d770e5d577a",
-	},
-	Event{
-		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC),
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
-				Rel:  "self",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-				Rel:  "resource",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-				Rel:  "stack",
-			},
-		},
-		LogicalResourceID:    "hello_world",
-		ResourceStatusReason: "state changed",
-		ResourceStatus:       "CREATE_COMPLETE",
-		PhysicalResourceID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
-		ID:                   "93940999-7d40-44ae-8de4-19624e7b8d18",
-	},
-}
-
-// ListOutput represents the response body from a List request.
-const ListOutput = `
-{
-  "events": [
-  {
-    "resource_name": "hello_world",
-    "event_time": "2015-02-05T21:33:11",
-    "links": [
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
-      "rel": "self"
-    },
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-      "rel": "resource"
-    },
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-      "rel": "stack"
-    }
-    ],
-    "logical_resource_id": "hello_world",
-    "resource_status_reason": "state changed",
-    "resource_status": "CREATE_IN_PROGRESS",
-    "physical_resource_id": null,
-    "id": "06feb26f-9298-4a9b-8749-9d770e5d577a"
-    },
-    {
-      "resource_name": "hello_world",
-      "event_time": "2015-02-05T21:33:27",
-      "links": [
-      {
-        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
-        "rel": "self"
-      },
-      {
-        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-        "rel": "resource"
-      },
-      {
-        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-        "rel": "stack"
-      }
-      ],
-      "logical_resource_id": "hello_world",
-      "resource_status_reason": "state changed",
-      "resource_status": "CREATE_COMPLETE",
-      "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
-      "id": "93940999-7d40-44ae-8de4-19624e7b8d18"
-    }
-  ]
-}`
-
-// HandleListSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/events`
-// on the test handler mux that responds with a `List` response.
-func HandleListSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/events", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, output)
-		case "93940999-7d40-44ae-8de4-19624e7b8d18":
-			fmt.Fprintf(w, `{"events":[]}`)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-}
-
-// ListResourceEventsExpected represents the expected object from a ListResourceEvents request.
-var ListResourceEventsExpected = []Event{
-	Event{
-		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
-				Rel:  "self",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-				Rel:  "resource",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-				Rel:  "stack",
-			},
-		},
-		LogicalResourceID:    "hello_world",
-		ResourceStatusReason: "state changed",
-		ResourceStatus:       "CREATE_IN_PROGRESS",
-		PhysicalResourceID:   "",
-		ID:                   "06feb26f-9298-4a9b-8749-9d770e5d577a",
-	},
-	Event{
-		ResourceName: "hello_world",
-		Time:         time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC),
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
-				Rel:  "self",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-				Rel:  "resource",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-				Rel:  "stack",
-			},
-		},
-		LogicalResourceID:    "hello_world",
-		ResourceStatusReason: "state changed",
-		ResourceStatus:       "CREATE_COMPLETE",
-		PhysicalResourceID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
-		ID:                   "93940999-7d40-44ae-8de4-19624e7b8d18",
-	},
-}
-
-// ListResourceEventsOutput represents the response body from a ListResourceEvents request.
-const ListResourceEventsOutput = `
-{
-  "events": [
-  {
-    "resource_name": "hello_world",
-    "event_time": "2015-02-05T21:33:11",
-    "links": [
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
-      "rel": "self"
-    },
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-      "rel": "resource"
-    },
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-      "rel": "stack"
-    }
-    ],
-    "logical_resource_id": "hello_world",
-    "resource_status_reason": "state changed",
-    "resource_status": "CREATE_IN_PROGRESS",
-    "physical_resource_id": null,
-    "id": "06feb26f-9298-4a9b-8749-9d770e5d577a"
-    },
-    {
-      "resource_name": "hello_world",
-      "event_time": "2015-02-05T21:33:27",
-      "links": [
-      {
-        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
-        "rel": "self"
-      },
-      {
-        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-        "rel": "resource"
-      },
-      {
-        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-        "rel": "stack"
-      }
-      ],
-      "logical_resource_id": "hello_world",
-      "resource_status_reason": "state changed",
-      "resource_status": "CREATE_COMPLETE",
-      "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
-      "id": "93940999-7d40-44ae-8de4-19624e7b8d18"
-    }
-  ]
-}`
-
-// HandleListResourceEventsSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events`
-// on the test handler mux that responds with a `ListResourceEvents` response.
-func HandleListResourceEventsSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, output)
-		case "93940999-7d40-44ae-8de4-19624e7b8d18":
-			fmt.Fprintf(w, `{"events":[]}`)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-}
-
-// GetExpected represents the expected object from a Get request.
-var GetExpected = &Event{
-	ResourceName: "hello_world",
-	Time:         time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC),
-	Links: []gophercloud.Link{
-		gophercloud.Link{
-			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
-			Rel:  "self",
-		},
-		gophercloud.Link{
-			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-			Rel:  "resource",
-		},
-		gophercloud.Link{
-			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-			Rel:  "stack",
-		},
-	},
-	LogicalResourceID:    "hello_world",
-	ResourceStatusReason: "state changed",
-	ResourceStatus:       "CREATE_COMPLETE",
-	PhysicalResourceID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
-	ID:                   "93940999-7d40-44ae-8de4-19624e7b8d18",
-}
-
-// GetOutput represents the response body from a Get request.
-const GetOutput = `
-{
-  "event":{
-    "resource_name": "hello_world",
-    "event_time": "2015-02-05T21:33:27",
-    "links": [
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
-      "rel": "self"
-    },
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-      "rel": "resource"
-    },
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-      "rel": "stack"
-    }
-    ],
-    "logical_resource_id": "hello_world",
-    "resource_status_reason": "state changed",
-    "resource_status": "CREATE_COMPLETE",
-    "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
-    "id": "93940999-7d40-44ae-8de4-19624e7b8d18"
-  }
-}`
-
-// HandleGetSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events/93940999-7d40-44ae-8de4-19624e7b8d18`
-// on the test handler mux that responds with a `Get` response.
-func HandleGetSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events/93940999-7d40-44ae-8de4-19624e7b8d18", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
diff --git a/openstack/orchestration/v1/stackevents/requests.go b/openstack/orchestration/v1/stackevents/requests.go
index 70c6b97..e6e7f79 100644
--- a/openstack/orchestration/v1/stackevents/requests.go
+++ b/openstack/orchestration/v1/stackevents/requests.go
@@ -1,18 +1,14 @@
 package stackevents
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Find retrieves stack events for the given stack name.
-func Find(c *gophercloud.ServiceClient, stackName string) FindResult {
-	var res FindResult
-
-	_, res.Err = c.Request("GET", findURL(c, stackName), gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
-	})
-	return res
+func Find(c *gophercloud.ServiceClient, stackName string) (r FindResult) {
+	_, r.Err = c.Get(findURL(c, stackName), &r.Body, nil)
+	return
 }
 
 // SortDir is a type for specifying in which direction to sort a list of events.
@@ -102,16 +98,12 @@
 // ToStackEventListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToStackEventListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List makes a request against the API to list resources for the given stack.
 func List(client *gophercloud.ServiceClient, stackName, stackID string, opts ListOptsBuilder) pagination.Pager {
 	url := listURL(client, stackName, stackID)
-
 	if opts != nil {
 		query, err := opts.ToStackEventListQuery()
 		if err != nil {
@@ -119,14 +111,11 @@
 		}
 		url += query
 	}
-
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
 		p := EventPage{pagination.MarkerPageBase{PageResult: r}}
 		p.MarkerPageBase.Owner = p
 		return p
-	}
-
-	return pagination.NewPager(client, url, createPageFn)
+	})
 }
 
 // ListResourceEventsOptsBuilder allows extensions to add additional parameters to the
@@ -166,16 +155,12 @@
 // ToResourceEventListQuery formats a ListResourceEventsOpts into a query string.
 func (opts ListResourceEventsOpts) ToResourceEventListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // ListResourceEvents makes a request against the API to list resources for the given stack.
 func ListResourceEvents(client *gophercloud.ServiceClient, stackName, stackID, resourceName string, opts ListResourceEventsOptsBuilder) pagination.Pager {
 	url := listResourceEventsURL(client, stackName, stackID, resourceName)
-
 	if opts != nil {
 		query, err := opts.ToResourceEventListQuery()
 		if err != nil {
@@ -183,21 +168,15 @@
 		}
 		url += query
 	}
-
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
 		p := EventPage{pagination.MarkerPageBase{PageResult: r}}
 		p.MarkerPageBase.Owner = p
 		return p
-	}
-
-	return pagination.NewPager(client, url, createPageFn)
+	})
 }
 
 // Get retreives data for the given stack resource.
-func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName, eventID string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, stackName, stackID, resourceName, eventID), &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-	return res
+func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName, eventID string) (r GetResult) {
+	_, r.Err = c.Get(getURL(c, stackName, stackID, resourceName, eventID), &r.Body, nil)
+	return
 }
diff --git a/openstack/orchestration/v1/stackevents/requests_test.go b/openstack/orchestration/v1/stackevents/requests_test.go
deleted file mode 100644
index a4da4d0..0000000
--- a/openstack/orchestration/v1/stackevents/requests_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package stackevents
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestFindEvents(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleFindSuccessfully(t, FindOutput)
-
-	actual, err := Find(fake.ServiceClient(), "postman_stack").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := FindExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListSuccessfully(t, ListOutput)
-
-	count := 0
-	err := List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractEvents(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, ListExpected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestListResourceEvents(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListResourceEventsSuccessfully(t, ListResourceEventsOutput)
-
-	count := 0
-	err := ListResourceEvents(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractEvents(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, ListResourceEventsExpected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestGetEvent(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t, GetOutput)
-
-	actual, err := Get(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", "93940999-7d40-44ae-8de4-19624e7b8d18").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := GetExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/openstack/orchestration/v1/stackevents/results.go b/openstack/orchestration/v1/stackevents/results.go
index cf9e240..6c7f183 100644
--- a/openstack/orchestration/v1/stackevents/results.go
+++ b/openstack/orchestration/v1/stackevents/results.go
@@ -1,35 +1,30 @@
 package stackevents
 
 import (
-	"fmt"
-	"reflect"
-	"time"
-
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Event represents a stack event.
 type Event struct {
 	// The name of the resource for which the event occurred.
-	ResourceName string `mapstructure:"resource_name"`
+	ResourceName string `json:"resource_name"`
 	// The time the event occurred.
-	Time time.Time `mapstructure:"-"`
+	Time gophercloud.JSONRFC3339NoZ `json:"event_time"`
 	// The URLs to the event.
-	Links []gophercloud.Link `mapstructure:"links"`
+	Links []gophercloud.Link `json:"links"`
 	// The logical ID of the stack resource.
-	LogicalResourceID string `mapstructure:"logical_resource_id"`
+	LogicalResourceID string `json:"logical_resource_id"`
 	// The reason of the status of the event.
-	ResourceStatusReason string `mapstructure:"resource_status_reason"`
+	ResourceStatusReason string `json:"resource_status_reason"`
 	// The status of the event.
-	ResourceStatus string `mapstructure:"resource_status"`
+	ResourceStatus string `json:"resource_status"`
 	// The physical ID of the stack resource.
-	PhysicalResourceID string `mapstructure:"physical_resource_id"`
+	PhysicalResourceID string `json:"physical_resource_id"`
 	// The event ID.
-	ID string `mapstructure:"id"`
+	ID string `json:"id"`
 	// Properties of the stack resource.
-	ResourceProperties map[string]interface{} `mapstructure:"resource_properties"`
+	ResourceProperties map[string]interface{} `json:"resource_properties"`
 }
 
 // FindResult represents the result of a Find operation.
@@ -40,32 +35,11 @@
 // Extract returns a slice of Event objects and is called after a
 // Find operation.
 func (r FindResult) Extract() ([]Event, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Events []Event `json:"events"`
 	}
-
-	var res struct {
-		Res []Event `mapstructure:"events"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	events := r.Body.(map[string]interface{})["events"].([]interface{})
-
-	for i, eventRaw := range events {
-		event := eventRaw.(map[string]interface{})
-		if date, ok := event["event_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			res.Res[i].Time = t
-		}
-	}
-
-	return res.Res, nil
+	err := r.ExtractInto(&s)
+	return s.Events, err
 }
 
 // EventPage abstracts the raw results of making a List() request against the API.
@@ -78,10 +52,7 @@
 // IsEmpty returns true if a page contains no Server results.
 func (r EventPage) IsEmpty() (bool, error) {
 	events, err := ExtractEvents(r)
-	if err != nil {
-		return true, err
-	}
-	return len(events) == 0, nil
+	return len(events) == 0, err
 }
 
 // LastMarker returns the last stack ID in a ListResult.
@@ -97,39 +68,12 @@
 }
 
 // ExtractEvents interprets the results of a single page from a List() call, producing a slice of Event entities.
-func ExtractEvents(page pagination.Page) ([]Event, error) {
-	casted := page.(EventPage).Body
-
-	var res struct {
-		Res []Event `mapstructure:"events"`
+func ExtractEvents(r pagination.Page) ([]Event, error) {
+	var s struct {
+		Events []Event `json:"events"`
 	}
-
-	if err := mapstructure.Decode(casted, &res); err != nil {
-		return nil, err
-	}
-
-	var events []interface{}
-	switch casted.(type) {
-	case map[string]interface{}:
-		events = casted.(map[string]interface{})["events"].([]interface{})
-	case map[string][]interface{}:
-		events = casted.(map[string][]interface{})["events"]
-	default:
-		return res.Res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i, eventRaw := range events {
-		event := eventRaw.(map[string]interface{})
-		if date, ok := event["event_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			res.Res[i].Time = t
-		}
-	}
-
-	return res.Res, nil
+	err := (r.(EventPage)).ExtractInto(&s)
+	return s.Events, err
 }
 
 // ExtractResourceEvents interprets the results of a single page from a
@@ -146,27 +90,9 @@
 // Extract returns a pointer to an Event object and is called after a
 // Get operation.
 func (r GetResult) Extract() (*Event, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Event *Event `json:"event"`
 	}
-
-	var res struct {
-		Res *Event `mapstructure:"event"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	event := r.Body.(map[string]interface{})["event"].(map[string]interface{})
-
-	if date, ok := event["event_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Res.Time = t
-	}
-
-	return res.Res, nil
+	err := r.ExtractInto(&s)
+	return s.Event, err
 }
diff --git a/openstack/orchestration/v1/stackevents/testing/doc.go b/openstack/orchestration/v1/stackevents/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/orchestration/v1/stackevents/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/orchestration/v1/stackevents/testing/fixtures.go b/openstack/orchestration/v1/stackevents/testing/fixtures.go
new file mode 100644
index 0000000..a7af025
--- /dev/null
+++ b/openstack/orchestration/v1/stackevents/testing/fixtures.go
@@ -0,0 +1,447 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// FindExpected represents the expected object from a Find request.
+var FindExpected = []stackevents.Event{
+	{
+		ResourceName: "hello_world",
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC)),
+		Links: []gophercloud.Link{
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
+				Rel:  "self",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+				Rel:  "resource",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+				Rel:  "stack",
+			},
+		},
+		LogicalResourceID:    "hello_world",
+		ResourceStatusReason: "state changed",
+		ResourceStatus:       "CREATE_IN_PROGRESS",
+		PhysicalResourceID:   "",
+		ID:                   "06feb26f-9298-4a9b-8749-9d770e5d577a",
+	},
+	{
+		ResourceName: "hello_world",
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC)),
+		Links: []gophercloud.Link{
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
+				Rel:  "self",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+				Rel:  "resource",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+				Rel:  "stack",
+			},
+		},
+		LogicalResourceID:    "hello_world",
+		ResourceStatusReason: "state changed",
+		ResourceStatus:       "CREATE_COMPLETE",
+		PhysicalResourceID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
+		ID:                   "93940999-7d40-44ae-8de4-19624e7b8d18",
+	},
+}
+
+// FindOutput represents the response body from a Find request.
+const FindOutput = `
+{
+  "events": [
+  {
+    "resource_name": "hello_world",
+    "event_time": "2015-02-05T21:33:11",
+    "links": [
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
+      "rel": "self"
+    },
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+      "rel": "resource"
+    },
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+      "rel": "stack"
+    }
+    ],
+    "logical_resource_id": "hello_world",
+    "resource_status_reason": "state changed",
+    "resource_status": "CREATE_IN_PROGRESS",
+    "physical_resource_id": null,
+    "id": "06feb26f-9298-4a9b-8749-9d770e5d577a"
+    },
+    {
+      "resource_name": "hello_world",
+      "event_time": "2015-02-05T21:33:27",
+      "links": [
+      {
+        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
+        "rel": "self"
+      },
+      {
+        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+        "rel": "resource"
+      },
+      {
+        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+        "rel": "stack"
+      }
+      ],
+      "logical_resource_id": "hello_world",
+      "resource_status_reason": "state changed",
+      "resource_status": "CREATE_COMPLETE",
+      "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
+      "id": "93940999-7d40-44ae-8de4-19624e7b8d18"
+    }
+  ]
+}`
+
+// HandleFindSuccessfully creates an HTTP handler at `/stacks/postman_stack/events`
+// on the test handler mux that responds with a `Find` response.
+func HandleFindSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/postman_stack/events", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
+
+// ListExpected represents the expected object from a List request.
+var ListExpected = []stackevents.Event{
+	{
+		ResourceName: "hello_world",
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC)),
+		Links: []gophercloud.Link{
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
+				Rel:  "self",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+				Rel:  "resource",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+				Rel:  "stack",
+			},
+		},
+		LogicalResourceID:    "hello_world",
+		ResourceStatusReason: "state changed",
+		ResourceStatus:       "CREATE_IN_PROGRESS",
+		PhysicalResourceID:   "",
+		ID:                   "06feb26f-9298-4a9b-8749-9d770e5d577a",
+	},
+	{
+		ResourceName: "hello_world",
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC)),
+		Links: []gophercloud.Link{
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
+				Rel:  "self",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+				Rel:  "resource",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+				Rel:  "stack",
+			},
+		},
+		LogicalResourceID:    "hello_world",
+		ResourceStatusReason: "state changed",
+		ResourceStatus:       "CREATE_COMPLETE",
+		PhysicalResourceID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
+		ID:                   "93940999-7d40-44ae-8de4-19624e7b8d18",
+	},
+}
+
+// ListOutput represents the response body from a List request.
+const ListOutput = `
+{
+  "events": [
+  {
+    "resource_name": "hello_world",
+    "event_time": "2015-02-05T21:33:11",
+    "links": [
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
+      "rel": "self"
+    },
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+      "rel": "resource"
+    },
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+      "rel": "stack"
+    }
+    ],
+    "logical_resource_id": "hello_world",
+    "resource_status_reason": "state changed",
+    "resource_status": "CREATE_IN_PROGRESS",
+    "physical_resource_id": null,
+    "id": "06feb26f-9298-4a9b-8749-9d770e5d577a"
+    },
+    {
+      "resource_name": "hello_world",
+      "event_time": "2015-02-05T21:33:27",
+      "links": [
+      {
+        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
+        "rel": "self"
+      },
+      {
+        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+        "rel": "resource"
+      },
+      {
+        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+        "rel": "stack"
+      }
+      ],
+      "logical_resource_id": "hello_world",
+      "resource_status_reason": "state changed",
+      "resource_status": "CREATE_COMPLETE",
+      "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
+      "id": "93940999-7d40-44ae-8de4-19624e7b8d18"
+    }
+  ]
+}`
+
+// HandleListSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/events`
+// on the test handler mux that responds with a `List` response.
+func HandleListSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/events", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, output)
+		case "93940999-7d40-44ae-8de4-19624e7b8d18":
+			fmt.Fprintf(w, `{"events":[]}`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+}
+
+// ListResourceEventsExpected represents the expected object from a ListResourceEvents request.
+var ListResourceEventsExpected = []stackevents.Event{
+	{
+		ResourceName: "hello_world",
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC)),
+		Links: []gophercloud.Link{
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
+				Rel:  "self",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+				Rel:  "resource",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+				Rel:  "stack",
+			},
+		},
+		LogicalResourceID:    "hello_world",
+		ResourceStatusReason: "state changed",
+		ResourceStatus:       "CREATE_IN_PROGRESS",
+		PhysicalResourceID:   "",
+		ID:                   "06feb26f-9298-4a9b-8749-9d770e5d577a",
+	},
+	{
+		ResourceName: "hello_world",
+		Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC)),
+		Links: []gophercloud.Link{
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
+				Rel:  "self",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+				Rel:  "resource",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+				Rel:  "stack",
+			},
+		},
+		LogicalResourceID:    "hello_world",
+		ResourceStatusReason: "state changed",
+		ResourceStatus:       "CREATE_COMPLETE",
+		PhysicalResourceID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
+		ID:                   "93940999-7d40-44ae-8de4-19624e7b8d18",
+	},
+}
+
+// ListResourceEventsOutput represents the response body from a ListResourceEvents request.
+const ListResourceEventsOutput = `
+{
+  "events": [
+  {
+    "resource_name": "hello_world",
+    "event_time": "2015-02-05T21:33:11",
+    "links": [
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
+      "rel": "self"
+    },
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+      "rel": "resource"
+    },
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+      "rel": "stack"
+    }
+    ],
+    "logical_resource_id": "hello_world",
+    "resource_status_reason": "state changed",
+    "resource_status": "CREATE_IN_PROGRESS",
+    "physical_resource_id": null,
+    "id": "06feb26f-9298-4a9b-8749-9d770e5d577a"
+    },
+    {
+      "resource_name": "hello_world",
+      "event_time": "2015-02-05T21:33:27",
+      "links": [
+      {
+        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
+        "rel": "self"
+      },
+      {
+        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+        "rel": "resource"
+      },
+      {
+        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+        "rel": "stack"
+      }
+      ],
+      "logical_resource_id": "hello_world",
+      "resource_status_reason": "state changed",
+      "resource_status": "CREATE_COMPLETE",
+      "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
+      "id": "93940999-7d40-44ae-8de4-19624e7b8d18"
+    }
+  ]
+}`
+
+// HandleListResourceEventsSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events`
+// on the test handler mux that responds with a `ListResourceEvents` response.
+func HandleListResourceEventsSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, output)
+		case "93940999-7d40-44ae-8de4-19624e7b8d18":
+			fmt.Fprintf(w, `{"events":[]}`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+}
+
+// GetExpected represents the expected object from a Get request.
+var GetExpected = &stackevents.Event{
+	ResourceName: "hello_world",
+	Time:         gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC)),
+	Links: []gophercloud.Link{
+		{
+			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
+			Rel:  "self",
+		},
+		{
+			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+			Rel:  "resource",
+		},
+		{
+			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+			Rel:  "stack",
+		},
+	},
+	LogicalResourceID:    "hello_world",
+	ResourceStatusReason: "state changed",
+	ResourceStatus:       "CREATE_COMPLETE",
+	PhysicalResourceID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
+	ID:                   "93940999-7d40-44ae-8de4-19624e7b8d18",
+}
+
+// GetOutput represents the response body from a Get request.
+const GetOutput = `
+{
+  "event":{
+    "resource_name": "hello_world",
+    "event_time": "2015-02-05T21:33:27",
+    "links": [
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
+      "rel": "self"
+    },
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+      "rel": "resource"
+    },
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+      "rel": "stack"
+    }
+    ],
+    "logical_resource_id": "hello_world",
+    "resource_status_reason": "state changed",
+    "resource_status": "CREATE_COMPLETE",
+    "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
+    "id": "93940999-7d40-44ae-8de4-19624e7b8d18"
+  }
+}`
+
+// HandleGetSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events/93940999-7d40-44ae-8de4-19624e7b8d18`
+// on the test handler mux that responds with a `Get` response.
+func HandleGetSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events/93940999-7d40-44ae-8de4-19624e7b8d18", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
diff --git a/openstack/orchestration/v1/stackevents/testing/requests_test.go b/openstack/orchestration/v1/stackevents/testing/requests_test.go
new file mode 100644
index 0000000..0ad3fc3
--- /dev/null
+++ b/openstack/orchestration/v1/stackevents/testing/requests_test.go
@@ -0,0 +1,72 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestFindEvents(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleFindSuccessfully(t, FindOutput)
+
+	actual, err := stackevents.Find(fake.ServiceClient(), "postman_stack").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := FindExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListSuccessfully(t, ListOutput)
+
+	count := 0
+	err := stackevents.List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := stackevents.ExtractEvents(page)
+		th.AssertNoErr(t, err)
+
+		th.CheckDeepEquals(t, ListExpected, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, count, 1)
+}
+
+func TestListResourceEvents(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListResourceEventsSuccessfully(t, ListResourceEventsOutput)
+
+	count := 0
+	err := stackevents.ListResourceEvents(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", nil).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := stackevents.ExtractEvents(page)
+		th.AssertNoErr(t, err)
+
+		th.CheckDeepEquals(t, ListResourceEventsExpected, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, count, 1)
+}
+
+func TestGetEvent(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t, GetOutput)
+
+	actual, err := stackevents.Get(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", "93940999-7d40-44ae-8de4-19624e7b8d18").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := GetExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
diff --git a/openstack/orchestration/v1/stackevents/urls.go b/openstack/orchestration/v1/stackevents/urls.go
index 8b5eceb..6b6b330 100644
--- a/openstack/orchestration/v1/stackevents/urls.go
+++ b/openstack/orchestration/v1/stackevents/urls.go
@@ -1,6 +1,6 @@
 package stackevents
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func findURL(c *gophercloud.ServiceClient, stackName string) string {
 	return c.ServiceURL("stacks", stackName, "events")
diff --git a/openstack/orchestration/v1/stackresources/fixtures.go b/openstack/orchestration/v1/stackresources/fixtures.go
deleted file mode 100644
index 8f5df7d..0000000
--- a/openstack/orchestration/v1/stackresources/fixtures.go
+++ /dev/null
@@ -1,441 +0,0 @@
-// +build fixtures
-
-package stackresources
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// FindExpected represents the expected object from a Find request.
-var FindExpected = []Resource{
-	Resource{
-		Name: "hello_world",
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-				Rel:  "self",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-				Rel:  "stack",
-			},
-		},
-		LogicalID:    "hello_world",
-		StatusReason: "state changed",
-		UpdatedTime:  time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
-		CreationTime: time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC),
-		RequiredBy:   []interface{}{},
-		Status:       "CREATE_IN_PROGRESS",
-		PhysicalID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
-		Type:         "OS::Nova::Server",
-		Attributes:   map[string]interface{}{"SXSW": "atx"},
-		Description:  "Some resource",
-	},
-}
-
-// FindOutput represents the response body from a Find request.
-const FindOutput = `
-{
-  "resources": [
-  {
-  	"description": "Some resource",
-  	"attributes": {"SXSW": "atx"},
-    "resource_name": "hello_world",
-    "links": [
-      {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-      "rel": "self"
-      },
-      {
-        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-        "rel": "stack"
-      }
-    ],
-    "logical_resource_id": "hello_world",
-    "resource_status_reason": "state changed",
-    "updated_time": "2015-02-05T21:33:11",
-	"creation_time": "2015-02-05T21:33:10",
-    "required_by": [],
-    "resource_status": "CREATE_IN_PROGRESS",
-    "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
-    "resource_type": "OS::Nova::Server"
-  }
-  ]
-}`
-
-// HandleFindSuccessfully creates an HTTP handler at `/stacks/hello_world/resources`
-// on the test handler mux that responds with a `Find` response.
-func HandleFindSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/hello_world/resources", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
-
-// ListExpected represents the expected object from a List request.
-var ListExpected = []Resource{
-	Resource{
-		Name: "hello_world",
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-				Rel:  "self",
-			},
-			gophercloud.Link{
-				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-				Rel:  "stack",
-			},
-		},
-		LogicalID:    "hello_world",
-		StatusReason: "state changed",
-		UpdatedTime:  time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
-		CreationTime: time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC),
-		RequiredBy:   []interface{}{},
-		Status:       "CREATE_IN_PROGRESS",
-		PhysicalID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
-		Type:         "OS::Nova::Server",
-		Attributes:   map[string]interface{}{"SXSW": "atx"},
-		Description:  "Some resource",
-	},
-}
-
-// ListOutput represents the response body from a List request.
-const ListOutput = `{
-  "resources": [
-  {
-    "resource_name": "hello_world",
-    "links": [
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
-      "rel": "self"
-    },
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
-      "rel": "stack"
-    }
-    ],
-    "logical_resource_id": "hello_world",
-    "resource_status_reason": "state changed",
-    "updated_time": "2015-02-05T21:33:11",
-    "required_by": [],
-    "resource_status": "CREATE_IN_PROGRESS",
-    "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
-	"creation_time": "2015-02-05T21:33:10",
-    "resource_type": "OS::Nova::Server",
-	"attributes": {"SXSW": "atx"},
-	"description": "Some resource"
-  }
-]
-}`
-
-// HandleListSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources`
-// on the test handler mux that responds with a `List` response.
-func HandleListSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, output)
-		case "49181cd6-169a-4130-9455-31185bbfc5bf":
-			fmt.Fprintf(w, `{"resources":[]}`)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-}
-
-// GetExpected represents the expected object from a Get request.
-var GetExpected = &Resource{
-	Name: "wordpress_instance",
-	Links: []gophercloud.Link{
-		gophercloud.Link{
-			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance",
-			Rel:  "self",
-		},
-		gophercloud.Link{
-			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e",
-			Rel:  "stack",
-		},
-	},
-	LogicalID:    "wordpress_instance",
-	Attributes:   map[string]interface{}{"SXSW": "atx"},
-	StatusReason: "state changed",
-	UpdatedTime:  time.Date(2014, 12, 10, 18, 34, 35, 0, time.UTC),
-	RequiredBy:   []interface{}{},
-	Status:       "CREATE_COMPLETE",
-	PhysicalID:   "00e3a2fe-c65d-403c-9483-4db9930dd194",
-	Type:         "OS::Nova::Server",
-}
-
-// GetOutput represents the response body from a Get request.
-const GetOutput = `
-{
-  "resource": {
-    "description": "Some resource",
-    "attributes": {"SXSW": "atx"},
-    "resource_name": "wordpress_instance",
-    "description": "",
-    "links": [
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance",
-      "rel": "self"
-    },
-    {
-      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e",
-      "rel": "stack"
-    }
-    ],
-    "logical_resource_id": "wordpress_instance",
-    "resource_status": "CREATE_COMPLETE",
-    "updated_time": "2014-12-10T18:34:35",
-    "required_by": [],
-    "resource_status_reason": "state changed",
-    "physical_resource_id": "00e3a2fe-c65d-403c-9483-4db9930dd194",
-    "resource_type": "OS::Nova::Server"
-  }
-}`
-
-// HandleGetSuccessfully creates an HTTP handler at `/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance`
-// on the test handler mux that responds with a `Get` response.
-func HandleGetSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
-
-// MetadataExpected represents the expected object from a Metadata request.
-var MetadataExpected = map[string]string{
-	"number": "7",
-	"animal": "auk",
-}
-
-// MetadataOutput represents the response body from a Metadata request.
-const MetadataOutput = `
-{
-    "metadata": {
-      "number": "7",
-      "animal": "auk"
-    }
-}`
-
-// HandleMetadataSuccessfully creates an HTTP handler at `/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance/metadata`
-// on the test handler mux that responds with a `Metadata` response.
-func HandleMetadataSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance/metadata", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
-
-// ListTypesExpected represents the expected object from a ListTypes request.
-var ListTypesExpected = ResourceTypes{
-	"OS::Nova::Server",
-	"OS::Heat::RandomString",
-	"OS::Swift::Container",
-	"OS::Trove::Instance",
-	"OS::Nova::FloatingIPAssociation",
-	"OS::Cinder::VolumeAttachment",
-	"OS::Nova::FloatingIP",
-	"OS::Nova::KeyPair",
-}
-
-// same as above, but sorted
-var SortedListTypesExpected = ResourceTypes{
-	"OS::Cinder::VolumeAttachment",
-	"OS::Heat::RandomString",
-	"OS::Nova::FloatingIP",
-	"OS::Nova::FloatingIPAssociation",
-	"OS::Nova::KeyPair",
-	"OS::Nova::Server",
-	"OS::Swift::Container",
-	"OS::Trove::Instance",
-}
-
-// ListTypesOutput represents the response body from a ListTypes request.
-const ListTypesOutput = `
-{
-  "resource_types": [
-    "OS::Nova::Server",
-    "OS::Heat::RandomString",
-    "OS::Swift::Container",
-    "OS::Trove::Instance",
-    "OS::Nova::FloatingIPAssociation",
-    "OS::Cinder::VolumeAttachment",
-    "OS::Nova::FloatingIP",
-    "OS::Nova::KeyPair"
-  ]
-}`
-
-// HandleListTypesSuccessfully creates an HTTP handler at `/resource_types`
-// on the test handler mux that responds with a `ListTypes` response.
-func HandleListTypesSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/resource_types", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
-
-// GetSchemaExpected represents the expected object from a Schema request.
-var GetSchemaExpected = &TypeSchema{
-	Attributes: map[string]interface{}{
-		"an_attribute": map[string]interface{}{
-			"description": "An attribute description .",
-		},
-	},
-	Properties: map[string]interface{}{
-		"a_property": map[string]interface{}{
-			"update_allowed": false,
-			"required":       true,
-			"type":           "string",
-			"description":    "A resource description.",
-		},
-	},
-	ResourceType: "OS::Heat::AResourceName",
-	SupportStatus: map[string]interface{}{
-		"message": "A status message",
-		"status":  "SUPPORTED",
-		"version": "2014.1",
-	},
-}
-
-// GetSchemaOutput represents the response body from a Schema request.
-const GetSchemaOutput = `
-{
-  "attributes": {
-    "an_attribute": {
-      "description": "An attribute description ."
-    }
-  },
-  "properties": {
-    "a_property": {
-      "update_allowed": false,
-      "required": true,
-      "type": "string",
-      "description": "A resource description."
-    }
-  },
-  "resource_type": "OS::Heat::AResourceName",
-  "support_status": {
-	"message": "A status message",
-	"status": "SUPPORTED",
-	"version": "2014.1"
-  }
-}`
-
-// HandleGetSchemaSuccessfully creates an HTTP handler at `/resource_types/OS::Heat::AResourceName`
-// on the test handler mux that responds with a `Schema` response.
-func HandleGetSchemaSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/resource_types/OS::Heat::AResourceName", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
-
-// GetTemplateExpected represents the expected object from a Template request.
-var GetTemplateExpected = "{\n  \"HeatTemplateFormatVersion\": \"2012-12-12\",\n  \"Outputs\": {\n    \"private_key\": {\n      \"Description\": \"The private key if it has been saved.\",\n      \"Value\": \"{\\\"Fn::GetAtt\\\": [\\\"KeyPair\\\", \\\"private_key\\\"]}\"\n    },\n    \"public_key\": {\n      \"Description\": \"The public key.\",\n      \"Value\": \"{\\\"Fn::GetAtt\\\": [\\\"KeyPair\\\", \\\"public_key\\\"]}\"\n    }\n  },\n  \"Parameters\": {\n    \"name\": {\n      \"Description\": \"The name of the key pair.\",\n      \"Type\": \"String\"\n    },\n    \"public_key\": {\n      \"Description\": \"The optional public key. This allows users to supply the public key from a pre-existing key pair. If not supplied, a new key pair will be generated.\",\n      \"Type\": \"String\"\n    },\n    \"save_private_key\": {\n      \"AllowedValues\": [\n        \"True\",\n        \"true\",\n        \"False\",\n        \"false\"\n      ],\n      \"Default\": false,\n      \"Description\": \"True if the system should remember a generated private key; False otherwise.\",\n      \"Type\": \"String\"\n    }\n  },\n  \"Resources\": {\n    \"KeyPair\": {\n      \"Properties\": {\n        \"name\": {\n          \"Ref\": \"name\"\n        },\n        \"public_key\": {\n          \"Ref\": \"public_key\"\n        },\n        \"save_private_key\": {\n          \"Ref\": \"save_private_key\"\n        }\n      },\n      \"Type\": \"OS::Nova::KeyPair\"\n    }\n  }\n}"
-
-// GetTemplateOutput represents the response body from a Template request.
-const GetTemplateOutput = `
-{
-  "HeatTemplateFormatVersion": "2012-12-12",
-  "Outputs": {
-    "private_key": {
-      "Description": "The private key if it has been saved.",
-      "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"private_key\"]}"
-    },
-    "public_key": {
-      "Description": "The public key.",
-      "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"public_key\"]}"
-    }
-  },
-  "Parameters": {
-    "name": {
-      "Description": "The name of the key pair.",
-      "Type": "String"
-    },
-    "public_key": {
-      "Description": "The optional public key. This allows users to supply the public key from a pre-existing key pair. If not supplied, a new key pair will be generated.",
-      "Type": "String"
-    },
-    "save_private_key": {
-      "AllowedValues": [
-      "True",
-      "true",
-      "False",
-      "false"
-      ],
-      "Default": false,
-      "Description": "True if the system should remember a generated private key; False otherwise.",
-      "Type": "String"
-    }
-  },
-  "Resources": {
-    "KeyPair": {
-      "Properties": {
-        "name": {
-          "Ref": "name"
-        },
-        "public_key": {
-          "Ref": "public_key"
-        },
-        "save_private_key": {
-          "Ref": "save_private_key"
-        }
-      },
-      "Type": "OS::Nova::KeyPair"
-    }
-  }
-}`
-
-// HandleGetTemplateSuccessfully creates an HTTP handler at `/resource_types/OS::Heat::AResourceName/template`
-// on the test handler mux that responds with a `Template` response.
-func HandleGetTemplateSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/resource_types/OS::Heat::AResourceName/template", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
diff --git a/openstack/orchestration/v1/stackresources/requests.go b/openstack/orchestration/v1/stackresources/requests.go
index fcb8d8a..f368b76 100644
--- a/openstack/orchestration/v1/stackresources/requests.go
+++ b/openstack/orchestration/v1/stackresources/requests.go
@@ -1,19 +1,14 @@
 package stackresources
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Find retrieves stack resources for the given stack name.
-func Find(c *gophercloud.ServiceClient, stackName string) FindResult {
-	var res FindResult
-
-	// Send request to API
-	_, res.Err = c.Request("GET", findURL(c, stackName), gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
-	})
-	return res
+func Find(c *gophercloud.ServiceClient, stackName string) (r FindResult) {
+	_, r.Err = c.Get(findURL(c, stackName), &r.Body, nil)
+	return
 }
 
 // ListOptsBuilder allows extensions to add additional parameters to the
@@ -32,16 +27,12 @@
 // ToStackResourceListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToStackResourceListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List makes a request against the API to list resources for the given stack.
 func List(client *gophercloud.ServiceClient, stackName, stackID string, opts ListOptsBuilder) pagination.Pager {
 	url := listURL(client, stackName, stackID)
-
 	if opts != nil {
 		query, err := opts.ToStackResourceListQuery()
 		if err != nil {
@@ -49,65 +40,38 @@
 		}
 		url += query
 	}
-
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
 		return ResourcePage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, url, createPageFn)
+	})
 }
 
 // Get retreives data for the given stack resource.
-func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) GetResult {
-	var res GetResult
-
-	// Send request to API
-	_, res.Err = c.Get(getURL(c, stackName, stackID, resourceName), &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-	return res
+func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) (r GetResult) {
+	_, r.Err = c.Get(getURL(c, stackName, stackID, resourceName), &r.Body, nil)
+	return
 }
 
 // Metadata retreives the metadata for the given stack resource.
-func Metadata(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) MetadataResult {
-	var res MetadataResult
-
-	// Send request to API
-	_, res.Err = c.Get(metadataURL(c, stackName, stackID, resourceName), &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-	return res
+func Metadata(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) (r MetadataResult) {
+	_, r.Err = c.Get(metadataURL(c, stackName, stackID, resourceName), &r.Body, nil)
+	return
 }
 
 // ListTypes makes a request against the API to list resource types.
 func ListTypes(client *gophercloud.ServiceClient) pagination.Pager {
-	url := listTypesURL(client)
-
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listTypesURL(client), func(r pagination.PageResult) pagination.Page {
 		return ResourceTypePage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, url, createPageFn)
+	})
 }
 
 // Schema retreives the schema for the given resource type.
-func Schema(c *gophercloud.ServiceClient, resourceType string) SchemaResult {
-	var res SchemaResult
-
-	// Send request to API
-	_, res.Err = c.Get(schemaURL(c, resourceType), &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-	return res
+func Schema(c *gophercloud.ServiceClient, resourceType string) (r SchemaResult) {
+	_, r.Err = c.Get(schemaURL(c, resourceType), &r.Body, nil)
+	return
 }
 
 // Template retreives the template representation for the given resource type.
-func Template(c *gophercloud.ServiceClient, resourceType string) TemplateResult {
-	var res TemplateResult
-
-	// Send request to API
-	_, res.Err = c.Get(templateURL(c, resourceType), &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-	return res
+func Template(c *gophercloud.ServiceClient, resourceType string) (r TemplateResult) {
+	_, r.Err = c.Get(templateURL(c, resourceType), &r.Body, nil)
+	return
 }
diff --git a/openstack/orchestration/v1/stackresources/requests_test.go b/openstack/orchestration/v1/stackresources/requests_test.go
deleted file mode 100644
index e5045a7..0000000
--- a/openstack/orchestration/v1/stackresources/requests_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package stackresources
-
-import (
-	"sort"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestFindResources(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleFindSuccessfully(t, FindOutput)
-
-	actual, err := Find(fake.ServiceClient(), "hello_world").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := FindExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestListResources(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListSuccessfully(t, ListOutput)
-
-	count := 0
-	err := List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractResources(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, ListExpected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestGetResource(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t, GetOutput)
-
-	actual, err := Get(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := GetExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestResourceMetadata(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleMetadataSuccessfully(t, MetadataOutput)
-
-	actual, err := Metadata(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := MetadataExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestListResourceTypes(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListTypesSuccessfully(t, ListTypesOutput)
-
-	count := 0
-	err := ListTypes(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractResourceTypes(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, ListTypesExpected, actual)
-		// test if sorting works
-		sort.Sort(actual)
-		th.CheckDeepEquals(t, SortedListTypesExpected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGetResourceSchema(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSchemaSuccessfully(t, GetSchemaOutput)
-
-	actual, err := Schema(fake.ServiceClient(), "OS::Heat::AResourceName").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := GetSchemaExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestGetResourceTemplate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetTemplateSuccessfully(t, GetTemplateOutput)
-
-	actual, err := Template(fake.ServiceClient(), "OS::Heat::AResourceName").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := GetTemplateExpected
-	th.AssertDeepEquals(t, expected, string(actual))
-}
diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go
index 6ddc766..bd3e29f 100644
--- a/openstack/orchestration/v1/stackresources/results.go
+++ b/openstack/orchestration/v1/stackresources/results.go
@@ -2,29 +2,25 @@
 
 import (
 	"encoding/json"
-	"fmt"
-	"reflect"
-	"time"
 
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // Resource represents a stack resource.
 type Resource struct {
-	Attributes   map[string]interface{} `mapstructure:"attributes"`
-	CreationTime time.Time              `mapstructure:"-"`
-	Description  string                 `mapstructure:"description"`
-	Links        []gophercloud.Link     `mapstructure:"links"`
-	LogicalID    string                 `mapstructure:"logical_resource_id"`
-	Name         string                 `mapstructure:"resource_name"`
-	PhysicalID   string                 `mapstructure:"physical_resource_id"`
-	RequiredBy   []interface{}          `mapstructure:"required_by"`
-	Status       string                 `mapstructure:"resource_status"`
-	StatusReason string                 `mapstructure:"resource_status_reason"`
-	Type         string                 `mapstructure:"resource_type"`
-	UpdatedTime  time.Time              `mapstructure:"-"`
+	Attributes   map[string]interface{}     `json:"attributes"`
+	CreationTime gophercloud.JSONRFC3339NoZ `json:"creation_time"`
+	Description  string                     `json:"description"`
+	Links        []gophercloud.Link         `json:"links"`
+	LogicalID    string                     `json:"logical_resource_id"`
+	Name         string                     `json:"resource_name"`
+	PhysicalID   string                     `json:"physical_resource_id"`
+	RequiredBy   []interface{}              `json:"required_by"`
+	Status       string                     `json:"resource_status"`
+	StatusReason string                     `json:"resource_status_reason"`
+	Type         string                     `json:"resource_type"`
+	UpdatedTime  gophercloud.JSONRFC3339NoZ `json:"updated_time"`
 }
 
 // FindResult represents the result of a Find operation.
@@ -35,39 +31,11 @@
 // Extract returns a slice of Resource objects and is called after a
 // Find operation.
 func (r FindResult) Extract() ([]Resource, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Resources []Resource `json:"resources"`
 	}
-
-	var res struct {
-		Res []Resource `mapstructure:"resources"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	resources := r.Body.(map[string]interface{})["resources"].([]interface{})
-
-	for i, resourceRaw := range resources {
-		resource := resourceRaw.(map[string]interface{})
-		if date, ok := resource["updated_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			res.Res[i].UpdatedTime = t
-		}
-		if date, ok := resource["creation_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			res.Res[i].CreationTime = t
-		}
-	}
-
-	return res.Res, nil
+	err := r.ExtractInto(&s)
+	return s.Resources, err
 }
 
 // ResourcePage abstracts the raw results of making a List() request against the API.
@@ -80,51 +48,16 @@
 // IsEmpty returns true if a page contains no Server results.
 func (r ResourcePage) IsEmpty() (bool, error) {
 	resources, err := ExtractResources(r)
-	if err != nil {
-		return true, err
-	}
-	return len(resources) == 0, nil
+	return len(resources) == 0, err
 }
 
 // ExtractResources interprets the results of a single page from a List() call, producing a slice of Resource entities.
-func ExtractResources(page pagination.Page) ([]Resource, error) {
-	casted := page.(ResourcePage).Body
-
-	var response struct {
-		Resources []Resource `mapstructure:"resources"`
+func ExtractResources(r pagination.Page) ([]Resource, error) {
+	var s struct {
+		Resources []Resource `json:"resources"`
 	}
-	if err := mapstructure.Decode(casted, &response); err != nil {
-		return nil, err
-	}
-	var resources []interface{}
-	switch casted.(type) {
-	case map[string]interface{}:
-		resources = casted.(map[string]interface{})["resources"].([]interface{})
-	case map[string][]interface{}:
-		resources = casted.(map[string][]interface{})["resources"]
-	default:
-		return response.Resources, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i, resourceRaw := range resources {
-		resource := resourceRaw.(map[string]interface{})
-		if date, ok := resource["updated_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			response.Resources[i].UpdatedTime = t
-		}
-		if date, ok := resource["creation_time"]; ok && date != nil {
-			t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			response.Resources[i].CreationTime = t
-		}
-	}
-
-	return response.Resources, nil
+	err := (r.(ResourcePage)).ExtractInto(&s)
+	return s.Resources, err
 }
 
 // GetResult represents the result of a Get operation.
@@ -135,36 +68,11 @@
 // Extract returns a pointer to a Resource object and is called after a
 // Get operation.
 func (r GetResult) Extract() (*Resource, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Resource *Resource `json:"resource"`
 	}
-
-	var res struct {
-		Res *Resource `mapstructure:"resource"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	resource := r.Body.(map[string]interface{})["resource"].(map[string]interface{})
-
-	if date, ok := resource["updated_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Res.UpdatedTime = t
-	}
-	if date, ok := resource["creation_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Res.CreationTime = t
-	}
-
-	return res.Res, nil
+	err := r.ExtractInto(&s)
+	return s.Resource, err
 }
 
 // MetadataResult represents the result of a Metadata operation.
@@ -175,19 +83,11 @@
 // Extract returns a map object and is called after a
 // Metadata operation.
 func (r MetadataResult) Extract() (map[string]string, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Meta map[string]string `json:"metadata"`
 	}
-
-	var res struct {
-		Meta map[string]string `mapstructure:"metadata"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return res.Meta, nil
+	err := r.ExtractInto(&s)
+	return s.Meta, err
 }
 
 // ResourceTypePage abstracts the raw results of making a ListTypes() request against the API.
@@ -200,10 +100,7 @@
 // IsEmpty returns true if a ResourceTypePage contains no resource types.
 func (r ResourceTypePage) IsEmpty() (bool, error) {
 	rts, err := ExtractResourceTypes(r)
-	if err != nil {
-		return true, err
-	}
-	return len(rts) == 0, nil
+	return len(rts) == 0, err
 }
 
 // ResourceTypes represents the type that holds the result of ExtractResourceTypes.
@@ -223,25 +120,20 @@
 }
 
 // ExtractResourceTypes extracts and returns resource types.
-func ExtractResourceTypes(page pagination.Page) (ResourceTypes, error) {
-	casted := page.(ResourceTypePage).Body
-
-	var response struct {
-		ResourceTypes ResourceTypes `mapstructure:"resource_types"`
+func ExtractResourceTypes(r pagination.Page) (ResourceTypes, error) {
+	var s struct {
+		ResourceTypes ResourceTypes `json:"resource_types"`
 	}
-
-	if err := mapstructure.Decode(casted, &response); err != nil {
-		return nil, err
-	}
-	return response.ResourceTypes, nil
+	err := (r.(ResourceTypePage)).ExtractInto(&s)
+	return s.ResourceTypes, err
 }
 
 // TypeSchema represents a stack resource schema.
 type TypeSchema struct {
-	Attributes    map[string]interface{} `mapstructure:"attributes"`
-	Properties    map[string]interface{} `mapstrucutre:"properties"`
-	ResourceType  string                 `mapstructure:"resource_type"`
-	SupportStatus map[string]interface{} `mapstructure:"support_status"`
+	Attributes    map[string]interface{} `json:"attributes"`
+	Properties    map[string]interface{} `json:"properties"`
+	ResourceType  string                 `json:"resource_type"`
+	SupportStatus map[string]interface{} `json:"support_status"`
 }
 
 // SchemaResult represents the result of a Schema operation.
@@ -252,17 +144,9 @@
 // Extract returns a pointer to a TypeSchema object and is called after a
 // Schema operation.
 func (r SchemaResult) Extract() (*TypeSchema, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res TypeSchema
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return &res, nil
+	var s *TypeSchema
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // TemplateResult represents the result of a Template operation.
@@ -277,8 +161,5 @@
 		return nil, r.Err
 	}
 	template, err := json.MarshalIndent(r.Body, "", "  ")
-	if err != nil {
-		return nil, err
-	}
-	return template, nil
+	return template, err
 }
diff --git a/openstack/orchestration/v1/stackresources/testing/doc.go b/openstack/orchestration/v1/stackresources/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/orchestration/v1/stackresources/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/orchestration/v1/stackresources/testing/fixtures.go b/openstack/orchestration/v1/stackresources/testing/fixtures.go
new file mode 100644
index 0000000..adcd4c6
--- /dev/null
+++ b/openstack/orchestration/v1/stackresources/testing/fixtures.go
@@ -0,0 +1,440 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// FindExpected represents the expected object from a Find request.
+var FindExpected = []stackresources.Resource{
+	{
+		Name: "hello_world",
+		Links: []gophercloud.Link{
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+				Rel:  "self",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+				Rel:  "stack",
+			},
+		},
+		LogicalID:    "hello_world",
+		StatusReason: "state changed",
+		UpdatedTime:  gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC)),
+		CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC)),
+		RequiredBy:   []interface{}{},
+		Status:       "CREATE_IN_PROGRESS",
+		PhysicalID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
+		Type:         "OS::Nova::Server",
+		Attributes:   map[string]interface{}{"SXSW": "atx"},
+		Description:  "Some resource",
+	},
+}
+
+// FindOutput represents the response body from a Find request.
+const FindOutput = `
+{
+  "resources": [
+  {
+  	"description": "Some resource",
+  	"attributes": {"SXSW": "atx"},
+    "resource_name": "hello_world",
+    "links": [
+      {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+      "rel": "self"
+      },
+      {
+        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+        "rel": "stack"
+      }
+    ],
+    "logical_resource_id": "hello_world",
+    "resource_status_reason": "state changed",
+    "updated_time": "2015-02-05T21:33:11",
+	"creation_time": "2015-02-05T21:33:10",
+    "required_by": [],
+    "resource_status": "CREATE_IN_PROGRESS",
+    "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
+    "resource_type": "OS::Nova::Server"
+  }
+  ]
+}`
+
+// HandleFindSuccessfully creates an HTTP handler at `/stacks/hello_world/resources`
+// on the test handler mux that responds with a `Find` response.
+func HandleFindSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/hello_world/resources", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
+
+// ListExpected represents the expected object from a List request.
+var ListExpected = []stackresources.Resource{
+	{
+		Name: "hello_world",
+		Links: []gophercloud.Link{
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+				Rel:  "self",
+			},
+			{
+				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+				Rel:  "stack",
+			},
+		},
+		LogicalID:    "hello_world",
+		StatusReason: "state changed",
+		UpdatedTime:  gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC)),
+		CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC)),
+		RequiredBy:   []interface{}{},
+		Status:       "CREATE_IN_PROGRESS",
+		PhysicalID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
+		Type:         "OS::Nova::Server",
+		Attributes:   map[string]interface{}{"SXSW": "atx"},
+		Description:  "Some resource",
+	},
+}
+
+// ListOutput represents the response body from a List request.
+const ListOutput = `{
+  "resources": [
+  {
+    "resource_name": "hello_world",
+    "links": [
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
+      "rel": "self"
+    },
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
+      "rel": "stack"
+    }
+    ],
+    "logical_resource_id": "hello_world",
+    "resource_status_reason": "state changed",
+    "updated_time": "2015-02-05T21:33:11",
+    "required_by": [],
+    "resource_status": "CREATE_IN_PROGRESS",
+    "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
+	"creation_time": "2015-02-05T21:33:10",
+    "resource_type": "OS::Nova::Server",
+	"attributes": {"SXSW": "atx"},
+	"description": "Some resource"
+  }
+]
+}`
+
+// HandleListSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources`
+// on the test handler mux that responds with a `List` response.
+func HandleListSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, output)
+		case "49181cd6-169a-4130-9455-31185bbfc5bf":
+			fmt.Fprintf(w, `{"resources":[]}`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+}
+
+// GetExpected represents the expected object from a Get request.
+var GetExpected = &stackresources.Resource{
+	Name: "wordpress_instance",
+	Links: []gophercloud.Link{
+		{
+			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance",
+			Rel:  "self",
+		},
+		{
+			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e",
+			Rel:  "stack",
+		},
+	},
+	LogicalID:    "wordpress_instance",
+	Attributes:   map[string]interface{}{"SXSW": "atx"},
+	StatusReason: "state changed",
+	UpdatedTime:  gophercloud.JSONRFC3339NoZ(time.Date(2014, 12, 10, 18, 34, 35, 0, time.UTC)),
+	RequiredBy:   []interface{}{},
+	Status:       "CREATE_COMPLETE",
+	PhysicalID:   "00e3a2fe-c65d-403c-9483-4db9930dd194",
+	Type:         "OS::Nova::Server",
+}
+
+// GetOutput represents the response body from a Get request.
+const GetOutput = `
+{
+  "resource": {
+    "description": "Some resource",
+    "attributes": {"SXSW": "atx"},
+    "resource_name": "wordpress_instance",
+    "description": "",
+    "links": [
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance",
+      "rel": "self"
+    },
+    {
+      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e",
+      "rel": "stack"
+    }
+    ],
+    "logical_resource_id": "wordpress_instance",
+    "resource_status": "CREATE_COMPLETE",
+    "updated_time": "2014-12-10T18:34:35",
+    "required_by": [],
+    "resource_status_reason": "state changed",
+    "physical_resource_id": "00e3a2fe-c65d-403c-9483-4db9930dd194",
+    "resource_type": "OS::Nova::Server"
+  }
+}`
+
+// HandleGetSuccessfully creates an HTTP handler at `/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance`
+// on the test handler mux that responds with a `Get` response.
+func HandleGetSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
+
+// MetadataExpected represents the expected object from a Metadata request.
+var MetadataExpected = map[string]string{
+	"number": "7",
+	"animal": "auk",
+}
+
+// MetadataOutput represents the response body from a Metadata request.
+const MetadataOutput = `
+{
+    "metadata": {
+      "number": "7",
+      "animal": "auk"
+    }
+}`
+
+// HandleMetadataSuccessfully creates an HTTP handler at `/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance/metadata`
+// on the test handler mux that responds with a `Metadata` response.
+func HandleMetadataSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance/metadata", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
+
+// ListTypesExpected represents the expected object from a ListTypes request.
+var ListTypesExpected = stackresources.ResourceTypes{
+	"OS::Nova::Server",
+	"OS::Heat::RandomString",
+	"OS::Swift::Container",
+	"OS::Trove::Instance",
+	"OS::Nova::FloatingIPAssociation",
+	"OS::Cinder::VolumeAttachment",
+	"OS::Nova::FloatingIP",
+	"OS::Nova::KeyPair",
+}
+
+// same as above, but sorted
+var SortedListTypesExpected = stackresources.ResourceTypes{
+	"OS::Cinder::VolumeAttachment",
+	"OS::Heat::RandomString",
+	"OS::Nova::FloatingIP",
+	"OS::Nova::FloatingIPAssociation",
+	"OS::Nova::KeyPair",
+	"OS::Nova::Server",
+	"OS::Swift::Container",
+	"OS::Trove::Instance",
+}
+
+// ListTypesOutput represents the response body from a ListTypes request.
+const ListTypesOutput = `
+{
+  "resource_types": [
+    "OS::Nova::Server",
+    "OS::Heat::RandomString",
+    "OS::Swift::Container",
+    "OS::Trove::Instance",
+    "OS::Nova::FloatingIPAssociation",
+    "OS::Cinder::VolumeAttachment",
+    "OS::Nova::FloatingIP",
+    "OS::Nova::KeyPair"
+  ]
+}`
+
+// HandleListTypesSuccessfully creates an HTTP handler at `/resource_types`
+// on the test handler mux that responds with a `ListTypes` response.
+func HandleListTypesSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/resource_types", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
+
+// GetSchemaExpected represents the expected object from a Schema request.
+var GetSchemaExpected = &stackresources.TypeSchema{
+	Attributes: map[string]interface{}{
+		"an_attribute": map[string]interface{}{
+			"description": "An attribute description .",
+		},
+	},
+	Properties: map[string]interface{}{
+		"a_property": map[string]interface{}{
+			"update_allowed": false,
+			"required":       true,
+			"type":           "string",
+			"description":    "A resource description.",
+		},
+	},
+	ResourceType: "OS::Heat::AResourceName",
+	SupportStatus: map[string]interface{}{
+		"message": "A status message",
+		"status":  "SUPPORTED",
+		"version": "2014.1",
+	},
+}
+
+// GetSchemaOutput represents the response body from a Schema request.
+const GetSchemaOutput = `
+{
+  "attributes": {
+    "an_attribute": {
+      "description": "An attribute description ."
+    }
+  },
+  "properties": {
+    "a_property": {
+      "update_allowed": false,
+      "required": true,
+      "type": "string",
+      "description": "A resource description."
+    }
+  },
+  "resource_type": "OS::Heat::AResourceName",
+  "support_status": {
+	"message": "A status message",
+	"status": "SUPPORTED",
+	"version": "2014.1"
+  }
+}`
+
+// HandleGetSchemaSuccessfully creates an HTTP handler at `/resource_types/OS::Heat::AResourceName`
+// on the test handler mux that responds with a `Schema` response.
+func HandleGetSchemaSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/resource_types/OS::Heat::AResourceName", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
+
+// GetTemplateExpected represents the expected object from a Template request.
+var GetTemplateExpected = "{\n  \"HeatTemplateFormatVersion\": \"2012-12-12\",\n  \"Outputs\": {\n    \"private_key\": {\n      \"Description\": \"The private key if it has been saved.\",\n      \"Value\": \"{\\\"Fn::GetAtt\\\": [\\\"KeyPair\\\", \\\"private_key\\\"]}\"\n    },\n    \"public_key\": {\n      \"Description\": \"The public key.\",\n      \"Value\": \"{\\\"Fn::GetAtt\\\": [\\\"KeyPair\\\", \\\"public_key\\\"]}\"\n    }\n  },\n  \"Parameters\": {\n    \"name\": {\n      \"Description\": \"The name of the key pair.\",\n      \"Type\": \"String\"\n    },\n    \"public_key\": {\n      \"Description\": \"The optional public key. This allows users to supply the public key from a pre-existing key pair. If not supplied, a new key pair will be generated.\",\n      \"Type\": \"String\"\n    },\n    \"save_private_key\": {\n      \"AllowedValues\": [\n        \"True\",\n        \"true\",\n        \"False\",\n        \"false\"\n      ],\n      \"Default\": false,\n      \"Description\": \"True if the system should remember a generated private key; False otherwise.\",\n      \"Type\": \"String\"\n    }\n  },\n  \"Resources\": {\n    \"KeyPair\": {\n      \"Properties\": {\n        \"name\": {\n          \"Ref\": \"name\"\n        },\n        \"public_key\": {\n          \"Ref\": \"public_key\"\n        },\n        \"save_private_key\": {\n          \"Ref\": \"save_private_key\"\n        }\n      },\n      \"Type\": \"OS::Nova::KeyPair\"\n    }\n  }\n}"
+
+// GetTemplateOutput represents the response body from a Template request.
+const GetTemplateOutput = `
+{
+  "HeatTemplateFormatVersion": "2012-12-12",
+  "Outputs": {
+    "private_key": {
+      "Description": "The private key if it has been saved.",
+      "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"private_key\"]}"
+    },
+    "public_key": {
+      "Description": "The public key.",
+      "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"public_key\"]}"
+    }
+  },
+  "Parameters": {
+    "name": {
+      "Description": "The name of the key pair.",
+      "Type": "String"
+    },
+    "public_key": {
+      "Description": "The optional public key. This allows users to supply the public key from a pre-existing key pair. If not supplied, a new key pair will be generated.",
+      "Type": "String"
+    },
+    "save_private_key": {
+      "AllowedValues": [
+      "True",
+      "true",
+      "False",
+      "false"
+      ],
+      "Default": false,
+      "Description": "True if the system should remember a generated private key; False otherwise.",
+      "Type": "String"
+    }
+  },
+  "Resources": {
+    "KeyPair": {
+      "Properties": {
+        "name": {
+          "Ref": "name"
+        },
+        "public_key": {
+          "Ref": "public_key"
+        },
+        "save_private_key": {
+          "Ref": "save_private_key"
+        }
+      },
+      "Type": "OS::Nova::KeyPair"
+    }
+  }
+}`
+
+// HandleGetTemplateSuccessfully creates an HTTP handler at `/resource_types/OS::Heat::AResourceName/template`
+// on the test handler mux that responds with a `Template` response.
+func HandleGetTemplateSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/resource_types/OS::Heat::AResourceName/template", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
diff --git a/openstack/orchestration/v1/stackresources/testing/requests_test.go b/openstack/orchestration/v1/stackresources/testing/requests_test.go
new file mode 100644
index 0000000..c714047
--- /dev/null
+++ b/openstack/orchestration/v1/stackresources/testing/requests_test.go
@@ -0,0 +1,112 @@
+package testing
+
+import (
+	"sort"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestFindResources(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleFindSuccessfully(t, FindOutput)
+
+	actual, err := stackresources.Find(fake.ServiceClient(), "hello_world").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := FindExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestListResources(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListSuccessfully(t, ListOutput)
+
+	count := 0
+	err := stackresources.List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := stackresources.ExtractResources(page)
+		th.AssertNoErr(t, err)
+
+		th.CheckDeepEquals(t, ListExpected, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, count, 1)
+}
+
+func TestGetResource(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t, GetOutput)
+
+	actual, err := stackresources.Get(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := GetExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestResourceMetadata(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleMetadataSuccessfully(t, MetadataOutput)
+
+	actual, err := stackresources.Metadata(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := MetadataExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestListResourceTypes(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListTypesSuccessfully(t, ListTypesOutput)
+
+	count := 0
+	err := stackresources.ListTypes(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := stackresources.ExtractResourceTypes(page)
+		th.AssertNoErr(t, err)
+
+		th.CheckDeepEquals(t, ListTypesExpected, actual)
+		// test if sorting works
+		sort.Sort(actual)
+		th.CheckDeepEquals(t, SortedListTypesExpected, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, 1, count)
+}
+
+func TestGetResourceSchema(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSchemaSuccessfully(t, GetSchemaOutput)
+
+	actual, err := stackresources.Schema(fake.ServiceClient(), "OS::Heat::AResourceName").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := GetSchemaExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestGetResourceTemplate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetTemplateSuccessfully(t, GetTemplateOutput)
+
+	actual, err := stackresources.Template(fake.ServiceClient(), "OS::Heat::AResourceName").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := GetTemplateExpected
+	th.AssertDeepEquals(t, expected, string(actual))
+}
diff --git a/openstack/orchestration/v1/stackresources/urls.go b/openstack/orchestration/v1/stackresources/urls.go
index ef078d9..bbddc69 100644
--- a/openstack/orchestration/v1/stackresources/urls.go
+++ b/openstack/orchestration/v1/stackresources/urls.go
@@ -1,6 +1,6 @@
 package stackresources
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func findURL(c *gophercloud.ServiceClient, stackName string) string {
 	return c.ServiceURL("stacks", stackName, "resources")
diff --git a/openstack/orchestration/v1/stacks/environment.go b/openstack/orchestration/v1/stacks/environment.go
index abaff20..8698918 100644
--- a/openstack/orchestration/v1/stacks/environment.go
+++ b/openstack/orchestration/v1/stacks/environment.go
@@ -1,9 +1,6 @@
 package stacks
 
-import (
-	"fmt"
-	"strings"
-)
+import "strings"
 
 // Environment is a structure that represents stack environments
 type Environment struct {
@@ -26,7 +23,7 @@
 	}
 	for key := range e.Parsed {
 		if _, ok := EnvironmentSections[key]; !ok {
-			return fmt.Errorf("Environment has wrong section: %s", key)
+			return ErrInvalidEnvironment{Section: key}
 		}
 	}
 	return nil
diff --git a/openstack/orchestration/v1/stacks/environment_test.go b/openstack/orchestration/v1/stacks/environment_test.go
index 3a3c2b9..a7e3aae 100644
--- a/openstack/orchestration/v1/stacks/environment_test.go
+++ b/openstack/orchestration/v1/stacks/environment_test.go
@@ -7,10 +7,11 @@
 	"strings"
 	"testing"
 
-	th "github.com/rackspace/gophercloud/testhelper"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestEnvironmentValidation(t *testing.T) {
+
 	environmentJSON := new(Environment)
 	environmentJSON.Bin = []byte(ValidJSONEnvironment)
 	err := environmentJSON.Validate()
diff --git a/openstack/orchestration/v1/stacks/errors.go b/openstack/orchestration/v1/stacks/errors.go
new file mode 100644
index 0000000..cd6c18f
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/errors.go
@@ -0,0 +1,33 @@
+package stacks
+
+import (
+	"fmt"
+
+	"github.com/gophercloud/gophercloud"
+)
+
+type ErrInvalidEnvironment struct {
+	gophercloud.BaseError
+	Section string
+}
+
+func (e ErrInvalidEnvironment) Error() string {
+	return fmt.Sprintf("Environment has wrong section: %s", e.Section)
+}
+
+type ErrInvalidDataFormat struct {
+	gophercloud.BaseError
+}
+
+func (e ErrInvalidDataFormat) Error() string {
+	return fmt.Sprintf("Data in neither json nor yaml format.")
+}
+
+type ErrInvalidTemplateFormatVersion struct {
+	gophercloud.BaseError
+	Version string
+}
+
+func (e ErrInvalidTemplateFormatVersion) Error() string {
+	return fmt.Sprintf("Template format version not found.")
+}
diff --git a/openstack/orchestration/v1/stacks/fixtures.go b/openstack/orchestration/v1/stacks/fixtures.go
index 9c95a89..d6fd075 100644
--- a/openstack/orchestration/v1/stacks/fixtures.go
+++ b/openstack/orchestration/v1/stacks/fixtures.go
@@ -1,412 +1,5 @@
-// +build fixtures
-
 package stacks
 
-import (
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// CreateExpected represents the expected object from a Create request.
-var CreateExpected = &CreatedStack{
-	ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-	Links: []gophercloud.Link{
-		gophercloud.Link{
-			Href: "http://168.28.170.117:8004/v1/98606384f58drad0bhdb7d02779549ac/stacks/stackcreated/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-			Rel:  "self",
-		},
-	},
-}
-
-// CreateOutput represents the response body from a Create request.
-const CreateOutput = `
-{
-  "stack": {
-    "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-    "links": [
-    {
-      "href": "http://168.28.170.117:8004/v1/98606384f58drad0bhdb7d02779549ac/stacks/stackcreated/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-      "rel": "self"
-    }
-    ]
-  }
-}`
-
-// HandleCreateSuccessfully creates an HTTP handler at `/stacks` on the test handler mux
-// that responds with a `Create` response.
-func HandleCreateSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, output)
-	})
-}
-
-// ListExpected represents the expected object from a List request.
-var ListExpected = []ListedStack{
-	ListedStack{
-		Description: "Simple template to test heat commands",
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-				Rel:  "self",
-			},
-		},
-		StatusReason: "Stack CREATE completed successfully",
-		Name:         "postman_stack",
-		CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
-		Status:       "CREATE_COMPLETE",
-		ID:           "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-		Tags:         []string{"rackspace", "atx"},
-	},
-	ListedStack{
-		Description: "Simple template to test heat commands",
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada",
-				Rel:  "self",
-			},
-		},
-		StatusReason: "Stack successfully updated",
-		Name:         "gophercloud-test-stack-2",
-		CreationTime: time.Date(2014, 12, 11, 17, 39, 16, 0, time.UTC),
-		UpdatedTime:  time.Date(2014, 12, 11, 17, 40, 37, 0, time.UTC),
-		Status:       "UPDATE_COMPLETE",
-		ID:           "db6977b2-27aa-4775-9ae7-6213212d4ada",
-		Tags:         []string{"sfo", "satx"},
-	},
-}
-
-// FullListOutput represents the response body from a List request without a marker.
-const FullListOutput = `
-{
-  "stacks": [
-  {
-    "description": "Simple template to test heat commands",
-    "links": [
-    {
-      "href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-      "rel": "self"
-    }
-    ],
-    "stack_status_reason": "Stack CREATE completed successfully",
-    "stack_name": "postman_stack",
-    "creation_time": "2015-02-03T20:07:39",
-    "updated_time": null,
-    "stack_status": "CREATE_COMPLETE",
-    "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-	"tags": ["rackspace", "atx"]
-  },
-  {
-    "description": "Simple template to test heat commands",
-    "links": [
-    {
-      "href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada",
-      "rel": "self"
-    }
-    ],
-    "stack_status_reason": "Stack successfully updated",
-    "stack_name": "gophercloud-test-stack-2",
-    "creation_time": "2014-12-11T17:39:16",
-    "updated_time": "2014-12-11T17:40:37",
-    "stack_status": "UPDATE_COMPLETE",
-    "id": "db6977b2-27aa-4775-9ae7-6213212d4ada",
-	"tags": ["sfo", "satx"]
-  }
-  ]
-}
-`
-
-// HandleListSuccessfully creates an HTTP handler at `/stacks` on the test handler mux
-// that responds with a `List` response.
-func HandleListSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, output)
-		case "db6977b2-27aa-4775-9ae7-6213212d4ada":
-			fmt.Fprintf(w, `[]`)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-}
-
-// GetExpected represents the expected object from a Get request.
-var GetExpected = &RetrievedStack{
-	DisableRollback: true,
-	Description:     "Simple template to test heat commands",
-	Parameters: map[string]string{
-		"flavor":         "m1.tiny",
-		"OS::stack_name": "postman_stack",
-		"OS::stack_id":   "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-	},
-	StatusReason: "Stack CREATE completed successfully",
-	Name:         "postman_stack",
-	Outputs:      []map[string]interface{}{},
-	CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
-	Links: []gophercloud.Link{
-		gophercloud.Link{
-			Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-			Rel:  "self",
-		},
-	},
-	Capabilities:        []interface{}{},
-	NotificationTopics:  []interface{}{},
-	Status:              "CREATE_COMPLETE",
-	ID:                  "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-	TemplateDescription: "Simple template to test heat commands",
-	Tags:                []string{"rackspace", "atx"},
-}
-
-// GetOutput represents the response body from a Get request.
-const GetOutput = `
-{
-  "stack": {
-    "disable_rollback": true,
-    "description": "Simple template to test heat commands",
-    "parameters": {
-      "flavor": "m1.tiny",
-      "OS::stack_name": "postman_stack",
-      "OS::stack_id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87"
-    },
-    "stack_status_reason": "Stack CREATE completed successfully",
-    "stack_name": "postman_stack",
-    "outputs": [],
-    "creation_time": "2015-02-03T20:07:39",
-    "links": [
-    {
-      "href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-      "rel": "self"
-    }
-    ],
-    "capabilities": [],
-    "notification_topics": [],
-    "timeout_mins": null,
-    "stack_status": "CREATE_COMPLETE",
-    "updated_time": null,
-    "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-    "template_description": "Simple template to test heat commands",
-	"tags": ["rackspace", "atx"]
-  }
-}
-`
-
-// HandleGetSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87`
-// on the test handler mux that responds with a `Get` response.
-func HandleGetSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
-
-// HandleUpdateSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87`
-// on the test handler mux that responds with an `Update` response.
-func HandleUpdateSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-// HandleDeleteSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87`
-// on the test handler mux that responds with a `Delete` response.
-func HandleDeleteSuccessfully(t *testing.T) {
-	th.Mux.HandleFunc("/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// GetExpected represents the expected object from a Get request.
-var PreviewExpected = &PreviewedStack{
-	DisableRollback: true,
-	Description:     "Simple template to test heat commands",
-	Parameters: map[string]string{
-		"flavor":         "m1.tiny",
-		"OS::stack_name": "postman_stack",
-		"OS::stack_id":   "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-	},
-	Name:         "postman_stack",
-	CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
-	Links: []gophercloud.Link{
-		gophercloud.Link{
-			Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-			Rel:  "self",
-		},
-	},
-	Capabilities:        []interface{}{},
-	NotificationTopics:  []interface{}{},
-	ID:                  "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-	TemplateDescription: "Simple template to test heat commands",
-}
-
-// HandlePreviewSuccessfully creates an HTTP handler at `/stacks/preview`
-// on the test handler mux that responds with a `Preview` response.
-func HandlePreviewSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/preview", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
-
-// AbandonExpected represents the expected object from an Abandon request.
-var AbandonExpected = &AbandonedStack{
-	Status: "COMPLETE",
-	Name:   "postman_stack",
-	Template: map[string]interface{}{
-		"heat_template_version": "2013-05-23",
-		"description":           "Simple template to test heat commands",
-		"parameters": map[string]interface{}{
-			"flavor": map[string]interface{}{
-				"default": "m1.tiny",
-				"type":    "string",
-			},
-		},
-		"resources": map[string]interface{}{
-			"hello_world": map[string]interface{}{
-				"type": "OS::Nova::Server",
-				"properties": map[string]interface{}{
-					"key_name": "heat_key",
-					"flavor": map[string]interface{}{
-						"get_param": "flavor",
-					},
-					"image":     "ad091b52-742f-469e-8f3c-fd81cadf0743",
-					"user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n",
-				},
-			},
-		},
-	},
-	Action: "CREATE",
-	ID:     "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-	Resources: map[string]interface{}{
-		"hello_world": map[string]interface{}{
-			"status":      "COMPLETE",
-			"name":        "hello_world",
-			"resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63",
-			"action":      "CREATE",
-			"type":        "OS::Nova::Server",
-		},
-	},
-	Files: map[string]string{
-		"file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "heat_template_version: 2014-10-16\nparameters:\n  flavor:\n    type: string\n    description: Flavor for the server to be created\n    default: 4353\n    hidden: true\nresources:\n  test_server:\n    type: \"OS::Nova::Server\"\n    properties:\n      name: test-server\n      flavor: 2 GB General Purpose v1\n image: Debian 7 (Wheezy) (PVHVM)\n",
-	},
-	StackUserProjectID: "897686",
-	ProjectID:          "897686",
-	Environment: map[string]interface{}{
-		"encrypted_param_names": make([]map[string]interface{}, 0),
-		"parameter_defaults":    make(map[string]interface{}),
-		"parameters":            make(map[string]interface{}),
-		"resource_registry": map[string]interface{}{
-			"file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml",
-			"resources": make(map[string]interface{}),
-		},
-	},
-}
-
-// AbandonOutput represents the response body from an Abandon request.
-const AbandonOutput = `
-{
-  "status": "COMPLETE",
-  "name": "postman_stack",
-  "template": {
-    "heat_template_version": "2013-05-23",
-    "description": "Simple template to test heat commands",
-    "parameters": {
-      "flavor": {
-        "default": "m1.tiny",
-        "type": "string"
-      }
-    },
-    "resources": {
-      "hello_world": {
-        "type": "OS::Nova::Server",
-        "properties": {
-          "key_name": "heat_key",
-          "flavor": {
-            "get_param": "flavor"
-          },
-          "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-          "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-        }
-      }
-    }
-  },
-  "action": "CREATE",
-  "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-  "resources": {
-    "hello_world": {
-      "status": "COMPLETE",
-      "name": "hello_world",
-      "resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63",
-      "action": "CREATE",
-      "type": "OS::Nova::Server"
-    }
-  },
-  "files": {
-    "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "heat_template_version: 2014-10-16\nparameters:\n  flavor:\n    type: string\n    description: Flavor for the server to be created\n    default: 4353\n    hidden: true\nresources:\n  test_server:\n    type: \"OS::Nova::Server\"\n    properties:\n      name: test-server\n      flavor: 2 GB General Purpose v1\n image: Debian 7 (Wheezy) (PVHVM)\n"
-},
-  "environment": {
-	"encrypted_param_names": [],
-	"parameter_defaults": {},
-	"parameters": {},
-	"resource_registry": {
-		"file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml",
-		"resources": {}
-	}
-  },
-  "stack_user_project_id": "897686",
-  "project_id": "897686"
-}`
-
-// HandleAbandonSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/abandon`
-// on the test handler mux that responds with an `Abandon` response.
-func HandleAbandonSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c8/abandon", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
-
 // ValidJSONTemplate is a valid OpenStack Heat template in JSON format
 const ValidJSONTemplate = `
 {
@@ -432,29 +25,6 @@
 }
 `
 
-// ValidJSONTemplateParsed is the expected parsed version of ValidJSONTemplate
-var ValidJSONTemplateParsed = map[string]interface{}{
-	"heat_template_version": "2014-10-16",
-	"parameters": map[string]interface{}{
-		"flavor": map[string]interface{}{
-			"default":     4353,
-			"description": "Flavor for the server to be created",
-			"hidden":      true,
-			"type":        "string",
-		},
-	},
-	"resources": map[string]interface{}{
-		"test_server": map[string]interface{}{
-			"properties": map[string]interface{}{
-				"flavor": "2 GB General Purpose v1",
-				"image":  "Debian 7 (Wheezy) (PVHVM)",
-				"name":   "test-server",
-			},
-			"type": "OS::Nova::Server",
-		},
-	},
-}
-
 // ValidYAMLTemplate is a valid OpenStack Heat template in YAML format
 const ValidYAMLTemplate = `
 heat_template_version: 2014-10-16
@@ -493,39 +63,84 @@
 // ValidJSONEnvironment is a valid environment for a stack in JSON format
 const ValidJSONEnvironment = `
 {
-  "parameters": {
-    "user_key": "userkey"
-  },
-  "resource_registry": {
-    "My::WP::Server": "file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml",
-    "OS::Quantum*": "OS::Neutron*",
-    "AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml",
-    "OS::Metering::Alarm": "OS::Ceilometer::Alarm",
-    "AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml",
-    "resources": {
-      "my_db_server": {
-        "OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml"
-      },
-      "my_server": {
-        "OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml",
-        "hooks": "pre-create"
-      },
-      "nested_stack": {
-        "nested_resource": {
-          "hooks": "pre-update"
-        },
-        "another_resource": {
-          "hooks": [
-            "pre-create",
-            "pre-update"
-          ]
-        }
-      }
-    }
-  }
+	"parameters": {
+		"user_key": "userkey"
+	},
+	"resource_registry": {
+		"My::WP::Server": "file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml",
+		"OS::Quantum*": "OS::Neutron*",
+		"AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml",
+		"OS::Metering::Alarm": "OS::Ceilometer::Alarm",
+		"AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml",
+		"resources": {
+			"my_db_server": {
+				"OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml"
+			},
+			"my_server": {
+				"OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml",
+				"hooks": "pre-create"
+			},
+			"nested_stack": {
+				"nested_resource": {
+					"hooks": "pre-update"
+				},
+				"another_resource": {
+					"hooks": [
+						"pre-create",
+						"pre-update"
+					]
+				}
+			}
+		}
+	}
 }
 `
 
+// ValidYAMLEnvironment is a valid environment for a stack in YAML format
+const ValidYAMLEnvironment = `
+parameters:
+  user_key: userkey
+resource_registry:
+  My::WP::Server: file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml
+  # allow older templates with Quantum in them.
+  "OS::Quantum*": "OS::Neutron*"
+  # Choose your implementation of AWS::CloudWatch::Alarm
+  "AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml"
+  #"AWS::CloudWatch::Alarm": "OS::Heat::CWLiteAlarm"
+  "OS::Metering::Alarm": "OS::Ceilometer::Alarm"
+  "AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml"
+  resources:
+    my_db_server:
+      "OS::DBInstance": file:///home/mine/all_my_cool_templates/db.yaml
+    my_server:
+      "OS::DBInstance": file:///home/mine/all_my_cool_templates/db.yaml
+      hooks: pre-create
+    nested_stack:
+      nested_resource:
+        hooks: pre-update
+      another_resource:
+        hooks: [pre-create, pre-update]
+`
+
+// InvalidEnvironment is an invalid environment as it has an extra section called `resources`
+const InvalidEnvironment = `
+parameters:
+	flavor:
+		type: string
+		description: Flavor for the server to be created
+		default: 4353
+		hidden: true
+resources:
+	test_server:
+		type: "OS::Nova::Server"
+		properties:
+			name: test-server
+			flavor: 2 GB General Purpose v1
+			image: Debian 7 (Wheezy) (PVHVM)
+parameter_defaults:
+	KeyName: heat_key
+`
+
 // ValidJSONEnvironmentParsed is the expected parsed version of ValidJSONEnvironment
 var ValidJSONEnvironmentParsed = map[string]interface{}{
 	"parameters": map[string]interface{}{
@@ -560,47 +175,25 @@
 	},
 }
 
-// ValidYAMLEnvironment is a valid environment for a stack in YAML format
-const ValidYAMLEnvironment = `
-parameters:
-  user_key: userkey
-resource_registry:
-  My::WP::Server: file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml
-  # allow older templates with Quantum in them.
-  "OS::Quantum*": "OS::Neutron*"
-  # Choose your implementation of AWS::CloudWatch::Alarm
-  "AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml"
-  #"AWS::CloudWatch::Alarm": "OS::Heat::CWLiteAlarm"
-  "OS::Metering::Alarm": "OS::Ceilometer::Alarm"
-  "AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml"
-  resources:
-    my_db_server:
-      "OS::DBInstance": file:///home/mine/all_my_cool_templates/db.yaml
-    my_server:
-      "OS::DBInstance": file:///home/mine/all_my_cool_templates/db.yaml
-      hooks: pre-create
-    nested_stack:
-      nested_resource:
-        hooks: pre-update
-      another_resource:
-        hooks: [pre-create, pre-update]
-`
-
-// InvalidEnvironment is an invalid environment as it has an extra section called `resources`
-const InvalidEnvironment = `
-parameters:
-  flavor:
-    type: string
-    description: Flavor for the server to be created
-    default: 4353
-    hidden: true
-resources:
-  test_server:
-    type: "OS::Nova::Server"
-    properties:
-      name: test-server
-      flavor: 2 GB General Purpose v1
-      image: Debian 7 (Wheezy) (PVHVM)
-parameter_defaults:
-  KeyName: heat_key
-`
+// ValidJSONTemplateParsed is the expected parsed version of ValidJSONTemplate
+var ValidJSONTemplateParsed = map[string]interface{}{
+	"heat_template_version": "2014-10-16",
+	"parameters": map[string]interface{}{
+		"flavor": map[string]interface{}{
+			"default":     4353,
+			"description": "Flavor for the server to be created",
+			"hidden":      true,
+			"type":        "string",
+		},
+	},
+	"resources": map[string]interface{}{
+		"test_server": map[string]interface{}{
+			"properties": map[string]interface{}{
+				"flavor": "2 GB General Purpose v1",
+				"image":  "Debian 7 (Wheezy) (PVHVM)",
+				"name":   "test-server",
+			},
+			"type": "OS::Nova::Server",
+		},
+	},
+}
diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go
index 1fc484d..91f38ee 100644
--- a/openstack/orchestration/v1/stacks/requests.go
+++ b/openstack/orchestration/v1/stacks/requests.go
@@ -1,23 +1,10 @@
 package stacks
 
 import (
-	"errors"
 	"strings"
 
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Rollback is used to specify whether or not a stack can be rolled back.
-type Rollback *bool
-
-var (
-	disable = true
-	// Disable is used to specify that a stack cannot be rolled back.
-	Disable Rollback = &disable
-	enable           = false
-	// Enable is used to specify that a stack can be rolled back.
-	Enable Rollback = &enable
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // CreateOptsBuilder is the interface options structs have to satisfy in order
@@ -31,87 +18,45 @@
 // CreateOpts is the common options struct used in this package's Create
 // operation.
 type CreateOpts struct {
-	// (REQUIRED) The name of the stack. It must start with an alphabetic character.
-	Name string
-	// (REQUIRED) A structure that contains either the template file or url. Call the
+	// The name of the stack. It must start with an alphabetic character.
+	Name string `json:"stack_name" required:"true"`
+	// A structure that contains either the template file or url. Call the
 	// associated methods to extract the information relevant to send in a create request.
-	TemplateOpts *Template
-	// (DEPRECATED): Please use TemplateOpts for providing the template. If
-	// TemplateOpts is provided, TemplateURL will be ignored
-	// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
-	// This value is ignored if Template is supplied inline.
-	TemplateURL string
-	// (DEPRECATED): Please use TemplateOpts for providing the template. If
-	// TemplateOpts is provided, Template will be ignored
-	// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
-	// is a stringified version of the JSON/YAML template. Since the template will likely
-	// be located in a file, one way to set this variable is by using ioutil.ReadFile:
-	// import "io/ioutil"
-	// var opts stacks.CreateOpts
-	// b, err := ioutil.ReadFile("path/to/you/template/file.json")
-	// if err != nil {
-	//   // handle error...
-	// }
-	// opts.Template = string(b)
-	Template string
-	// (OPTIONAL) Enables or disables deletion of all stack resources when a stack
+	TemplateOpts *Template `json:"-" required:"true"`
+	// Enables or disables deletion of all stack resources when a stack
 	// creation fails. Default is true, meaning all resources are not deleted when
 	// stack creation fails.
-	DisableRollback Rollback
-	// (OPTIONAL) A structure that contains details for the environment of the stack.
-	EnvironmentOpts *Environment
-	// (DEPRECATED): Please use EnvironmentOpts to provide Environment data
-	// (OPTIONAL) A stringified JSON environment for the stack.
-	Environment string
-	// (DEPRECATED): Files is automatically determined
-	// by parsing the template and environment passed as TemplateOpts and
-	// EnvironmentOpts respectively.
-	// (OPTIONAL) A map that maps file names to file contents. It can also be used
-	// to pass provider template contents. Example:
-	// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
-	Files map[string]interface{}
-	// (OPTIONAL) User-defined parameters to pass to the template.
-	Parameters map[string]string
-	// (OPTIONAL) The timeout for stack creation in minutes.
-	Timeout int
-	// (OPTIONAL) A list of tags to assosciate with the Stack
-	Tags []string
+	DisableRollback *bool `json:"disable_rollback,omitempty"`
+	// A structure that contains details for the environment of the stack.
+	EnvironmentOpts *Environment `json:"-"`
+	// User-defined parameters to pass to the template.
+	Parameters map[string]string `json:"parameters,omitempty"`
+	// The timeout for stack creation in minutes.
+	Timeout int `json:"timeout_mins,omitempty"`
+	// A list of tags to assosciate with the Stack
+	Tags []string `json:"-"`
 }
 
 // ToStackCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToStackCreateMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-
-	if opts.Name == "" {
-		return s, errors.New("Required field 'Name' not provided.")
+	b, err := gophercloud.BuildRequestBody(opts, "")
+	if err != nil {
+		return nil, err
 	}
-	s["stack_name"] = opts.Name
-	Files := make(map[string]string)
-	if opts.TemplateOpts == nil {
-		if opts.Template != "" {
-			s["template"] = opts.Template
-		} else if opts.TemplateURL != "" {
-			s["template_url"] = opts.TemplateURL
-		} else {
-			return s, errors.New("Either Template or TemplateURL must be provided.")
-		}
-	} else {
-		if err := opts.TemplateOpts.Parse(); err != nil {
-			return nil, err
-		}
 
-		if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
-			return nil, err
-		}
-		opts.TemplateOpts.fixFileRefs()
-		s["template"] = string(opts.TemplateOpts.Bin)
-
-		for k, v := range opts.TemplateOpts.Files {
-			Files[k] = v
-		}
+	if err := opts.TemplateOpts.Parse(); err != nil {
+		return nil, err
 	}
-	if opts.DisableRollback != nil {
-		s["disable_rollback"] = &opts.DisableRollback
+
+	if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
+		return nil, err
+	}
+	opts.TemplateOpts.fixFileRefs()
+	b["template"] = string(opts.TemplateOpts.Bin)
+
+	files := make(map[string]string)
+	for k, v := range opts.TemplateOpts.Files {
+		files[k] = v
 	}
 
 	if opts.EnvironmentOpts != nil {
@@ -123,50 +68,32 @@
 		}
 		opts.EnvironmentOpts.fixFileRefs()
 		for k, v := range opts.EnvironmentOpts.Files {
-			Files[k] = v
+			files[k] = v
 		}
-		s["environment"] = string(opts.EnvironmentOpts.Bin)
-	} else if opts.Environment != "" {
-		s["environment"] = opts.Environment
+		b["environment"] = string(opts.EnvironmentOpts.Bin)
 	}
 
-	if opts.Files != nil {
-		s["files"] = opts.Files
-	} else {
-		s["files"] = Files
-	}
-
-	if opts.DisableRollback != nil {
-		s["disable_rollback"] = &opts.DisableRollback
-	}
-
-	if opts.Parameters != nil {
-		s["parameters"] = opts.Parameters
-	}
-
-	if opts.Timeout != 0 {
-		s["timeout_mins"] = opts.Timeout
+	if len(files) > 0 {
+		b["files"] = files
 	}
 
 	if opts.Tags != nil {
-		s["tags"] = strings.Join(opts.Tags, ",")
+		b["tags"] = strings.Join(opts.Tags, ",")
 	}
-	return s, nil
+
+	return b, nil
 }
 
 // Create accepts a CreateOpts struct and creates a new stack using the values
 // provided.
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToStackCreateMap()
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToStackCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(createURL(c), b, &r.Body, nil)
+	return
 }
 
 // AdoptOptsBuilder is the interface options structs have to satisfy in order
@@ -180,91 +107,49 @@
 // AdoptOpts is the common options struct used in this package's Adopt
 // operation.
 type AdoptOpts struct {
-	// (REQUIRED) Existing resources data represented as a string to add to the
+	// Existing resources data represented as a string to add to the
 	// new stack. Data returned by Abandon could be provided as AdoptsStackData.
-	AdoptStackData string
-	// (REQUIRED) The name of the stack. It must start with an alphabetic character.
-	Name string
-	// (REQUIRED) The timeout for stack creation in minutes.
-	Timeout int
-	// (REQUIRED) A structure that contains either the template file or url. Call the
+	AdoptStackData string `json:"adopt_stack_data" required:"true"`
+	// The name of the stack. It must start with an alphabetic character.
+	Name string `json:"stack_name" required:"true"`
+	// A structure that contains either the template file or url. Call the
 	// associated methods to extract the information relevant to send in a create request.
-	TemplateOpts *Template
-	// (DEPRECATED): Please use TemplateOpts for providing the template. If
-	// TemplateOpts is provided, TemplateURL will be ignored
-	// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
-	// This value is ignored if Template is supplied inline.
-	TemplateURL string
-	// (DEPRECATED): Please use TemplateOpts for providing the template. If
-	// TemplateOpts is provided, Template will be ignored
-	// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
-	// is a stringified version of the JSON/YAML template. Since the template will likely
-	// be located in a file, one way to set this variable is by using ioutil.ReadFile:
-	// import "io/ioutil"
-	// var opts stacks.CreateOpts
-	// b, err := ioutil.ReadFile("path/to/you/template/file.json")
-	// if err != nil {
-	//   // handle error...
-	// }
-	// opts.Template = string(b)
-	Template string
-	// (OPTIONAL) Enables or disables deletion of all stack resources when a stack
+	TemplateOpts *Template `json:"-" required:"true"`
+	// The timeout for stack creation in minutes.
+	Timeout int `json:"timeout_mins,omitempty"`
+	// A structure that contains either the template file or url. Call the
+	// associated methods to extract the information relevant to send in a create request.
+	//TemplateOpts *Template `json:"-" required:"true"`
+	// Enables or disables deletion of all stack resources when a stack
 	// creation fails. Default is true, meaning all resources are not deleted when
 	// stack creation fails.
-	DisableRollback Rollback
-	// (OPTIONAL) A structure that contains details for the environment of the stack.
-	EnvironmentOpts *Environment
-	// (DEPRECATED): Please use EnvironmentOpts to provide Environment data
-	// (OPTIONAL) A stringified JSON environment for the stack.
-	Environment string
-	// (DEPRECATED): Files is automatically determined
-	// by parsing the template and environment passed as TemplateOpts and
-	// EnvironmentOpts respectively.
-	// (OPTIONAL) A map that maps file names to file contents. It can also be used
-	// to pass provider template contents. Example:
-	// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
-	Files map[string]interface{}
-	// (OPTIONAL) User-defined parameters to pass to the template.
-	Parameters map[string]string
+	DisableRollback *bool `json:"disable_rollback,omitempty"`
+	// A structure that contains details for the environment of the stack.
+	EnvironmentOpts *Environment `json:"-"`
+	// User-defined parameters to pass to the template.
+	Parameters map[string]string `json:"parameters,omitempty"`
 }
 
 // ToStackAdoptMap casts a CreateOpts struct to a map.
 func (opts AdoptOpts) ToStackAdoptMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-
-	if opts.Name == "" {
-		return s, errors.New("Required field 'Name' not provided.")
-	}
-	s["stack_name"] = opts.Name
-	Files := make(map[string]string)
-	if opts.AdoptStackData != "" {
-		s["adopt_stack_data"] = opts.AdoptStackData
-	} else if opts.TemplateOpts == nil {
-		if opts.Template != "" {
-			s["template"] = opts.Template
-		} else if opts.TemplateURL != "" {
-			s["template_url"] = opts.TemplateURL
-		} else {
-			return s, errors.New("One of AdoptStackData, Template, TemplateURL or TemplateOpts must be provided.")
-		}
-	} else {
-		if err := opts.TemplateOpts.Parse(); err != nil {
-			return nil, err
-		}
-
-		if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
-			return nil, err
-		}
-		opts.TemplateOpts.fixFileRefs()
-		s["template"] = string(opts.TemplateOpts.Bin)
-
-		for k, v := range opts.TemplateOpts.Files {
-			Files[k] = v
-		}
+	b, err := gophercloud.BuildRequestBody(opts, "")
+	if err != nil {
+		return nil, err
 	}
 
-	if opts.DisableRollback != nil {
-		s["disable_rollback"] = &opts.DisableRollback
+	if err := opts.TemplateOpts.Parse(); err != nil {
+		return nil, err
+	}
+
+	if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
+		return nil, err
+	}
+	opts.TemplateOpts.fixFileRefs()
+	b["template"] = string(opts.TemplateOpts.Bin)
+
+	files := make(map[string]string)
+	for k, v := range opts.TemplateOpts.Files {
+		files[k] = v
 	}
 
 	if opts.EnvironmentOpts != nil {
@@ -276,44 +161,28 @@
 		}
 		opts.EnvironmentOpts.fixFileRefs()
 		for k, v := range opts.EnvironmentOpts.Files {
-			Files[k] = v
+			files[k] = v
 		}
-		s["environment"] = string(opts.EnvironmentOpts.Bin)
-	} else if opts.Environment != "" {
-		s["environment"] = opts.Environment
+		b["environment"] = string(opts.EnvironmentOpts.Bin)
 	}
 
-	if opts.Files != nil {
-		s["files"] = opts.Files
-	} else {
-		s["files"] = Files
+	if len(files) > 0 {
+		b["files"] = files
 	}
 
-	if opts.Parameters != nil {
-		s["parameters"] = opts.Parameters
-	}
-
-	if opts.Timeout != 0 {
-		s["timeout"] = opts.Timeout
-	}
-	s["timeout_mins"] = opts.Timeout
-
-	return s, nil
+	return b, nil
 }
 
 // Adopt accepts an AdoptOpts struct and creates a new stack using the resources
 // from another stack.
-func Adopt(c *gophercloud.ServiceClient, opts AdoptOptsBuilder) AdoptResult {
-	var res AdoptResult
-
-	reqBody, err := opts.ToStackAdoptMap()
+func Adopt(c *gophercloud.ServiceClient, opts AdoptOptsBuilder) (r AdoptResult) {
+	b, err := opts.ToStackAdoptMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(adoptURL(c), reqBody, &res.Body, nil)
-	return res
+	_, r.Err = c.Post(adoptURL(c), b, &r.Body, nil)
+	return
 }
 
 // SortDir is a type for specifying in which direction to sort a list of stacks.
@@ -378,7 +247,6 @@
 		}
 		url += query
 	}
-
 	createPage := func(r pagination.PageResult) pagination.Page {
 		return StackPage{pagination.SinglePageBase(r)}
 	}
@@ -386,10 +254,9 @@
 }
 
 // Get retreives a stack based on the stack name and stack ID.
-func Get(c *gophercloud.ServiceClient, stackName, stackID string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, stackName, stackID), &res.Body, nil)
-	return res
+func Get(c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) {
+	_, r.Err = c.Get(getURL(c, stackName, stackID), &r.Body, nil)
+	return
 }
 
 // UpdateOptsBuilder is the interface options structs have to satisfy in order
@@ -401,73 +268,39 @@
 // UpdateOpts contains the common options struct used in this package's Update
 // operation.
 type UpdateOpts struct {
-	// (REQUIRED) A structure that contains either the template file or url. Call the
+	// A structure that contains either the template file or url. Call the
 	// associated methods to extract the information relevant to send in a create request.
-	TemplateOpts *Template
-	// (DEPRECATED): Please use TemplateOpts for providing the template. If
-	// TemplateOpts is provided, TemplateURL will be ignored
-	// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
-	// This value is ignored if Template is supplied inline.
-	TemplateURL string
-	// (DEPRECATED): Please use TemplateOpts for providing the template. If
-	// TemplateOpts is provided, Template will be ignored
-	// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
-	// is a stringified version of the JSON/YAML template. Since the template will likely
-	// be located in a file, one way to set this variable is by using ioutil.ReadFile:
-	// import "io/ioutil"
-	// var opts stacks.CreateOpts
-	// b, err := ioutil.ReadFile("path/to/you/template/file.json")
-	// if err != nil {
-	//   // handle error...
-	// }
-	// opts.Template = string(b)
-	Template string
-	// (OPTIONAL) A structure that contains details for the environment of the stack.
-	EnvironmentOpts *Environment
-	// (DEPRECATED): Please use EnvironmentOpts to provide Environment data
-	// (OPTIONAL) A stringified JSON environment for the stack.
-	Environment string
-	// (DEPRECATED): Files is automatically determined
-	// by parsing the template and environment passed as TemplateOpts and
-	// EnvironmentOpts respectively.
-	// (OPTIONAL) A map that maps file names to file contents. It can also be used
-	// to pass provider template contents. Example:
-	// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
-	Files map[string]interface{}
-	// (OPTIONAL) User-defined parameters to pass to the template.
-	Parameters map[string]string
-	// (OPTIONAL) The timeout for stack creation in minutes.
-	Timeout int
-	// (OPTIONAL) A list of tags to assosciate with the Stack
-	Tags []string
+	TemplateOpts *Template `json:"-" required:"true"`
+	// A structure that contains details for the environment of the stack.
+	EnvironmentOpts *Environment `json:"-"`
+	// User-defined parameters to pass to the template.
+	Parameters map[string]string `json:"parameters,omitempty"`
+	// The timeout for stack creation in minutes.
+	Timeout int `json:"timeout_mins,omitempty"`
+	// A list of tags to assosciate with the Stack
+	Tags []string `json:"-"`
 }
 
 // ToStackUpdateMap casts a CreateOpts struct to a map.
 func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-	Files := make(map[string]string)
-	if opts.TemplateOpts == nil {
-		if opts.Template != "" {
-			s["template"] = opts.Template
-		} else if opts.TemplateURL != "" {
-			s["template_url"] = opts.TemplateURL
-		} else {
-			return s, errors.New("Either Template or TemplateURL must be provided.")
-		}
-	} else {
-		if err := opts.TemplateOpts.Parse(); err != nil {
-			return nil, err
-		}
+	b, err := gophercloud.BuildRequestBody(opts, "")
+	if err != nil {
+		return nil, err
+	}
 
-		if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
-			return nil, err
-		}
-		opts.TemplateOpts.fixFileRefs()
-		s["template"] = string(opts.TemplateOpts.Bin)
+	if err := opts.TemplateOpts.Parse(); err != nil {
+		return nil, err
+	}
 
-		for k, v := range opts.TemplateOpts.Files {
-			Files[k] = v
-		}
+	if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
+		return nil, err
+	}
+	opts.TemplateOpts.fixFileRefs()
+	b["template"] = string(opts.TemplateOpts.Bin)
+
+	files := make(map[string]string)
+	for k, v := range opts.TemplateOpts.Files {
+		files[k] = v
 	}
 
 	if opts.EnvironmentOpts != nil {
@@ -479,54 +312,38 @@
 		}
 		opts.EnvironmentOpts.fixFileRefs()
 		for k, v := range opts.EnvironmentOpts.Files {
-			Files[k] = v
+			files[k] = v
 		}
-		s["environment"] = string(opts.EnvironmentOpts.Bin)
-	} else if opts.Environment != "" {
-		s["environment"] = opts.Environment
+		b["environment"] = string(opts.EnvironmentOpts.Bin)
 	}
 
-	if opts.Files != nil {
-		s["files"] = opts.Files
-	} else {
-		s["files"] = Files
-	}
-
-	if opts.Parameters != nil {
-		s["parameters"] = opts.Parameters
-	}
-
-	if opts.Timeout != 0 {
-		s["timeout_mins"] = opts.Timeout
+	if len(files) > 0 {
+		b["files"] = files
 	}
 
 	if opts.Tags != nil {
-		s["tags"] = strings.Join(opts.Tags, ",")
+		b["tags"] = strings.Join(opts.Tags, ",")
 	}
 
-	return s, nil
+	return b, nil
 }
 
 // Update accepts an UpdateOpts struct and updates an existing stack using the values
 // provided.
-func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToStackUpdateMap()
+func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdateOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToStackUpdateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Put(updateURL(c, stackName, stackID), reqBody, nil, nil)
-	return res
+	_, r.Err = c.Put(updateURL(c, stackName, stackID), b, nil, nil)
+	return
 }
 
 // Delete deletes a stack based on the stack name and stack ID.
-func Delete(c *gophercloud.ServiceClient, stackName, stackID string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(deleteURL(c, stackName, stackID), nil)
-	return res
+func Delete(c *gophercloud.ServiceClient, stackName, stackID string) (r DeleteResult) {
+	_, r.Err = c.Delete(deleteURL(c, stackName, stackID), nil)
+	return
 }
 
 // PreviewOptsBuilder is the interface options structs have to satisfy in order
@@ -538,85 +355,43 @@
 // PreviewOpts contains the common options struct used in this package's Preview
 // operation.
 type PreviewOpts struct {
-	// (REQUIRED) The name of the stack. It must start with an alphabetic character.
-	Name string
-	// (REQUIRED) The timeout for stack creation in minutes.
-	Timeout int
-	// (REQUIRED) A structure that contains either the template file or url. Call the
+	// The name of the stack. It must start with an alphabetic character.
+	Name string `json:"stack_name" required:"true"`
+	// The timeout for stack creation in minutes.
+	Timeout int `json:"timeout_mins" required:"true"`
+	// A structure that contains either the template file or url. Call the
 	// associated methods to extract the information relevant to send in a create request.
-	TemplateOpts *Template
-	// (DEPRECATED): Please use TemplateOpts for providing the template. If
-	// TemplateOpts is provided, TemplateURL will be ignored
-	// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
-	// This value is ignored if Template is supplied inline.
-	TemplateURL string
-	// (DEPRECATED): Please use TemplateOpts for providing the template. If
-	// TemplateOpts is provided, Template will be ignored
-	// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
-	// is a stringified version of the JSON/YAML template. Since the template will likely
-	// be located in a file, one way to set this variable is by using ioutil.ReadFile:
-	// import "io/ioutil"
-	// var opts stacks.CreateOpts
-	// b, err := ioutil.ReadFile("path/to/you/template/file.json")
-	// if err != nil {
-	//   // handle error...
-	// }
-	// opts.Template = string(b)
-	Template string
-	// (OPTIONAL) Enables or disables deletion of all stack resources when a stack
+	TemplateOpts *Template `json:"-" required:"true"`
+	// Enables or disables deletion of all stack resources when a stack
 	// creation fails. Default is true, meaning all resources are not deleted when
 	// stack creation fails.
-	DisableRollback Rollback
-	// (OPTIONAL) A structure that contains details for the environment of the stack.
-	EnvironmentOpts *Environment
-	// (DEPRECATED): Please use EnvironmentOpts to provide Environment data
-	// (OPTIONAL) A stringified JSON environment for the stack.
-	Environment string
-	// (DEPRECATED): Files is automatically determined
-	// by parsing the template and environment passed as TemplateOpts and
-	// EnvironmentOpts respectively.
-	// (OPTIONAL) A map that maps file names to file contents. It can also be used
-	// to pass provider template contents. Example:
-	// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
-	Files map[string]interface{}
-	// (OPTIONAL) User-defined parameters to pass to the template.
-	Parameters map[string]string
+	DisableRollback *bool `json:"disable_rollback,omitempty"`
+	// A structure that contains details for the environment of the stack.
+	EnvironmentOpts *Environment `json:"-"`
+	// User-defined parameters to pass to the template.
+	Parameters map[string]string `json:"parameters,omitempty"`
 }
 
 // ToStackPreviewMap casts a PreviewOpts struct to a map.
 func (opts PreviewOpts) ToStackPreviewMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-
-	if opts.Name == "" {
-		return s, errors.New("Required field 'Name' not provided.")
+	b, err := gophercloud.BuildRequestBody(opts, "")
+	if err != nil {
+		return nil, err
 	}
-	s["stack_name"] = opts.Name
-	Files := make(map[string]string)
-	if opts.TemplateOpts == nil {
-		if opts.Template != "" {
-			s["template"] = opts.Template
-		} else if opts.TemplateURL != "" {
-			s["template_url"] = opts.TemplateURL
-		} else {
-			return s, errors.New("Either Template or TemplateURL must be provided.")
-		}
-	} else {
-		if err := opts.TemplateOpts.Parse(); err != nil {
-			return nil, err
-		}
 
-		if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
-			return nil, err
-		}
-		opts.TemplateOpts.fixFileRefs()
-		s["template"] = string(opts.TemplateOpts.Bin)
-
-		for k, v := range opts.TemplateOpts.Files {
-			Files[k] = v
-		}
+	if err := opts.TemplateOpts.Parse(); err != nil {
+		return nil, err
 	}
-	if opts.DisableRollback != nil {
-		s["disable_rollback"] = &opts.DisableRollback
+
+	if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
+		return nil, err
+	}
+	opts.TemplateOpts.fixFileRefs()
+	b["template"] = string(opts.TemplateOpts.Bin)
+
+	files := make(map[string]string)
+	for k, v := range opts.TemplateOpts.Files {
+		files[k] = v
 	}
 
 	if opts.EnvironmentOpts != nil {
@@ -628,55 +403,38 @@
 		}
 		opts.EnvironmentOpts.fixFileRefs()
 		for k, v := range opts.EnvironmentOpts.Files {
-			Files[k] = v
+			files[k] = v
 		}
-		s["environment"] = string(opts.EnvironmentOpts.Bin)
-	} else if opts.Environment != "" {
-		s["environment"] = opts.Environment
+		b["environment"] = string(opts.EnvironmentOpts.Bin)
 	}
 
-	if opts.Files != nil {
-		s["files"] = opts.Files
-	} else {
-		s["files"] = Files
+	if len(files) > 0 {
+		b["files"] = files
 	}
 
-	if opts.Parameters != nil {
-		s["parameters"] = opts.Parameters
-	}
-
-	if opts.Timeout != 0 {
-		s["timeout_mins"] = opts.Timeout
-	}
-
-	return s, nil
+	return b, nil
 }
 
 // Preview accepts a PreviewOptsBuilder interface and creates a preview of a stack using the values
 // provided.
-func Preview(c *gophercloud.ServiceClient, opts PreviewOptsBuilder) PreviewResult {
-	var res PreviewResult
-
-	reqBody, err := opts.ToStackPreviewMap()
+func Preview(c *gophercloud.ServiceClient, opts PreviewOptsBuilder) (r PreviewResult) {
+	b, err := opts.ToStackPreviewMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	// Send request to API
-	_, res.Err = c.Post(previewURL(c), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Post(previewURL(c), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
 
 // Abandon deletes the stack with the provided stackName and stackID, but leaves its
 // resources intact, and returns data describing the stack and its resources.
-func Abandon(c *gophercloud.ServiceClient, stackName, stackID string) AbandonResult {
-	var res AbandonResult
-	_, res.Err = c.Delete(abandonURL(c, stackName, stackID), &gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
+func Abandon(c *gophercloud.ServiceClient, stackName, stackID string) (r AbandonResult) {
+	_, r.Err = c.Delete(abandonURL(c, stackName, stackID), &gophercloud.RequestOpts{
+		JSONResponse: &r.Body,
 		OkCodes:      []int{200},
 	})
-	return res
+	return
 }
diff --git a/openstack/orchestration/v1/stacks/requests_test.go b/openstack/orchestration/v1/stacks/requests_test.go
deleted file mode 100644
index 0fde44b..0000000
--- a/openstack/orchestration/v1/stacks/requests_test.go
+++ /dev/null
@@ -1,358 +0,0 @@
-package stacks
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestCreateStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreateSuccessfully(t, CreateOutput)
-
-	createOpts := CreateOpts{
-		Name:    "stackcreated",
-		Timeout: 60,
-		Template: `
-    {
-      "stack_name": "postman_stack",
-      "template": {
-        "heat_template_version": "2013-05-23",
-        "description": "Simple template to test heat commands",
-        "parameters": {
-          "flavor": {
-            "default": "m1.tiny",
-            "type": "string"
-          }
-        },
-        "resources": {
-          "hello_world": {
-            "type":"OS::Nova::Server",
-            "properties": {
-              "key_name": "heat_key",
-              "flavor": {
-                "get_param": "flavor"
-              },
-              "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-              "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-            }
-          }
-        }
-      }
-    }`,
-		DisableRollback: Disable,
-	}
-	actual, err := Create(fake.ServiceClient(), createOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := CreateExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestCreateStackNewTemplateFormat(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreateSuccessfully(t, CreateOutput)
-	template := new(Template)
-	template.Bin = []byte(`
-		{
-			"heat_template_version": "2013-05-23",
-			"description": "Simple template to test heat commands",
-			"parameters": {
-				"flavor": {
-					"default": "m1.tiny",
-					"type": "string"
-				}
-			}
-		}`)
-	createOpts := CreateOpts{
-		Name:            "stackcreated",
-		Timeout:         60,
-		TemplateOpts:    template,
-		DisableRollback: Disable,
-	}
-	actual, err := Create(fake.ServiceClient(), createOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := CreateExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestAdoptStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreateSuccessfully(t, CreateOutput)
-
-	adoptOpts := AdoptOpts{
-		AdoptStackData: `{environment{parameters{}}}`,
-		Name:           "stackcreated",
-		Timeout:        60,
-		Template: `
-    {
-      "stack_name": "postman_stack",
-      "template": {
-        "heat_template_version": "2013-05-23",
-        "description": "Simple template to test heat commands",
-        "parameters": {
-          "flavor": {
-            "default": "m1.tiny",
-            "type": "string"
-          }
-        },
-        "resources": {
-          "hello_world": {
-            "type":"OS::Nova::Server",
-            "properties": {
-              "key_name": "heat_key",
-              "flavor": {
-                "get_param": "flavor"
-              },
-              "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-              "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-            }
-          }
-        }
-      }
-    }`,
-		DisableRollback: Disable,
-	}
-	actual, err := Adopt(fake.ServiceClient(), adoptOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := CreateExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestAdoptStackNewTemplateFormat(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleCreateSuccessfully(t, CreateOutput)
-	template := new(Template)
-	template.Bin = []byte(`
-{
-  "stack_name": "postman_stack",
-  "template": {
-	"heat_template_version": "2013-05-23",
-	"description": "Simple template to test heat commands",
-	"parameters": {
-	  "flavor": {
-		"default": "m1.tiny",
-		"type": "string"
-	  }
-	},
-	"resources": {
-	  "hello_world": {
-		"type":"OS::Nova::Server",
-		"properties": {
-		  "key_name": "heat_key",
-		  "flavor": {
-			"get_param": "flavor"
-		  },
-		  "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-		  "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-		}
-	  }
-	}
-  }
-}`)
-	adoptOpts := AdoptOpts{
-		AdoptStackData:  `{environment{parameters{}}}`,
-		Name:            "stackcreated",
-		Timeout:         60,
-		TemplateOpts:    template,
-		DisableRollback: Disable,
-	}
-	actual, err := Adopt(fake.ServiceClient(), adoptOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := CreateExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestListStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleListSuccessfully(t, FullListOutput)
-
-	count := 0
-	err := List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractStacks(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, ListExpected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestGetStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t, GetOutput)
-
-	actual, err := Get(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := GetExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestUpdateStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleUpdateSuccessfully(t)
-
-	updateOpts := UpdateOpts{
-		Template: `
-    {
-      "heat_template_version": "2013-05-23",
-      "description": "Simple template to test heat commands",
-      "parameters": {
-        "flavor": {
-          "default": "m1.tiny",
-          "type": "string"
-        }
-      },
-      "resources": {
-        "hello_world": {
-          "type":"OS::Nova::Server",
-          "properties": {
-            "key_name": "heat_key",
-            "flavor": {
-              "get_param": "flavor"
-            },
-            "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-            "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-          }
-        }
-      }
-    }`,
-	}
-	err := Update(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestUpdateStackNewTemplateFormat(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleUpdateSuccessfully(t)
-
-	template := new(Template)
-	template.Bin = []byte(`
-		{
-			"heat_template_version": "2013-05-23",
-			"description": "Simple template to test heat commands",
-			"parameters": {
-				"flavor": {
-					"default": "m1.tiny",
-					"type": "string"
-				}
-			}
-		}`)
-	updateOpts := UpdateOpts{
-		TemplateOpts: template,
-	}
-	err := Update(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDeleteStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDeleteSuccessfully(t)
-
-	err := Delete(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada").ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestPreviewStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePreviewSuccessfully(t, GetOutput)
-
-	previewOpts := PreviewOpts{
-		Name:    "stackcreated",
-		Timeout: 60,
-		Template: `
-    {
-      "stack_name": "postman_stack",
-      "template": {
-        "heat_template_version": "2013-05-23",
-        "description": "Simple template to test heat commands",
-        "parameters": {
-          "flavor": {
-            "default": "m1.tiny",
-            "type": "string"
-          }
-        },
-        "resources": {
-          "hello_world": {
-            "type":"OS::Nova::Server",
-            "properties": {
-              "key_name": "heat_key",
-              "flavor": {
-                "get_param": "flavor"
-              },
-              "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-              "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-            }
-          }
-        }
-      }
-    }`,
-		DisableRollback: Disable,
-	}
-	actual, err := Preview(fake.ServiceClient(), previewOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := PreviewExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestPreviewStackNewTemplateFormat(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePreviewSuccessfully(t, GetOutput)
-
-	template := new(Template)
-	template.Bin = []byte(`
-		{
-			"heat_template_version": "2013-05-23",
-			"description": "Simple template to test heat commands",
-			"parameters": {
-				"flavor": {
-					"default": "m1.tiny",
-					"type": "string"
-				}
-			}
-		}`)
-	previewOpts := PreviewOpts{
-		Name:            "stackcreated",
-		Timeout:         60,
-		TemplateOpts:    template,
-		DisableRollback: Disable,
-	}
-	actual, err := Preview(fake.ServiceClient(), previewOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := PreviewExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestAbandonStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleAbandonSuccessfully(t, AbandonOutput)
-
-	actual, err := Abandon(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c8").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := AbandonExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/openstack/orchestration/v1/stacks/results.go b/openstack/orchestration/v1/stacks/results.go
index 432bc8e..6b6f3a3 100644
--- a/openstack/orchestration/v1/stacks/results.go
+++ b/openstack/orchestration/v1/stacks/results.go
@@ -2,19 +2,15 @@
 
 import (
 	"encoding/json"
-	"fmt"
-	"reflect"
-	"time"
 
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
 )
 
 // CreatedStack represents the object extracted from a Create operation.
 type CreatedStack struct {
-	ID    string             `mapstructure:"id"`
-	Links []gophercloud.Link `mapstructure:"links"`
+	ID    string             `json:"id"`
+	Links []gophercloud.Link `json:"links"`
 }
 
 // CreateResult represents the result of a Create operation.
@@ -25,19 +21,11 @@
 // Extract returns a pointer to a CreatedStack object and is called after a
 // Create operation.
 func (r CreateResult) Extract() (*CreatedStack, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		CreatedStack *CreatedStack `json:"stack"`
 	}
-
-	var res struct {
-		Stack *CreatedStack `mapstructure:"stack"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return res.Stack, nil
+	err := r.ExtractInto(&s)
+	return s.CreatedStack, err
 }
 
 // AdoptResult represents the result of an Adopt operation. AdoptResult has the
@@ -54,90 +42,50 @@
 // IsEmpty returns true if a ListResult contains no Stacks.
 func (r StackPage) IsEmpty() (bool, error) {
 	stacks, err := ExtractStacks(r)
-	if err != nil {
-		return true, err
-	}
-	return len(stacks) == 0, nil
+	return len(stacks) == 0, err
 }
 
 // ListedStack represents an element in the slice extracted from a List operation.
 type ListedStack struct {
-	CreationTime time.Time          `mapstructure:"-"`
-	Description  string             `mapstructure:"description"`
-	ID           string             `mapstructure:"id"`
-	Links        []gophercloud.Link `mapstructure:"links"`
-	Name         string             `mapstructure:"stack_name"`
-	Status       string             `mapstructure:"stack_status"`
-	StatusReason string             `mapstructure:"stack_status_reason"`
-	Tags         []string           `mapstructure:"tags"`
-	UpdatedTime  time.Time          `mapstructure:"-"`
+	CreationTime gophercloud.JSONRFC3339NoZ `json:"creation_time"`
+	Description  string                     `json:"description"`
+	ID           string                     `json:"id"`
+	Links        []gophercloud.Link         `json:"links"`
+	Name         string                     `json:"stack_name"`
+	Status       string                     `json:"stack_status"`
+	StatusReason string                     `json:"stack_status_reason"`
+	Tags         []string                   `json:"tags"`
+	UpdatedTime  gophercloud.JSONRFC3339NoZ `json:"updated_time"`
 }
 
 // ExtractStacks extracts and returns a slice of ListedStack. It is used while iterating
 // over a stacks.List call.
-func ExtractStacks(page pagination.Page) ([]ListedStack, error) {
-	casted := page.(StackPage).Body
-
-	var res struct {
-		Stacks []ListedStack `mapstructure:"stacks"`
+func ExtractStacks(r pagination.Page) ([]ListedStack, error) {
+	var s struct {
+		ListedStacks []ListedStack `json:"stacks"`
 	}
-
-	err := mapstructure.Decode(casted, &res)
-	if err != nil {
-		return nil, err
-	}
-
-	var rawStacks []interface{}
-	switch casted.(type) {
-	case map[string]interface{}:
-		rawStacks = casted.(map[string]interface{})["stacks"].([]interface{})
-	case map[string][]interface{}:
-		rawStacks = casted.(map[string][]interface{})["stacks"]
-	default:
-		return res.Stacks, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i := range rawStacks {
-		thisStack := (rawStacks[i]).(map[string]interface{})
-
-		if t, ok := thisStack["creation_time"].(string); ok && t != "" {
-			creationTime, err := time.Parse(gophercloud.STACK_TIME_FMT, t)
-			if err != nil {
-				return res.Stacks, err
-			}
-			res.Stacks[i].CreationTime = creationTime
-		}
-
-		if t, ok := thisStack["updated_time"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(gophercloud.STACK_TIME_FMT, t)
-			if err != nil {
-				return res.Stacks, err
-			}
-			res.Stacks[i].UpdatedTime = updatedTime
-		}
-	}
-
-	return res.Stacks, nil
+	err := (r.(StackPage)).ExtractInto(&s)
+	return s.ListedStacks, err
 }
 
 // RetrievedStack represents the object extracted from a Get operation.
 type RetrievedStack struct {
-	Capabilities        []interface{}            `mapstructure:"capabilities"`
-	CreationTime        time.Time                `mapstructure:"-"`
-	Description         string                   `mapstructure:"description"`
-	DisableRollback     bool                     `mapstructure:"disable_rollback"`
-	ID                  string                   `mapstructure:"id"`
-	Links               []gophercloud.Link       `mapstructure:"links"`
-	NotificationTopics  []interface{}            `mapstructure:"notification_topics"`
-	Outputs             []map[string]interface{} `mapstructure:"outputs"`
-	Parameters          map[string]string        `mapstructure:"parameters"`
-	Name                string                   `mapstructure:"stack_name"`
-	Status              string                   `mapstructure:"stack_status"`
-	StatusReason        string                   `mapstructure:"stack_status_reason"`
-	Tags                []string                 `mapstructure:"tags"`
-	TemplateDescription string                   `mapstructure:"template_description"`
-	Timeout             int                      `mapstructure:"timeout_mins"`
-	UpdatedTime         time.Time                `mapstructure:"-"`
+	Capabilities        []interface{}              `json:"capabilities"`
+	CreationTime        gophercloud.JSONRFC3339NoZ `json:"creation_time"`
+	Description         string                     `json:"description"`
+	DisableRollback     bool                       `json:"disable_rollback"`
+	ID                  string                     `json:"id"`
+	Links               []gophercloud.Link         `json:"links"`
+	NotificationTopics  []interface{}              `json:"notification_topics"`
+	Outputs             []map[string]interface{}   `json:"outputs"`
+	Parameters          map[string]string          `json:"parameters"`
+	Name                string                     `json:"stack_name"`
+	Status              string                     `json:"stack_status"`
+	StatusReason        string                     `json:"stack_status_reason"`
+	Tags                []string                   `json:"tags"`
+	TemplateDescription string                     `json:"template_description"`
+	Timeout             int                        `json:"timeout_mins"`
+	UpdatedTime         gophercloud.JSONRFC3339NoZ `json:"updated_time"`
 }
 
 // GetResult represents the result of a Get operation.
@@ -148,46 +96,11 @@
 // Extract returns a pointer to a RetrievedStack object and is called after a
 // Get operation.
 func (r GetResult) Extract() (*RetrievedStack, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		Stack *RetrievedStack `json:"stack"`
 	}
-
-	var res struct {
-		Stack *RetrievedStack `mapstructure:"stack"`
-	}
-
-	config := &mapstructure.DecoderConfig{
-		Result:           &res,
-		WeaklyTypedInput: true,
-	}
-	decoder, err := mapstructure.NewDecoder(config)
-	if err != nil {
-		return nil, err
-	}
-
-	if err := decoder.Decode(r.Body); err != nil {
-		return nil, err
-	}
-
-	b := r.Body.(map[string]interface{})["stack"].(map[string]interface{})
-
-	if date, ok := b["creation_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Stack.CreationTime = t
-	}
-
-	if date, ok := b["updated_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Stack.UpdatedTime = t
-	}
-
-	return res.Stack, err
+	err := r.ExtractInto(&s)
+	return s.Stack, err
 }
 
 // UpdateResult represents the result of a Update operation.
@@ -202,19 +115,19 @@
 
 // PreviewedStack represents the result of a Preview operation.
 type PreviewedStack struct {
-	Capabilities        []interface{}      `mapstructure:"capabilities"`
-	CreationTime        time.Time          `mapstructure:"-"`
-	Description         string             `mapstructure:"description"`
-	DisableRollback     bool               `mapstructure:"disable_rollback"`
-	ID                  string             `mapstructure:"id"`
-	Links               []gophercloud.Link `mapstructure:"links"`
-	Name                string             `mapstructure:"stack_name"`
-	NotificationTopics  []interface{}      `mapstructure:"notification_topics"`
-	Parameters          map[string]string  `mapstructure:"parameters"`
-	Resources           []interface{}      `mapstructure:"resources"`
-	TemplateDescription string             `mapstructure:"template_description"`
-	Timeout             int                `mapstructure:"timeout_mins"`
-	UpdatedTime         time.Time          `mapstructure:"-"`
+	Capabilities        []interface{}              `json:"capabilities"`
+	CreationTime        gophercloud.JSONRFC3339NoZ `json:"creation_time"`
+	Description         string                     `json:"description"`
+	DisableRollback     bool                       `json:"disable_rollback"`
+	ID                  string                     `json:"id"`
+	Links               []gophercloud.Link         `json:"links"`
+	Name                string                     `json:"stack_name"`
+	NotificationTopics  []interface{}              `json:"notification_topics"`
+	Parameters          map[string]string          `json:"parameters"`
+	Resources           []interface{}              `json:"resources"`
+	TemplateDescription string                     `json:"template_description"`
+	Timeout             int                        `json:"timeout_mins"`
+	UpdatedTime         gophercloud.JSONRFC3339NoZ `json:"updated_time"`
 }
 
 // PreviewResult represents the result of a Preview operation.
@@ -225,60 +138,25 @@
 // Extract returns a pointer to a PreviewedStack object and is called after a
 // Preview operation.
 func (r PreviewResult) Extract() (*PreviewedStack, error) {
-	if r.Err != nil {
-		return nil, r.Err
+	var s struct {
+		PreviewedStack *PreviewedStack `json:"stack"`
 	}
-
-	var res struct {
-		Stack *PreviewedStack `mapstructure:"stack"`
-	}
-
-	config := &mapstructure.DecoderConfig{
-		Result:           &res,
-		WeaklyTypedInput: true,
-	}
-	decoder, err := mapstructure.NewDecoder(config)
-	if err != nil {
-		return nil, err
-	}
-
-	if err := decoder.Decode(r.Body); err != nil {
-		return nil, err
-	}
-
-	b := r.Body.(map[string]interface{})["stack"].(map[string]interface{})
-
-	if date, ok := b["creation_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Stack.CreationTime = t
-	}
-
-	if date, ok := b["updated_time"]; ok && date != nil {
-		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.Stack.UpdatedTime = t
-	}
-
-	return res.Stack, err
+	err := r.ExtractInto(&s)
+	return s.PreviewedStack, err
 }
 
 // AbandonedStack represents the result of an Abandon operation.
 type AbandonedStack struct {
-	Status             string                 `mapstructure:"status"`
-	Name               string                 `mapstructure:"name"`
-	Template           map[string]interface{} `mapstructure:"template"`
-	Action             string                 `mapstructure:"action"`
-	ID                 string                 `mapstructure:"id"`
-	Resources          map[string]interface{} `mapstructure:"resources"`
-	Files              map[string]string      `mapstructure:"files"`
-	StackUserProjectID string                 `mapstructure:"stack_user_project_id"`
-	ProjectID          string                 `mapstructure:"project_id"`
-	Environment        map[string]interface{} `mapstructure:"environment"`
+	Status             string                 `json:"status"`
+	Name               string                 `json:"name"`
+	Template           map[string]interface{} `json:"template"`
+	Action             string                 `json:"action"`
+	ID                 string                 `json:"id"`
+	Resources          map[string]interface{} `json:"resources"`
+	Files              map[string]string      `json:"files"`
+	StackUserProjectID string                 `json:"stack_user_project_id"`
+	ProjectID          string                 `json:"project_id"`
+	Environment        map[string]interface{} `json:"environment"`
 }
 
 // AbandonResult represents the result of an Abandon operation.
@@ -289,25 +167,14 @@
 // Extract returns a pointer to an AbandonedStack object and is called after an
 // Abandon operation.
 func (r AbandonResult) Extract() (*AbandonedStack, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res AbandonedStack
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return &res, nil
+	var s *AbandonedStack
+	err := r.ExtractInto(&s)
+	return s, err
 }
 
 // String converts an AbandonResult to a string. This is useful to when passing
 // the result of an Abandon operation to an AdoptOpts AdoptStackData field.
 func (r AbandonResult) String() (string, error) {
 	out, err := json.Marshal(r)
-	if err != nil {
-		return "", err
-	}
-	return string(out), nil
+	return string(out), err
 }
diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go
index 234ce49..4cf5aae 100644
--- a/openstack/orchestration/v1/stacks/template.go
+++ b/openstack/orchestration/v1/stacks/template.go
@@ -2,9 +2,10 @@
 
 import (
 	"fmt"
-	"github.com/rackspace/gophercloud"
 	"reflect"
 	"strings"
+
+	"github.com/gophercloud/gophercloud"
 )
 
 // Template is a structure that represents OpenStack Heat templates
@@ -27,12 +28,14 @@
 			return err
 		}
 	}
+	var invalid string
 	for key := range t.Parsed {
 		if _, ok := TemplateFormatVersions[key]; ok {
 			return nil
 		}
+		invalid = key
 	}
-	return fmt.Errorf("Template format version not found.")
+	return ErrInvalidTemplateFormatVersion{Version: invalid}
 }
 
 // GetFileContents recursively parses a template to search for urls. These urls
@@ -114,8 +117,7 @@
 	case string, bool, float64, nil, int:
 		return nil
 	default:
-		return fmt.Errorf("%v: Unrecognized type", reflect.TypeOf(te))
-
+		return gophercloud.ErrUnexpectedType{Actual: fmt.Sprintf("%v", reflect.TypeOf(te))}
 	}
 	return nil
 }
diff --git a/openstack/orchestration/v1/stacks/template_test.go b/openstack/orchestration/v1/stacks/template_test.go
index 6884db8..cbe99ed 100644
--- a/openstack/orchestration/v1/stacks/template_test.go
+++ b/openstack/orchestration/v1/stacks/template_test.go
@@ -7,7 +7,7 @@
 	"strings"
 	"testing"
 
-	th "github.com/rackspace/gophercloud/testhelper"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestTemplateValidation(t *testing.T) {
diff --git a/openstack/orchestration/v1/stacks/testing/doc.go b/openstack/orchestration/v1/stacks/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/orchestration/v1/stacks/testing/fixtures.go b/openstack/orchestration/v1/stacks/testing/fixtures.go
new file mode 100644
index 0000000..2afbed2
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/testing/fixtures.go
@@ -0,0 +1,407 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// CreateExpected represents the expected object from a Create request.
+var CreateExpected = &stacks.CreatedStack{
+	ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+	Links: []gophercloud.Link{
+		{
+			Href: "http://168.28.170.117:8004/v1/98606384f58drad0bhdb7d02779549ac/stacks/stackcreated/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+			Rel:  "self",
+		},
+	},
+}
+
+// CreateOutput represents the response body from a Create request.
+const CreateOutput = `
+{
+  "stack": {
+    "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+    "links": [
+    {
+      "href": "http://168.28.170.117:8004/v1/98606384f58drad0bhdb7d02779549ac/stacks/stackcreated/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+      "rel": "self"
+    }
+    ]
+  }
+}`
+
+// HandleCreateSuccessfully creates an HTTP handler at `/stacks` on the test handler mux
+// that responds with a `Create` response.
+func HandleCreateSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusCreated)
+		fmt.Fprintf(w, output)
+	})
+}
+
+// ListExpected represents the expected object from a List request.
+var ListExpected = []stacks.ListedStack{
+	{
+		Description: "Simple template to test heat commands",
+		Links: []gophercloud.Link{
+			{
+				Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+				Rel:  "self",
+			},
+		},
+		StatusReason: "Stack CREATE completed successfully",
+		Name:         "postman_stack",
+		CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC)),
+		Status:       "CREATE_COMPLETE",
+		ID:           "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+		Tags:         []string{"rackspace", "atx"},
+	},
+	{
+		Description: "Simple template to test heat commands",
+		Links: []gophercloud.Link{
+			{
+				Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada",
+				Rel:  "self",
+			},
+		},
+		StatusReason: "Stack successfully updated",
+		Name:         "gophercloud-test-stack-2",
+		CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2014, 12, 11, 17, 39, 16, 0, time.UTC)),
+		UpdatedTime:  gophercloud.JSONRFC3339NoZ(time.Date(2014, 12, 11, 17, 40, 37, 0, time.UTC)),
+		Status:       "UPDATE_COMPLETE",
+		ID:           "db6977b2-27aa-4775-9ae7-6213212d4ada",
+		Tags:         []string{"sfo", "satx"},
+	},
+}
+
+// FullListOutput represents the response body from a List request without a marker.
+const FullListOutput = `
+{
+  "stacks": [
+  {
+    "description": "Simple template to test heat commands",
+    "links": [
+    {
+      "href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+      "rel": "self"
+    }
+    ],
+    "stack_status_reason": "Stack CREATE completed successfully",
+    "stack_name": "postman_stack",
+    "creation_time": "2015-02-03T20:07:39",
+    "updated_time": null,
+    "stack_status": "CREATE_COMPLETE",
+    "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+	"tags": ["rackspace", "atx"]
+  },
+  {
+    "description": "Simple template to test heat commands",
+    "links": [
+    {
+      "href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada",
+      "rel": "self"
+    }
+    ],
+    "stack_status_reason": "Stack successfully updated",
+    "stack_name": "gophercloud-test-stack-2",
+    "creation_time": "2014-12-11T17:39:16",
+    "updated_time": "2014-12-11T17:40:37",
+    "stack_status": "UPDATE_COMPLETE",
+    "id": "db6977b2-27aa-4775-9ae7-6213212d4ada",
+	"tags": ["sfo", "satx"]
+  }
+  ]
+}
+`
+
+// HandleListSuccessfully creates an HTTP handler at `/stacks` on the test handler mux
+// that responds with a `List` response.
+func HandleListSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, output)
+		case "db6977b2-27aa-4775-9ae7-6213212d4ada":
+			fmt.Fprintf(w, `[]`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+}
+
+// GetExpected represents the expected object from a Get request.
+var GetExpected = &stacks.RetrievedStack{
+	DisableRollback: true,
+	Description:     "Simple template to test heat commands",
+	Parameters: map[string]string{
+		"flavor":         "m1.tiny",
+		"OS::stack_name": "postman_stack",
+		"OS::stack_id":   "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+	},
+	StatusReason: "Stack CREATE completed successfully",
+	Name:         "postman_stack",
+	Outputs:      []map[string]interface{}{},
+	CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC)),
+	Links: []gophercloud.Link{
+		{
+			Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+			Rel:  "self",
+		},
+	},
+	Capabilities:        []interface{}{},
+	NotificationTopics:  []interface{}{},
+	Status:              "CREATE_COMPLETE",
+	ID:                  "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+	TemplateDescription: "Simple template to test heat commands",
+	Tags:                []string{"rackspace", "atx"},
+}
+
+// GetOutput represents the response body from a Get request.
+const GetOutput = `
+{
+  "stack": {
+    "disable_rollback": true,
+    "description": "Simple template to test heat commands",
+    "parameters": {
+      "flavor": "m1.tiny",
+      "OS::stack_name": "postman_stack",
+      "OS::stack_id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87"
+    },
+    "stack_status_reason": "Stack CREATE completed successfully",
+    "stack_name": "postman_stack",
+    "outputs": [],
+    "creation_time": "2015-02-03T20:07:39",
+    "links": [
+    {
+      "href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+      "rel": "self"
+    }
+    ],
+    "capabilities": [],
+    "notification_topics": [],
+    "timeout_mins": null,
+    "stack_status": "CREATE_COMPLETE",
+    "updated_time": null,
+    "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+    "template_description": "Simple template to test heat commands",
+	"tags": ["rackspace", "atx"]
+  }
+}
+`
+
+// HandleGetSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87`
+// on the test handler mux that responds with a `Get` response.
+func HandleGetSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
+
+// HandleUpdateSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87`
+// on the test handler mux that responds with an `Update` response.
+func HandleUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusAccepted)
+	})
+}
+
+// HandleDeleteSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87`
+// on the test handler mux that responds with a `Delete` response.
+func HandleDeleteSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// GetExpected represents the expected object from a Get request.
+var PreviewExpected = &stacks.PreviewedStack{
+	DisableRollback: true,
+	Description:     "Simple template to test heat commands",
+	Parameters: map[string]string{
+		"flavor":         "m1.tiny",
+		"OS::stack_name": "postman_stack",
+		"OS::stack_id":   "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+	},
+	Name:         "postman_stack",
+	CreationTime: gophercloud.JSONRFC3339NoZ(time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC)),
+	Links: []gophercloud.Link{
+		{
+			Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+			Rel:  "self",
+		},
+	},
+	Capabilities:        []interface{}{},
+	NotificationTopics:  []interface{}{},
+	ID:                  "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+	TemplateDescription: "Simple template to test heat commands",
+}
+
+// HandlePreviewSuccessfully creates an HTTP handler at `/stacks/preview`
+// on the test handler mux that responds with a `Preview` response.
+func HandlePreviewSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/preview", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
+
+// AbandonExpected represents the expected object from an Abandon request.
+var AbandonExpected = &stacks.AbandonedStack{
+	Status: "COMPLETE",
+	Name:   "postman_stack",
+	Template: map[string]interface{}{
+		"heat_template_version": "2013-05-23",
+		"description":           "Simple template to test heat commands",
+		"parameters": map[string]interface{}{
+			"flavor": map[string]interface{}{
+				"default": "m1.tiny",
+				"type":    "string",
+			},
+		},
+		"resources": map[string]interface{}{
+			"hello_world": map[string]interface{}{
+				"type": "OS::Nova::Server",
+				"properties": map[string]interface{}{
+					"key_name": "heat_key",
+					"flavor": map[string]interface{}{
+						"get_param": "flavor",
+					},
+					"image":     "ad091b52-742f-469e-8f3c-fd81cadf0743",
+					"user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n",
+				},
+			},
+		},
+	},
+	Action: "CREATE",
+	ID:     "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+	Resources: map[string]interface{}{
+		"hello_world": map[string]interface{}{
+			"status":      "COMPLETE",
+			"name":        "hello_world",
+			"resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63",
+			"action":      "CREATE",
+			"type":        "OS::Nova::Server",
+		},
+	},
+	Files: map[string]string{
+		"file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "heat_template_version: 2014-10-16\nparameters:\n  flavor:\n    type: string\n    description: Flavor for the server to be created\n    default: 4353\n    hidden: true\nresources:\n  test_server:\n    type: \"OS::Nova::Server\"\n    properties:\n      name: test-server\n      flavor: 2 GB General Purpose v1\n image: Debian 7 (Wheezy) (PVHVM)\n",
+	},
+	StackUserProjectID: "897686",
+	ProjectID:          "897686",
+	Environment: map[string]interface{}{
+		"encrypted_param_names": make([]map[string]interface{}, 0),
+		"parameter_defaults":    make(map[string]interface{}),
+		"parameters":            make(map[string]interface{}),
+		"resource_registry": map[string]interface{}{
+			"file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml",
+			"resources": make(map[string]interface{}),
+		},
+	},
+}
+
+// AbandonOutput represents the response body from an Abandon request.
+const AbandonOutput = `
+{
+  "status": "COMPLETE",
+  "name": "postman_stack",
+  "template": {
+    "heat_template_version": "2013-05-23",
+    "description": "Simple template to test heat commands",
+    "parameters": {
+      "flavor": {
+        "default": "m1.tiny",
+        "type": "string"
+      }
+    },
+    "resources": {
+      "hello_world": {
+        "type": "OS::Nova::Server",
+        "properties": {
+          "key_name": "heat_key",
+          "flavor": {
+            "get_param": "flavor"
+          },
+          "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
+          "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
+        }
+      }
+    }
+  },
+  "action": "CREATE",
+  "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+  "resources": {
+    "hello_world": {
+      "status": "COMPLETE",
+      "name": "hello_world",
+      "resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63",
+      "action": "CREATE",
+      "type": "OS::Nova::Server"
+    }
+  },
+  "files": {
+    "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "heat_template_version: 2014-10-16\nparameters:\n  flavor:\n    type: string\n    description: Flavor for the server to be created\n    default: 4353\n    hidden: true\nresources:\n  test_server:\n    type: \"OS::Nova::Server\"\n    properties:\n      name: test-server\n      flavor: 2 GB General Purpose v1\n image: Debian 7 (Wheezy) (PVHVM)\n"
+},
+  "environment": {
+	"encrypted_param_names": [],
+	"parameter_defaults": {},
+	"parameters": {},
+	"resource_registry": {
+		"file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml",
+		"resources": {}
+	}
+  },
+  "stack_user_project_id": "897686",
+  "project_id": "897686"
+}`
+
+// HandleAbandonSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/abandon`
+// on the test handler mux that responds with an `Abandon` response.
+func HandleAbandonSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c8/abandon", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
diff --git a/openstack/orchestration/v1/stacks/testing/requests_test.go b/openstack/orchestration/v1/stacks/testing/requests_test.go
new file mode 100644
index 0000000..bdc6229
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/testing/requests_test.go
@@ -0,0 +1,192 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks"
+	"github.com/gophercloud/gophercloud/pagination"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestCreateStack(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleCreateSuccessfully(t, CreateOutput)
+	template := new(stacks.Template)
+	template.Bin = []byte(`
+		{
+			"heat_template_version": "2013-05-23",
+			"description": "Simple template to test heat commands",
+			"parameters": {
+				"flavor": {
+					"default": "m1.tiny",
+					"type": "string"
+				}
+			}
+		}`)
+	createOpts := stacks.CreateOpts{
+		Name:            "stackcreated",
+		Timeout:         60,
+		TemplateOpts:    template,
+		DisableRollback: gophercloud.Disabled,
+	}
+	actual, err := stacks.Create(fake.ServiceClient(), createOpts).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := CreateExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestAdoptStack(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleCreateSuccessfully(t, CreateOutput)
+	template := new(stacks.Template)
+	template.Bin = []byte(`
+{
+  "stack_name": "postman_stack",
+  "template": {
+	"heat_template_version": "2013-05-23",
+	"description": "Simple template to test heat commands",
+	"parameters": {
+	  "flavor": {
+		"default": "m1.tiny",
+		"type": "string"
+	  }
+	},
+	"resources": {
+	  "hello_world": {
+		"type":"OS::Nova::Server",
+		"properties": {
+		  "key_name": "heat_key",
+		  "flavor": {
+			"get_param": "flavor"
+		  },
+		  "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
+		  "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
+		}
+	  }
+	}
+  }
+}`)
+	adoptOpts := stacks.AdoptOpts{
+		AdoptStackData:  `{environment{parameters{}}}`,
+		Name:            "stackcreated",
+		Timeout:         60,
+		TemplateOpts:    template,
+		DisableRollback: gophercloud.Disabled,
+	}
+	actual, err := stacks.Adopt(fake.ServiceClient(), adoptOpts).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := CreateExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestListStack(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListSuccessfully(t, FullListOutput)
+
+	count := 0
+	err := stacks.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := stacks.ExtractStacks(page)
+		th.AssertNoErr(t, err)
+
+		th.CheckDeepEquals(t, ListExpected, actual)
+
+		return true, nil
+	})
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, count, 1)
+}
+
+func TestGetStack(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t, GetOutput)
+
+	actual, err := stacks.Get(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := GetExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestUpdateStack(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleUpdateSuccessfully(t)
+
+	template := new(stacks.Template)
+	template.Bin = []byte(`
+		{
+			"heat_template_version": "2013-05-23",
+			"description": "Simple template to test heat commands",
+			"parameters": {
+				"flavor": {
+					"default": "m1.tiny",
+					"type": "string"
+				}
+			}
+		}`)
+	updateOpts := stacks.UpdateOpts{
+		TemplateOpts: template,
+	}
+	err := stacks.Update(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestDeleteStack(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleDeleteSuccessfully(t)
+
+	err := stacks.Delete(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada").ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
+func TestPreviewStack(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePreviewSuccessfully(t, GetOutput)
+
+	template := new(stacks.Template)
+	template.Bin = []byte(`
+		{
+			"heat_template_version": "2013-05-23",
+			"description": "Simple template to test heat commands",
+			"parameters": {
+				"flavor": {
+					"default": "m1.tiny",
+					"type": "string"
+				}
+			}
+		}`)
+	previewOpts := stacks.PreviewOpts{
+		Name:            "stackcreated",
+		Timeout:         60,
+		TemplateOpts:    template,
+		DisableRollback: gophercloud.Disabled,
+	}
+	actual, err := stacks.Preview(fake.ServiceClient(), previewOpts).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := PreviewExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestAbandonStack(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleAbandonSuccessfully(t, AbandonOutput)
+
+	actual, err := stacks.Abandon(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c8").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := AbandonExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
diff --git a/openstack/orchestration/v1/stacks/urls.go b/openstack/orchestration/v1/stacks/urls.go
index 3dd2bb3..b00be54 100644
--- a/openstack/orchestration/v1/stacks/urls.go
+++ b/openstack/orchestration/v1/stacks/urls.go
@@ -1,6 +1,6 @@
 package stacks
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func createURL(c *gophercloud.ServiceClient) string {
 	return c.ServiceURL("stacks")
diff --git a/openstack/orchestration/v1/stacks/utils.go b/openstack/orchestration/v1/stacks/utils.go
index 7b476a9..71d9e35 100644
--- a/openstack/orchestration/v1/stacks/utils.go
+++ b/openstack/orchestration/v1/stacks/utils.go
@@ -9,7 +9,7 @@
 	"reflect"
 	"strings"
 
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 	"gopkg.in/yaml.v2"
 )
 
@@ -113,7 +113,7 @@
 	}
 	if jerr := json.Unmarshal(t.Bin, &t.Parsed); jerr != nil {
 		if yerr := yaml.Unmarshal(t.Bin, &t.Parsed); yerr != nil {
-			return fmt.Errorf("Data in neither json nor yaml format.")
+			return ErrInvalidDataFormat{}
 		}
 	}
 	return t.Validate()
@@ -142,8 +142,7 @@
 		}
 		return typedMap, nil
 	default:
-		return nil, fmt.Errorf("Expected a map of type map[string]interface{} or map[interface{}]interface{}, actual type: %v", reflect.TypeOf(m))
-
+		return nil, gophercloud.ErrUnexpectedType{Expected: "map[string]interface{}/map[interface{}]interface{}", Actual: fmt.Sprintf("%v", reflect.TypeOf(m))}
 	}
 }
 
diff --git a/openstack/orchestration/v1/stacks/utils_test.go b/openstack/orchestration/v1/stacks/utils_test.go
index 2536e03..4d5cc73 100644
--- a/openstack/orchestration/v1/stacks/utils_test.go
+++ b/openstack/orchestration/v1/stacks/utils_test.go
@@ -7,7 +7,7 @@
 	"strings"
 	"testing"
 
-	th "github.com/rackspace/gophercloud/testhelper"
+	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
 func TestTEFixFileRefs(t *testing.T) {
diff --git a/openstack/orchestration/v1/stacktemplates/fixtures.go b/openstack/orchestration/v1/stacktemplates/fixtures.go
deleted file mode 100644
index ff544d4..0000000
--- a/openstack/orchestration/v1/stacktemplates/fixtures.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// +build fixtures
-
-package stacktemplates
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// GetExpected represents the expected object from a Get request.
-var GetExpected = "{\n  \"description\": \"Simple template to test heat commands\",\n  \"heat_template_version\": \"2013-05-23\",\n  \"parameters\": {\n    \"flavor\": {\n      \"default\": \"m1.tiny\",\n      \"type\": \"string\"\n    }\n  },\n  \"resources\": {\n    \"hello_world\": {\n      \"properties\": {\n        \"flavor\": {\n          \"get_param\": \"flavor\"\n        },\n        \"image\": \"ad091b52-742f-469e-8f3c-fd81cadf0743\",\n        \"key_name\": \"heat_key\"\n      },\n      \"type\": \"OS::Nova::Server\"\n    }\n  }\n}"
-
-// GetOutput represents the response body from a Get request.
-const GetOutput = `
-{
-  "heat_template_version": "2013-05-23",
-  "description": "Simple template to test heat commands",
-  "parameters": {
-    "flavor": {
-      "default": "m1.tiny",
-      "type": "string"
-    }
-  },
-  "resources": {
-    "hello_world": {
-      "type": "OS::Nova::Server",
-      "properties": {
-        "key_name": "heat_key",
-        "flavor": {
-          "get_param": "flavor"
-        },
-        "image": "ad091b52-742f-469e-8f3c-fd81cadf0743"
-      }
-    }
-  }
-}`
-
-// HandleGetSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/template`
-// on the test handler mux that responds with a `Get` response.
-func HandleGetSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/template", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
-
-// ValidateExpected represents the expected object from a Validate request.
-var ValidateExpected = &ValidatedTemplate{
-	Description: "Simple template to test heat commands",
-	Parameters: map[string]interface{}{
-		"flavor": map[string]interface{}{
-			"Default":     "m1.tiny",
-			"Type":        "String",
-			"NoEcho":      "false",
-			"Description": "",
-			"Label":       "flavor",
-		},
-	},
-}
-
-// ValidateOutput represents the response body from a Validate request.
-const ValidateOutput = `
-{
-	"Description": "Simple template to test heat commands",
-	"Parameters": {
-		"flavor": {
-			"Default": "m1.tiny",
-			"Type": "String",
-			"NoEcho": "false",
-			"Description": "",
-			"Label": "flavor"
-		}
-	}
-}`
-
-// HandleValidateSuccessfully creates an HTTP handler at `/validate`
-// on the test handler mux that responds with a `Validate` response.
-func HandleValidateSuccessfully(t *testing.T, output string) {
-	th.Mux.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, output)
-	})
-}
diff --git a/openstack/orchestration/v1/stacktemplates/requests.go b/openstack/orchestration/v1/stacktemplates/requests.go
index c0cea35..d248c24 100644
--- a/openstack/orchestration/v1/stacktemplates/requests.go
+++ b/openstack/orchestration/v1/stacktemplates/requests.go
@@ -1,18 +1,11 @@
 package stacktemplates
 
-import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-)
+import "github.com/gophercloud/gophercloud"
 
 // Get retreives data for the given stack template.
-func Get(c *gophercloud.ServiceClient, stackName, stackID string) GetResult {
-	var res GetResult
-	_, res.Err = c.Request("GET", getURL(c, stackName, stackID), gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
-	})
-	return res
+func Get(c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) {
+	_, r.Err = c.Get(getURL(c, stackName, stackID), &r.Body, nil)
+	return
 }
 
 // ValidateOptsBuilder describes struct types that can be accepted by the Validate call.
@@ -23,36 +16,24 @@
 
 // ValidateOpts specifies the template validation parameters.
 type ValidateOpts struct {
-	Template    string
-	TemplateURL string
+	Template    string `json:"template" or:"TemplateURL"`
+	TemplateURL string `json:"template_url" or:"Template"`
 }
 
 // ToStackTemplateValidateMap assembles a request body based on the contents of a ValidateOpts.
 func (opts ValidateOpts) ToStackTemplateValidateMap() (map[string]interface{}, error) {
-	vo := make(map[string]interface{})
-	if opts.Template != "" {
-		vo["template"] = opts.Template
-		return vo, nil
-	}
-	if opts.TemplateURL != "" {
-		vo["template_url"] = opts.TemplateURL
-		return vo, nil
-	}
-	return vo, fmt.Errorf("One of Template or TemplateURL is required.")
+	return gophercloud.BuildRequestBody(opts, "")
 }
 
 // Validate validates the given stack template.
-func Validate(c *gophercloud.ServiceClient, opts ValidateOptsBuilder) ValidateResult {
-	var res ValidateResult
-
-	reqBody, err := opts.ToStackTemplateValidateMap()
+func Validate(c *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r ValidateResult) {
+	b, err := opts.ToStackTemplateValidateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return
 	}
-
-	_, res.Err = c.Post(validateURL(c), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = c.Post(validateURL(c), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return
 }
diff --git a/openstack/orchestration/v1/stacktemplates/requests_test.go b/openstack/orchestration/v1/stacktemplates/requests_test.go
deleted file mode 100644
index 42667c9..0000000
--- a/openstack/orchestration/v1/stacktemplates/requests_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package stacktemplates
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestGetTemplate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleGetSuccessfully(t, GetOutput)
-
-	actual, err := Get(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := GetExpected
-	th.AssertDeepEquals(t, expected, string(actual))
-}
-
-func TestValidateTemplate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleValidateSuccessfully(t, ValidateOutput)
-
-	opts := ValidateOpts{
-		Template: `{
-		  "heat_template_version": "2013-05-23",
-		  "description": "Simple template to test heat commands",
-		  "parameters": {
-		    "flavor": {
-		      "default": "m1.tiny",
-		      "type": "string"
-		    }
-		  },
-		  "resources": {
-		    "hello_world": {
-		      "type": "OS::Nova::Server",
-		      "properties": {
-		        "key_name": "heat_key",
-		        "flavor": {
-		          "get_param": "flavor"
-		        },
-		        "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-		        "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-		      }
-		    }
-		  }
-		}`,
-	}
-	actual, err := Validate(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := ValidateExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/openstack/orchestration/v1/stacktemplates/results.go b/openstack/orchestration/v1/stacktemplates/results.go
index 4e9ba5a..bca959b 100644
--- a/openstack/orchestration/v1/stacktemplates/results.go
+++ b/openstack/orchestration/v1/stacktemplates/results.go
@@ -2,8 +2,8 @@
 
 import (
 	"encoding/json"
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
+
+	"github.com/gophercloud/gophercloud"
 )
 
 // GetResult represents the result of a Get operation.
@@ -25,9 +25,9 @@
 
 // ValidatedTemplate represents the parsed object returned from a Validate request.
 type ValidatedTemplate struct {
-	Description     string                 `mapstructure:"Description"`
-	Parameters      map[string]interface{} `mapstructure:"Parameters"`
-	ParameterGroups map[string]interface{} `mapstructure:"ParameterGroups"`
+	Description     string                 `json:"Description"`
+	Parameters      map[string]interface{} `json:"Parameters"`
+	ParameterGroups map[string]interface{} `json:"ParameterGroups"`
 }
 
 // ValidateResult represents the result of a Validate operation.
@@ -38,14 +38,7 @@
 // Extract returns a pointer to a ValidatedTemplate object and is called after a
 // Validate operation.
 func (r ValidateResult) Extract() (*ValidatedTemplate, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res ValidatedTemplate
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
-		return nil, err
-	}
-
-	return &res, nil
+	var s *ValidatedTemplate
+	err := r.ExtractInto(&s)
+	return s, err
 }
diff --git a/openstack/orchestration/v1/stacktemplates/testing/doc.go b/openstack/orchestration/v1/stacktemplates/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/orchestration/v1/stacktemplates/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/orchestration/v1/stacktemplates/testing/fixtures.go b/openstack/orchestration/v1/stacktemplates/testing/fixtures.go
new file mode 100644
index 0000000..23ec579
--- /dev/null
+++ b/openstack/orchestration/v1/stacktemplates/testing/fixtures.go
@@ -0,0 +1,96 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacktemplates"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// GetExpected represents the expected object from a Get request.
+var GetExpected = "{\n  \"description\": \"Simple template to test heat commands\",\n  \"heat_template_version\": \"2013-05-23\",\n  \"parameters\": {\n    \"flavor\": {\n      \"default\": \"m1.tiny\",\n      \"type\": \"string\"\n    }\n  },\n  \"resources\": {\n    \"hello_world\": {\n      \"properties\": {\n        \"flavor\": {\n          \"get_param\": \"flavor\"\n        },\n        \"image\": \"ad091b52-742f-469e-8f3c-fd81cadf0743\",\n        \"key_name\": \"heat_key\"\n      },\n      \"type\": \"OS::Nova::Server\"\n    }\n  }\n}"
+
+// GetOutput represents the response body from a Get request.
+const GetOutput = `
+{
+  "heat_template_version": "2013-05-23",
+  "description": "Simple template to test heat commands",
+  "parameters": {
+    "flavor": {
+      "default": "m1.tiny",
+      "type": "string"
+    }
+  },
+  "resources": {
+    "hello_world": {
+      "type": "OS::Nova::Server",
+      "properties": {
+        "key_name": "heat_key",
+        "flavor": {
+          "get_param": "flavor"
+        },
+        "image": "ad091b52-742f-469e-8f3c-fd81cadf0743"
+      }
+    }
+  }
+}`
+
+// HandleGetSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/template`
+// on the test handler mux that responds with a `Get` response.
+func HandleGetSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/template", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
+
+// ValidateExpected represents the expected object from a Validate request.
+var ValidateExpected = &stacktemplates.ValidatedTemplate{
+	Description: "Simple template to test heat commands",
+	Parameters: map[string]interface{}{
+		"flavor": map[string]interface{}{
+			"Default":     "m1.tiny",
+			"Type":        "String",
+			"NoEcho":      "false",
+			"Description": "",
+			"Label":       "flavor",
+		},
+	},
+}
+
+// ValidateOutput represents the response body from a Validate request.
+const ValidateOutput = `
+{
+	"Description": "Simple template to test heat commands",
+	"Parameters": {
+		"flavor": {
+			"Default": "m1.tiny",
+			"Type": "String",
+			"NoEcho": "false",
+			"Description": "",
+			"Label": "flavor"
+		}
+	}
+}`
+
+// HandleValidateSuccessfully creates an HTTP handler at `/validate`
+// on the test handler mux that responds with a `Validate` response.
+func HandleValidateSuccessfully(t *testing.T, output string) {
+	th.Mux.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, output)
+	})
+}
diff --git a/openstack/orchestration/v1/stacktemplates/testing/requests_test.go b/openstack/orchestration/v1/stacktemplates/testing/requests_test.go
new file mode 100644
index 0000000..442bcb7
--- /dev/null
+++ b/openstack/orchestration/v1/stacktemplates/testing/requests_test.go
@@ -0,0 +1,58 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacktemplates"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestGetTemplate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetSuccessfully(t, GetOutput)
+
+	actual, err := stacktemplates.Get(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := GetExpected
+	th.AssertDeepEquals(t, expected, string(actual))
+}
+
+func TestValidateTemplate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleValidateSuccessfully(t, ValidateOutput)
+
+	opts := stacktemplates.ValidateOpts{
+		Template: `{
+		  "heat_template_version": "2013-05-23",
+		  "description": "Simple template to test heat commands",
+		  "parameters": {
+		    "flavor": {
+		      "default": "m1.tiny",
+		      "type": "string"
+		    }
+		  },
+		  "resources": {
+		    "hello_world": {
+		      "type": "OS::Nova::Server",
+		      "properties": {
+		        "key_name": "heat_key",
+		        "flavor": {
+		          "get_param": "flavor"
+		        },
+		        "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
+		        "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
+		      }
+		    }
+		  }
+		}`,
+	}
+	actual, err := stacktemplates.Validate(fake.ServiceClient(), opts).Extract()
+	th.AssertNoErr(t, err)
+
+	expected := ValidateExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
diff --git a/openstack/orchestration/v1/stacktemplates/urls.go b/openstack/orchestration/v1/stacktemplates/urls.go
index c30b7ca..aed6b4b 100644
--- a/openstack/orchestration/v1/stacktemplates/urls.go
+++ b/openstack/orchestration/v1/stacktemplates/urls.go
@@ -1,6 +1,6 @@
 package stacktemplates
 
-import "github.com/rackspace/gophercloud"
+import "github.com/gophercloud/gophercloud"
 
 func getURL(c *gophercloud.ServiceClient, stackName, stackID string) string {
 	return c.ServiceURL("stacks", stackName, stackID, "template")
diff --git a/openstack/testing/client_test.go b/openstack/testing/client_test.go
new file mode 100644
index 0000000..37e63c1
--- /dev/null
+++ b/openstack/testing/client_test.go
@@ -0,0 +1,162 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestAuthenticatedClientV3(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	const ID = "0123456789"
+
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, `
+			{
+				"versions": {
+					"values": [
+						{
+							"status": "stable",
+							"id": "v3.0",
+							"links": [
+								{ "href": "%s", "rel": "self" }
+							]
+						},
+						{
+							"status": "stable",
+							"id": "v2.0",
+							"links": [
+								{ "href": "%s", "rel": "self" }
+							]
+						}
+					]
+				}
+			}
+		`, th.Endpoint()+"v3/", th.Endpoint()+"v2.0/")
+	})
+
+	th.Mux.HandleFunc("/v3/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("X-Subject-Token", ID)
+
+		w.WriteHeader(http.StatusCreated)
+		fmt.Fprintf(w, `{ "token": { "expires_at": "2013-02-02T18:30:59.000000Z" } }`)
+	})
+
+	options := gophercloud.AuthOptions{
+		UserID:           "me",
+		Password:         "secret",
+		IdentityEndpoint: th.Endpoint(),
+	}
+	client, err := openstack.AuthenticatedClient(options)
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, ID, client.TokenID)
+}
+
+func TestAuthenticatedClientV2(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, `
+			{
+				"versions": {
+					"values": [
+						{
+							"status": "experimental",
+							"id": "v3.0",
+							"links": [
+								{ "href": "%s", "rel": "self" }
+							]
+						},
+						{
+							"status": "stable",
+							"id": "v2.0",
+							"links": [
+								{ "href": "%s", "rel": "self" }
+							]
+						}
+					]
+				}
+			}
+		`, th.Endpoint()+"v3/", th.Endpoint()+"v2.0/")
+	})
+
+	th.Mux.HandleFunc("/v2.0/tokens", func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, `
+			{
+				"access": {
+					"token": {
+						"id": "01234567890",
+						"expires": "2014-10-01T10:00:00.000000Z"
+					},
+					"serviceCatalog": [
+						{
+							"name": "Cloud Servers",
+							"type": "compute",
+							"endpoints": [
+								{
+									"tenantId": "t1000",
+									"publicURL": "https://compute.north.host.com/v1/t1000",
+									"internalURL": "https://compute.north.internal/v1/t1000",
+									"region": "North",
+									"versionId": "1",
+									"versionInfo": "https://compute.north.host.com/v1/",
+									"versionList": "https://compute.north.host.com/"
+								},
+								{
+									"tenantId": "t1000",
+									"publicURL": "https://compute.north.host.com/v1.1/t1000",
+									"internalURL": "https://compute.north.internal/v1.1/t1000",
+									"region": "North",
+									"versionId": "1.1",
+									"versionInfo": "https://compute.north.host.com/v1.1/",
+									"versionList": "https://compute.north.host.com/"
+								}
+							],
+							"endpoints_links": []
+						},
+						{
+							"name": "Cloud Files",
+							"type": "object-store",
+							"endpoints": [
+								{
+									"tenantId": "t1000",
+									"publicURL": "https://storage.north.host.com/v1/t1000",
+									"internalURL": "https://storage.north.internal/v1/t1000",
+									"region": "North",
+									"versionId": "1",
+									"versionInfo": "https://storage.north.host.com/v1/",
+									"versionList": "https://storage.north.host.com/"
+								},
+								{
+									"tenantId": "t1000",
+									"publicURL": "https://storage.south.host.com/v1/t1000",
+									"internalURL": "https://storage.south.internal/v1/t1000",
+									"region": "South",
+									"versionId": "1",
+									"versionInfo": "https://storage.south.host.com/v1/",
+									"versionList": "https://storage.south.host.com/"
+								}
+							]
+						}
+					]
+				}
+			}
+		`)
+	})
+
+	options := gophercloud.AuthOptions{
+		Username:         "me",
+		Password:         "secret",
+		IdentityEndpoint: th.Endpoint(),
+	}
+	client, err := openstack.AuthenticatedClient(options)
+	th.AssertNoErr(t, err)
+	th.CheckEquals(t, "01234567890", client.TokenID)
+}
diff --git a/openstack/testing/doc.go b/openstack/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/testing/endpoint_location_test.go b/openstack/testing/endpoint_location_test.go
new file mode 100644
index 0000000..ea7bdd2
--- /dev/null
+++ b/openstack/testing/endpoint_location_test.go
@@ -0,0 +1,231 @@
+package testing
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
+	tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+// Service catalog fixtures take too much vertical space!
+var catalog2 = tokens2.ServiceCatalog{
+	Entries: []tokens2.CatalogEntry{
+		tokens2.CatalogEntry{
+			Type: "same",
+			Name: "same",
+			Endpoints: []tokens2.Endpoint{
+				tokens2.Endpoint{
+					Region:      "same",
+					PublicURL:   "https://public.correct.com/",
+					InternalURL: "https://internal.correct.com/",
+					AdminURL:    "https://admin.correct.com/",
+				},
+				tokens2.Endpoint{
+					Region:    "different",
+					PublicURL: "https://badregion.com/",
+				},
+			},
+		},
+		tokens2.CatalogEntry{
+			Type: "same",
+			Name: "different",
+			Endpoints: []tokens2.Endpoint{
+				tokens2.Endpoint{
+					Region:    "same",
+					PublicURL: "https://badname.com/",
+				},
+				tokens2.Endpoint{
+					Region:    "different",
+					PublicURL: "https://badname.com/+badregion",
+				},
+			},
+		},
+		tokens2.CatalogEntry{
+			Type: "different",
+			Name: "different",
+			Endpoints: []tokens2.Endpoint{
+				tokens2.Endpoint{
+					Region:    "same",
+					PublicURL: "https://badtype.com/+badname",
+				},
+				tokens2.Endpoint{
+					Region:    "different",
+					PublicURL: "https://badtype.com/+badregion+badname",
+				},
+			},
+		},
+	},
+}
+
+func TestV2EndpointExact(t *testing.T) {
+	expectedURLs := map[gophercloud.Availability]string{
+		gophercloud.AvailabilityPublic:   "https://public.correct.com/",
+		gophercloud.AvailabilityAdmin:    "https://admin.correct.com/",
+		gophercloud.AvailabilityInternal: "https://internal.correct.com/",
+	}
+
+	for availability, expected := range expectedURLs {
+		actual, err := openstack.V2EndpointURL(&catalog2, gophercloud.EndpointOpts{
+			Type:         "same",
+			Name:         "same",
+			Region:       "same",
+			Availability: availability,
+		})
+		th.AssertNoErr(t, err)
+		th.CheckEquals(t, expected, actual)
+	}
+}
+
+func TestV2EndpointNone(t *testing.T) {
+	_, actual := openstack.V2EndpointURL(&catalog2, gophercloud.EndpointOpts{
+		Type:         "nope",
+		Availability: gophercloud.AvailabilityPublic,
+	})
+	expected := &gophercloud.ErrEndpointNotFound{}
+	th.CheckEquals(t, expected.Error(), actual.Error())
+}
+
+func TestV2EndpointMultiple(t *testing.T) {
+	_, err := openstack.V2EndpointURL(&catalog2, gophercloud.EndpointOpts{
+		Type:         "same",
+		Region:       "same",
+		Availability: gophercloud.AvailabilityPublic,
+	})
+	if !strings.HasPrefix(err.Error(), "Discovered 2 matching endpoints:") {
+		t.Errorf("Received unexpected error: %v", err)
+	}
+}
+
+func TestV2EndpointBadAvailability(t *testing.T) {
+	_, err := openstack.V2EndpointURL(&catalog2, gophercloud.EndpointOpts{
+		Type:         "same",
+		Name:         "same",
+		Region:       "same",
+		Availability: "wat",
+	})
+	th.CheckEquals(t, "Unexpected availability in endpoint query: wat", err.Error())
+}
+
+var catalog3 = tokens3.ServiceCatalog{
+	Entries: []tokens3.CatalogEntry{
+		tokens3.CatalogEntry{
+			Type: "same",
+			Name: "same",
+			Endpoints: []tokens3.Endpoint{
+				tokens3.Endpoint{
+					ID:        "1",
+					Region:    "same",
+					Interface: "public",
+					URL:       "https://public.correct.com/",
+				},
+				tokens3.Endpoint{
+					ID:        "2",
+					Region:    "same",
+					Interface: "admin",
+					URL:       "https://admin.correct.com/",
+				},
+				tokens3.Endpoint{
+					ID:        "3",
+					Region:    "same",
+					Interface: "internal",
+					URL:       "https://internal.correct.com/",
+				},
+				tokens3.Endpoint{
+					ID:        "4",
+					Region:    "different",
+					Interface: "public",
+					URL:       "https://badregion.com/",
+				},
+			},
+		},
+		tokens3.CatalogEntry{
+			Type: "same",
+			Name: "different",
+			Endpoints: []tokens3.Endpoint{
+				tokens3.Endpoint{
+					ID:        "5",
+					Region:    "same",
+					Interface: "public",
+					URL:       "https://badname.com/",
+				},
+				tokens3.Endpoint{
+					ID:        "6",
+					Region:    "different",
+					Interface: "public",
+					URL:       "https://badname.com/+badregion",
+				},
+			},
+		},
+		tokens3.CatalogEntry{
+			Type: "different",
+			Name: "different",
+			Endpoints: []tokens3.Endpoint{
+				tokens3.Endpoint{
+					ID:        "7",
+					Region:    "same",
+					Interface: "public",
+					URL:       "https://badtype.com/+badname",
+				},
+				tokens3.Endpoint{
+					ID:        "8",
+					Region:    "different",
+					Interface: "public",
+					URL:       "https://badtype.com/+badregion+badname",
+				},
+			},
+		},
+	},
+}
+
+func TestV3EndpointExact(t *testing.T) {
+	expectedURLs := map[gophercloud.Availability]string{
+		gophercloud.AvailabilityPublic:   "https://public.correct.com/",
+		gophercloud.AvailabilityAdmin:    "https://admin.correct.com/",
+		gophercloud.AvailabilityInternal: "https://internal.correct.com/",
+	}
+
+	for availability, expected := range expectedURLs {
+		actual, err := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{
+			Type:         "same",
+			Name:         "same",
+			Region:       "same",
+			Availability: availability,
+		})
+		th.AssertNoErr(t, err)
+		th.CheckEquals(t, expected, actual)
+	}
+}
+
+func TestV3EndpointNone(t *testing.T) {
+	_, actual := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{
+		Type:         "nope",
+		Availability: gophercloud.AvailabilityPublic,
+	})
+	expected := &gophercloud.ErrEndpointNotFound{}
+	th.CheckEquals(t, expected.Error(), actual.Error())
+}
+
+func TestV3EndpointMultiple(t *testing.T) {
+	_, err := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{
+		Type:         "same",
+		Region:       "same",
+		Availability: gophercloud.AvailabilityPublic,
+	})
+	if !strings.HasPrefix(err.Error(), "Discovered 2 matching endpoints:") {
+		t.Errorf("Received unexpected error: %v", err)
+	}
+}
+
+func TestV3EndpointBadAvailability(t *testing.T) {
+	_, err := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{
+		Type:         "same",
+		Name:         "same",
+		Region:       "same",
+		Availability: "wat",
+	})
+	th.CheckEquals(t, "Unexpected availability in endpoint query: wat", err.Error())
+}
diff --git a/openstack/utils/choose_version.go b/openstack/utils/choose_version.go
index b697ba8..c605d08 100644
--- a/openstack/utils/choose_version.go
+++ b/openstack/utils/choose_version.go
@@ -4,7 +4,7 @@
 	"fmt"
 	"strings"
 
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 // Version is a supported API version, corresponding to a vN package within the appropriate service.
@@ -59,7 +59,7 @@
 	}
 
 	var resp response
-	_, err := client.Request("GET", client.IdentityBase, gophercloud.RequestOpts{
+	_, err := client.Request("GET", client.IdentityBase, &gophercloud.RequestOpts{
 		JSONResponse: &resp,
 		OkCodes:      []int{200, 300},
 	})
diff --git a/openstack/utils/choose_version_test.go b/openstack/utils/choose_version_test.go
deleted file mode 100644
index 388d689..0000000
--- a/openstack/utils/choose_version_test.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package utils
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/testhelper"
-)
-
-func setupVersionHandler() {
-	testhelper.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		fmt.Fprintf(w, `
-			{
-				"versions": {
-					"values": [
-						{
-							"status": "stable",
-							"id": "v3.0",
-							"links": [
-								{ "href": "%s/v3.0", "rel": "self" }
-							]
-						},
-						{
-							"status": "stable",
-							"id": "v2.0",
-							"links": [
-								{ "href": "%s/v2.0", "rel": "self" }
-							]
-						}
-					]
-				}
-			}
-		`, testhelper.Server.URL, testhelper.Server.URL)
-	})
-}
-
-func TestChooseVersion(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-	setupVersionHandler()
-
-	v2 := &Version{ID: "v2.0", Priority: 2, Suffix: "blarg"}
-	v3 := &Version{ID: "v3.0", Priority: 3, Suffix: "hargl"}
-
-	c := &gophercloud.ProviderClient{
-		IdentityBase:     testhelper.Endpoint(),
-		IdentityEndpoint: "",
-	}
-	v, endpoint, err := ChooseVersion(c, []*Version{v2, v3})
-
-	if err != nil {
-		t.Fatalf("Unexpected error from ChooseVersion: %v", err)
-	}
-
-	if v != v3 {
-		t.Errorf("Expected %#v to win, but %#v did instead", v3, v)
-	}
-
-	expected := testhelper.Endpoint() + "v3.0/"
-	if endpoint != expected {
-		t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint)
-	}
-}
-
-func TestChooseVersionOpinionatedLink(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-	setupVersionHandler()
-
-	v2 := &Version{ID: "v2.0", Priority: 2, Suffix: "nope"}
-	v3 := &Version{ID: "v3.0", Priority: 3, Suffix: "northis"}
-
-	c := &gophercloud.ProviderClient{
-		IdentityBase:     testhelper.Endpoint(),
-		IdentityEndpoint: testhelper.Endpoint() + "v2.0/",
-	}
-	v, endpoint, err := ChooseVersion(c, []*Version{v2, v3})
-	if err != nil {
-		t.Fatalf("Unexpected error from ChooseVersion: %v", err)
-	}
-
-	if v != v2 {
-		t.Errorf("Expected %#v to win, but %#v did instead", v2, v)
-	}
-
-	expected := testhelper.Endpoint() + "v2.0/"
-	if endpoint != expected {
-		t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint)
-	}
-}
-
-func TestChooseVersionFromSuffix(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
-
-	v2 := &Version{ID: "v2.0", Priority: 2, Suffix: "/v2.0/"}
-	v3 := &Version{ID: "v3.0", Priority: 3, Suffix: "/v3.0/"}
-
-	c := &gophercloud.ProviderClient{
-		IdentityBase:     testhelper.Endpoint(),
-		IdentityEndpoint: testhelper.Endpoint() + "v2.0/",
-	}
-	v, endpoint, err := ChooseVersion(c, []*Version{v2, v3})
-	if err != nil {
-		t.Fatalf("Unexpected error from ChooseVersion: %v", err)
-	}
-
-	if v != v2 {
-		t.Errorf("Expected %#v to win, but %#v did instead", v2, v)
-	}
-
-	expected := testhelper.Endpoint() + "v2.0/"
-	if endpoint != expected {
-		t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint)
-	}
-}
diff --git a/openstack/utils/testing/choose_version_test.go b/openstack/utils/testing/choose_version_test.go
new file mode 100644
index 0000000..9c0119c
--- /dev/null
+++ b/openstack/utils/testing/choose_version_test.go
@@ -0,0 +1,119 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/utils"
+	"github.com/gophercloud/gophercloud/testhelper"
+)
+
+func setupVersionHandler() {
+	testhelper.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, `
+			{
+				"versions": {
+					"values": [
+						{
+							"status": "stable",
+							"id": "v3.0",
+							"links": [
+								{ "href": "%s/v3.0", "rel": "self" }
+							]
+						},
+						{
+							"status": "stable",
+							"id": "v2.0",
+							"links": [
+								{ "href": "%s/v2.0", "rel": "self" }
+							]
+						}
+					]
+				}
+			}
+		`, testhelper.Server.URL, testhelper.Server.URL)
+	})
+}
+
+func TestChooseVersion(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	setupVersionHandler()
+
+	v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "blarg"}
+	v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "hargl"}
+
+	c := &gophercloud.ProviderClient{
+		IdentityBase:     testhelper.Endpoint(),
+		IdentityEndpoint: "",
+	}
+	v, endpoint, err := utils.ChooseVersion(c, []*utils.Version{v2, v3})
+
+	if err != nil {
+		t.Fatalf("Unexpected error from ChooseVersion: %v", err)
+	}
+
+	if v != v3 {
+		t.Errorf("Expected %#v to win, but %#v did instead", v3, v)
+	}
+
+	expected := testhelper.Endpoint() + "v3.0/"
+	if endpoint != expected {
+		t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint)
+	}
+}
+
+func TestChooseVersionOpinionatedLink(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	setupVersionHandler()
+
+	v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "nope"}
+	v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "northis"}
+
+	c := &gophercloud.ProviderClient{
+		IdentityBase:     testhelper.Endpoint(),
+		IdentityEndpoint: testhelper.Endpoint() + "v2.0/",
+	}
+	v, endpoint, err := utils.ChooseVersion(c, []*utils.Version{v2, v3})
+	if err != nil {
+		t.Fatalf("Unexpected error from ChooseVersion: %v", err)
+	}
+
+	if v != v2 {
+		t.Errorf("Expected %#v to win, but %#v did instead", v2, v)
+	}
+
+	expected := testhelper.Endpoint() + "v2.0/"
+	if endpoint != expected {
+		t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint)
+	}
+}
+
+func TestChooseVersionFromSuffix(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "/v2.0/"}
+	v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "/v3.0/"}
+
+	c := &gophercloud.ProviderClient{
+		IdentityBase:     testhelper.Endpoint(),
+		IdentityEndpoint: testhelper.Endpoint() + "v2.0/",
+	}
+	v, endpoint, err := utils.ChooseVersion(c, []*utils.Version{v2, v3})
+	if err != nil {
+		t.Fatalf("Unexpected error from ChooseVersion: %v", err)
+	}
+
+	if v != v2 {
+		t.Errorf("Expected %#v to win, but %#v did instead", v2, v)
+	}
+
+	expected := testhelper.Endpoint() + "v2.0/"
+	if endpoint != expected {
+		t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint)
+	}
+}
diff --git a/openstack/utils/testing/doc.go b/openstack/utils/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/utils/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/pagination/http.go b/pagination/http.go
index 1b3fe94..cb4b4ae 100644
--- a/pagination/http.go
+++ b/pagination/http.go
@@ -7,7 +7,7 @@
 	"net/url"
 	"strings"
 
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 // PageResult stores the HTTP response that returned the current page of results.
@@ -53,7 +53,7 @@
 
 // Request performs an HTTP request and extracts the http.Response from the result.
 func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) {
-	return client.Request("GET", url, gophercloud.RequestOpts{
+	return client.Get(url, nil, &gophercloud.RequestOpts{
 		MoreHeaders: headers,
 		OkCodes:     []int{200, 204},
 	})
diff --git a/pagination/linked.go b/pagination/linked.go
index e9bd8de..3656fb7 100644
--- a/pagination/linked.go
+++ b/pagination/linked.go
@@ -1,6 +1,11 @@
 package pagination
 
-import "fmt"
+import (
+	"fmt"
+	"reflect"
+
+	"github.com/gophercloud/gophercloud"
+)
 
 // LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result.
 type LinkedPageBase struct {
@@ -28,7 +33,10 @@
 
 	submap, ok := current.Body.(map[string]interface{})
 	if !ok {
-		return "", fmt.Errorf("Expected an object, but was %#v", current.Body)
+		err := gophercloud.ErrUnexpectedType{}
+		err.Expected = "map[string]interface{}"
+		err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body))
+		return "", err
 	}
 
 	for {
@@ -42,7 +50,10 @@
 		if len(path) > 0 {
 			submap, ok = value.(map[string]interface{})
 			if !ok {
-				return "", fmt.Errorf("Expected an object, but was %#v", value)
+				err := gophercloud.ErrUnexpectedType{}
+				err.Expected = "map[string]interface{}"
+				err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value))
+				return "", err
 			}
 		} else {
 			if value == nil {
@@ -52,7 +63,10 @@
 
 			url, ok := value.(string)
 			if !ok {
-				return "", fmt.Errorf("Expected a string, but was %#v", value)
+				err := gophercloud.ErrUnexpectedType{}
+				err.Expected = "string"
+				err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value))
+				return "", err
 			}
 
 			return url, nil
@@ -60,6 +74,17 @@
 	}
 }
 
+// IsEmpty satisifies the IsEmpty method of the Page interface
+func (current LinkedPageBase) IsEmpty() (bool, error) {
+	if b, ok := current.Body.([]interface{}); ok {
+		return len(b) == 0, nil
+	}
+	err := gophercloud.ErrUnexpectedType{}
+	err.Expected = "[]interface{}"
+	err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body))
+	return true, err
+}
+
 // GetBody returns the linked page's body. This method is needed to satisfy the
 // Page interface.
 func (current LinkedPageBase) GetBody() interface{} {
diff --git a/pagination/linked_test.go b/pagination/linked_test.go
deleted file mode 100644
index 1ac0f73..0000000
--- a/pagination/linked_test.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package pagination
-
-import (
-	"fmt"
-	"net/http"
-	"reflect"
-	"testing"
-
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud/testhelper"
-)
-
-// LinkedPager sample and test cases.
-
-type LinkedPageResult struct {
-	LinkedPageBase
-}
-
-func (r LinkedPageResult) IsEmpty() (bool, error) {
-	is, err := ExtractLinkedInts(r)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
-}
-
-func ExtractLinkedInts(page Page) ([]int, error) {
-	var response struct {
-		Ints []int `mapstructure:"ints"`
-	}
-
-	err := mapstructure.Decode(page.(LinkedPageResult).Body, &response)
-	if err != nil {
-		return nil, err
-	}
-
-	return response.Ints, nil
-}
-
-func createLinked(t *testing.T) Pager {
-	testhelper.SetupHTTP()
-
-	testhelper.Mux.HandleFunc("/page1", func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `{ "ints": [1, 2, 3], "links": { "next": "%s/page2" } }`, testhelper.Server.URL)
-	})
-
-	testhelper.Mux.HandleFunc("/page2", func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `{ "ints": [4, 5, 6], "links": { "next": "%s/page3" } }`, testhelper.Server.URL)
-	})
-
-	testhelper.Mux.HandleFunc("/page3", func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `{ "ints": [7, 8, 9], "links": { "next": null } }`)
-	})
-
-	client := createClient()
-
-	createPage := func(r PageResult) Page {
-		return LinkedPageResult{LinkedPageBase{PageResult: r}}
-	}
-
-	return NewPager(client, testhelper.Server.URL+"/page1", createPage)
-}
-
-func TestEnumerateLinked(t *testing.T) {
-	pager := createLinked(t)
-	defer testhelper.TeardownHTTP()
-
-	callCount := 0
-	err := pager.EachPage(func(page Page) (bool, error) {
-		actual, err := ExtractLinkedInts(page)
-		if err != nil {
-			return false, err
-		}
-
-		t.Logf("Handler invoked with %v", actual)
-
-		var expected []int
-		switch callCount {
-		case 0:
-			expected = []int{1, 2, 3}
-		case 1:
-			expected = []int{4, 5, 6}
-		case 2:
-			expected = []int{7, 8, 9}
-		default:
-			t.Fatalf("Unexpected call count: %d", callCount)
-			return false, nil
-		}
-
-		if !reflect.DeepEqual(expected, actual) {
-			t.Errorf("Call %d: Expected %#v, but was %#v", callCount, expected, actual)
-		}
-
-		callCount++
-		return true, nil
-	})
-	if err != nil {
-		t.Errorf("Unexpected error for page iteration: %v", err)
-	}
-
-	if callCount != 3 {
-		t.Errorf("Expected 3 calls, but was %d", callCount)
-	}
-}
-
-func TestAllPagesLinked(t *testing.T) {
-	pager := createLinked(t)
-	defer testhelper.TeardownHTTP()
-
-	page, err := pager.AllPages()
-	testhelper.AssertNoErr(t, err)
-
-	expected := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
-	actual, err := ExtractLinkedInts(page)
-	testhelper.AssertNoErr(t, err)
-	testhelper.CheckDeepEquals(t, expected, actual)
-}
diff --git a/pagination/marker.go b/pagination/marker.go
index f355afc..52e53ba 100644
--- a/pagination/marker.go
+++ b/pagination/marker.go
@@ -1,5 +1,12 @@
 package pagination
 
+import (
+	"fmt"
+	"reflect"
+
+	"github.com/gophercloud/gophercloud"
+)
+
 // MarkerPage is a stricter Page interface that describes additional functionality required for use with NewMarkerPager.
 // For convenience, embed the MarkedPageBase struct.
 type MarkerPage interface {
@@ -33,6 +40,17 @@
 	return currentURL.String(), nil
 }
 
+// IsEmpty satisifies the IsEmpty method of the Page interface
+func (current MarkerPageBase) IsEmpty() (bool, error) {
+	if b, ok := current.Body.([]interface{}); ok {
+		return len(b) == 0, nil
+	}
+	err := gophercloud.ErrUnexpectedType{}
+	err.Expected = "[]interface{}"
+	err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body))
+	return true, err
+}
+
 // GetBody returns the linked page's body. This method is needed to satisfy the
 // Page interface.
 func (current MarkerPageBase) GetBody() interface{} {
diff --git a/pagination/marker_test.go b/pagination/marker_test.go
deleted file mode 100644
index f4d55be..0000000
--- a/pagination/marker_test.go
+++ /dev/null
@@ -1,126 +0,0 @@
-package pagination
-
-import (
-	"fmt"
-	"net/http"
-	"strings"
-	"testing"
-
-	"github.com/rackspace/gophercloud/testhelper"
-)
-
-// MarkerPager sample and test cases.
-
-type MarkerPageResult struct {
-	MarkerPageBase
-}
-
-func (r MarkerPageResult) IsEmpty() (bool, error) {
-	results, err := ExtractMarkerStrings(r)
-	if err != nil {
-		return true, err
-	}
-	return len(results) == 0, err
-}
-
-func (r MarkerPageResult) LastMarker() (string, error) {
-	results, err := ExtractMarkerStrings(r)
-	if err != nil {
-		return "", err
-	}
-	if len(results) == 0 {
-		return "", nil
-	}
-	return results[len(results)-1], nil
-}
-
-func createMarkerPaged(t *testing.T) Pager {
-	testhelper.SetupHTTP()
-
-	testhelper.Mux.HandleFunc("/page", func(w http.ResponseWriter, r *http.Request) {
-		r.ParseForm()
-		ms := r.Form["marker"]
-		switch {
-		case len(ms) == 0:
-			fmt.Fprintf(w, "aaa\nbbb\nccc")
-		case len(ms) == 1 && ms[0] == "ccc":
-			fmt.Fprintf(w, "ddd\neee\nfff")
-		case len(ms) == 1 && ms[0] == "fff":
-			fmt.Fprintf(w, "ggg\nhhh\niii")
-		case len(ms) == 1 && ms[0] == "iii":
-			w.WriteHeader(http.StatusNoContent)
-		default:
-			t.Errorf("Request with unexpected marker: [%v]", ms)
-		}
-	})
-
-	client := createClient()
-
-	createPage := func(r PageResult) Page {
-		p := MarkerPageResult{MarkerPageBase{PageResult: r}}
-		p.MarkerPageBase.Owner = p
-		return p
-	}
-
-	return NewPager(client, testhelper.Server.URL+"/page", createPage)
-}
-
-func ExtractMarkerStrings(page Page) ([]string, error) {
-	content := page.(MarkerPageResult).Body.([]uint8)
-	parts := strings.Split(string(content), "\n")
-	results := make([]string, 0, len(parts))
-	for _, part := range parts {
-		if len(part) > 0 {
-			results = append(results, part)
-		}
-	}
-	return results, nil
-}
-
-func TestEnumerateMarker(t *testing.T) {
-	pager := createMarkerPaged(t)
-	defer testhelper.TeardownHTTP()
-
-	callCount := 0
-	err := pager.EachPage(func(page Page) (bool, error) {
-		actual, err := ExtractMarkerStrings(page)
-		if err != nil {
-			return false, err
-		}
-
-		t.Logf("Handler invoked with %v", actual)
-
-		var expected []string
-		switch callCount {
-		case 0:
-			expected = []string{"aaa", "bbb", "ccc"}
-		case 1:
-			expected = []string{"ddd", "eee", "fff"}
-		case 2:
-			expected = []string{"ggg", "hhh", "iii"}
-		default:
-			t.Fatalf("Unexpected call count: %d", callCount)
-			return false, nil
-		}
-
-		testhelper.CheckDeepEquals(t, expected, actual)
-
-		callCount++
-		return true, nil
-	})
-	testhelper.AssertNoErr(t, err)
-	testhelper.AssertEquals(t, callCount, 3)
-}
-
-func TestAllPagesMarker(t *testing.T) {
-	pager := createMarkerPaged(t)
-	defer testhelper.TeardownHTTP()
-
-	page, err := pager.AllPages()
-	testhelper.AssertNoErr(t, err)
-
-	expected := []string{"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii"}
-	actual, err := ExtractMarkerStrings(page)
-	testhelper.AssertNoErr(t, err)
-	testhelper.CheckDeepEquals(t, expected, actual)
-}
diff --git a/pagination/null.go b/pagination/null.go
deleted file mode 100644
index ae57e18..0000000
--- a/pagination/null.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package pagination
-
-// nullPage is an always-empty page that trivially satisfies all Page interfacts.
-// It's useful to be returned along with an error.
-type nullPage struct{}
-
-// NextPageURL always returns "" to indicate that there are no more pages to return.
-func (p nullPage) NextPageURL() (string, error) {
-	return "", nil
-}
-
-// IsEmpty always returns true to prevent iteration over nullPages.
-func (p nullPage) IsEmpty() (bool, error) {
-	return true, nil
-}
-
-// LastMark always returns "" because the nullPage contains no items to have a mark.
-func (p nullPage) LastMark() (string, error) {
-	return "", nil
-}
diff --git a/pagination/pager.go b/pagination/pager.go
index 6f1ca04..1b5192a 100644
--- a/pagination/pager.go
+++ b/pagination/pager.go
@@ -7,7 +7,7 @@
 	"reflect"
 	"strings"
 
-	"github.com/rackspace/gophercloud"
+	"github.com/gophercloud/gophercloud"
 )
 
 var (
@@ -150,7 +150,7 @@
 		// key is the map key for the page body if the body type is `map[string]interface{}`.
 		var key string
 		// Iterate over the pages to concatenate the bodies.
-		err := p.EachPage(func(page Page) (bool, error) {
+		err = p.EachPage(func(page Page) (bool, error) {
 			b := page.GetBody().(map[string]interface{})
 			for k := range b {
 				// If it's a linked page, we don't want the `links`, we want the other one.
@@ -176,7 +176,7 @@
 		body.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(pagesSlice))
 	case []byte:
 		// Iterate over the pages to concatenate the bodies.
-		err := p.EachPage(func(page Page) (bool, error) {
+		err = p.EachPage(func(page Page) (bool, error) {
 			b := page.GetBody().([]byte)
 			pagesSlice = append(pagesSlice, b)
 			// seperate pages with a comma
@@ -200,7 +200,7 @@
 		body.SetBytes(b)
 	case []interface{}:
 		// Iterate over the pages to concatenate the bodies.
-		err := p.EachPage(func(page Page) (bool, error) {
+		err = p.EachPage(func(page Page) (bool, error) {
 			b := page.GetBody().([]interface{})
 			pagesSlice = append(pagesSlice, b...)
 			return true, nil
@@ -214,7 +214,10 @@
 			body.Index(i).Set(reflect.ValueOf(s))
 		}
 	default:
-		return nil, fmt.Errorf("Page body has unrecognized type.")
+		err := gophercloud.ErrUnexpectedType{}
+		err.Expected = "map[string]interface{}/[]byte/[]interface{}"
+		err.Actual = fmt.Sprintf("%v", reflect.TypeOf(testPage.GetBody()))
+		return nil, err
 	}
 
 	// Each `Extract*` function is expecting a specific type of page coming back,
diff --git a/pagination/pagination_test.go b/pagination/pagination_test.go
deleted file mode 100644
index f3e4de1..0000000
--- a/pagination/pagination_test.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package pagination
-
-import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/testhelper"
-)
-
-func createClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{
-		ProviderClient: &gophercloud.ProviderClient{TokenID: "abc123"},
-		Endpoint:       testhelper.Endpoint(),
-	}
-}
diff --git a/pagination/single.go b/pagination/single.go
index f78d4ab..4251d64 100644
--- a/pagination/single.go
+++ b/pagination/single.go
@@ -1,5 +1,12 @@
 package pagination
 
+import (
+	"fmt"
+	"reflect"
+
+	"github.com/gophercloud/gophercloud"
+)
+
 // SinglePageBase may be embedded in a Page that contains all of the results from an operation at once.
 type SinglePageBase PageResult
 
@@ -8,6 +15,17 @@
 	return "", nil
 }
 
+// IsEmpty satisifies the IsEmpty method of the Page interface
+func (current SinglePageBase) IsEmpty() (bool, error) {
+	if b, ok := current.Body.([]interface{}); ok {
+		return len(b) == 0, nil
+	}
+	err := gophercloud.ErrUnexpectedType{}
+	err.Expected = "[]interface{}"
+	err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body))
+	return true, err
+}
+
 // GetBody returns the single page's body. This method is needed to satisfy the
 // Page interface.
 func (current SinglePageBase) GetBody() interface{} {
diff --git a/pagination/single_test.go b/pagination/single_test.go
deleted file mode 100644
index 4af0fee..0000000
--- a/pagination/single_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package pagination
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud/testhelper"
-)
-
-// SinglePage sample and test cases.
-
-type SinglePageResult struct {
-	SinglePageBase
-}
-
-func (r SinglePageResult) IsEmpty() (bool, error) {
-	is, err := ExtractSingleInts(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
-}
-
-func ExtractSingleInts(page Page) ([]int, error) {
-	var response struct {
-		Ints []int `mapstructure:"ints"`
-	}
-
-	err := mapstructure.Decode(page.(SinglePageResult).Body, &response)
-	if err != nil {
-		return nil, err
-	}
-
-	return response.Ints, nil
-}
-
-func setupSinglePaged() Pager {
-	testhelper.SetupHTTP()
-	client := createClient()
-
-	testhelper.Mux.HandleFunc("/only", func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, `{ "ints": [1, 2, 3] }`)
-	})
-
-	createPage := func(r PageResult) Page {
-		return SinglePageResult{SinglePageBase(r)}
-	}
-
-	return NewPager(client, testhelper.Server.URL+"/only", createPage)
-}
-
-func TestEnumerateSinglePaged(t *testing.T) {
-	callCount := 0
-	pager := setupSinglePaged()
-	defer testhelper.TeardownHTTP()
-
-	err := pager.EachPage(func(page Page) (bool, error) {
-		callCount++
-
-		expected := []int{1, 2, 3}
-		actual, err := ExtractSingleInts(page)
-		testhelper.AssertNoErr(t, err)
-		testhelper.CheckDeepEquals(t, expected, actual)
-		return true, nil
-	})
-	testhelper.CheckNoErr(t, err)
-	testhelper.CheckEquals(t, 1, callCount)
-}
-
-func TestAllPagesSingle(t *testing.T) {
-	pager := setupSinglePaged()
-	defer testhelper.TeardownHTTP()
-
-	page, err := pager.AllPages()
-	testhelper.AssertNoErr(t, err)
-
-	expected := []int{1, 2, 3}
-	actual, err := ExtractSingleInts(page)
-	testhelper.AssertNoErr(t, err)
-	testhelper.CheckDeepEquals(t, expected, actual)
-}
diff --git a/pagination/testing/doc.go b/pagination/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/pagination/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/pagination/testing/linked_test.go b/pagination/testing/linked_test.go
new file mode 100644
index 0000000..3533e44
--- /dev/null
+++ b/pagination/testing/linked_test.go
@@ -0,0 +1,112 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud/testhelper"
+)
+
+// LinkedPager sample and test cases.
+
+type LinkedPageResult struct {
+	pagination.LinkedPageBase
+}
+
+func (r LinkedPageResult) IsEmpty() (bool, error) {
+	is, err := ExtractLinkedInts(r)
+	return len(is) == 0, err
+}
+
+func ExtractLinkedInts(r pagination.Page) ([]int, error) {
+	var s struct {
+		Ints []int `json:"ints"`
+	}
+	err := (r.(LinkedPageResult)).ExtractInto(&s)
+	return s.Ints, err
+}
+
+func createLinked(t *testing.T) pagination.Pager {
+	testhelper.SetupHTTP()
+
+	testhelper.Mux.HandleFunc("/page1", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `{ "ints": [1, 2, 3], "links": { "next": "%s/page2" } }`, testhelper.Server.URL)
+	})
+
+	testhelper.Mux.HandleFunc("/page2", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `{ "ints": [4, 5, 6], "links": { "next": "%s/page3" } }`, testhelper.Server.URL)
+	})
+
+	testhelper.Mux.HandleFunc("/page3", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `{ "ints": [7, 8, 9], "links": { "next": null } }`)
+	})
+
+	client := createClient()
+
+	createPage := func(r pagination.PageResult) pagination.Page {
+		return LinkedPageResult{pagination.LinkedPageBase{PageResult: r}}
+	}
+
+	return pagination.NewPager(client, testhelper.Server.URL+"/page1", createPage)
+}
+
+func TestEnumerateLinked(t *testing.T) {
+	pager := createLinked(t)
+	defer testhelper.TeardownHTTP()
+
+	callCount := 0
+	err := pager.EachPage(func(page pagination.Page) (bool, error) {
+		actual, err := ExtractLinkedInts(page)
+		if err != nil {
+			return false, err
+		}
+
+		t.Logf("Handler invoked with %v", actual)
+
+		var expected []int
+		switch callCount {
+		case 0:
+			expected = []int{1, 2, 3}
+		case 1:
+			expected = []int{4, 5, 6}
+		case 2:
+			expected = []int{7, 8, 9}
+		default:
+			t.Fatalf("Unexpected call count: %d", callCount)
+			return false, nil
+		}
+
+		if !reflect.DeepEqual(expected, actual) {
+			t.Errorf("Call %d: Expected %#v, but was %#v", callCount, expected, actual)
+		}
+
+		callCount++
+		return true, nil
+	})
+	if err != nil {
+		t.Errorf("Unexpected error for page iteration: %v", err)
+	}
+
+	if callCount != 3 {
+		t.Errorf("Expected 3 calls, but was %d", callCount)
+	}
+}
+
+func TestAllPagesLinked(t *testing.T) {
+	pager := createLinked(t)
+	defer testhelper.TeardownHTTP()
+
+	page, err := pager.AllPages()
+	testhelper.AssertNoErr(t, err)
+
+	expected := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
+	actual, err := ExtractLinkedInts(page)
+	testhelper.AssertNoErr(t, err)
+	testhelper.CheckDeepEquals(t, expected, actual)
+}
diff --git a/pagination/testing/marker_test.go b/pagination/testing/marker_test.go
new file mode 100644
index 0000000..7b1a6da
--- /dev/null
+++ b/pagination/testing/marker_test.go
@@ -0,0 +1,127 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud/testhelper"
+)
+
+// MarkerPager sample and test cases.
+
+type MarkerPageResult struct {
+	pagination.MarkerPageBase
+}
+
+func (r MarkerPageResult) IsEmpty() (bool, error) {
+	results, err := ExtractMarkerStrings(r)
+	if err != nil {
+		return true, err
+	}
+	return len(results) == 0, err
+}
+
+func (r MarkerPageResult) LastMarker() (string, error) {
+	results, err := ExtractMarkerStrings(r)
+	if err != nil {
+		return "", err
+	}
+	if len(results) == 0 {
+		return "", nil
+	}
+	return results[len(results)-1], nil
+}
+
+func createMarkerPaged(t *testing.T) pagination.Pager {
+	testhelper.SetupHTTP()
+
+	testhelper.Mux.HandleFunc("/page", func(w http.ResponseWriter, r *http.Request) {
+		r.ParseForm()
+		ms := r.Form["marker"]
+		switch {
+		case len(ms) == 0:
+			fmt.Fprintf(w, "aaa\nbbb\nccc")
+		case len(ms) == 1 && ms[0] == "ccc":
+			fmt.Fprintf(w, "ddd\neee\nfff")
+		case len(ms) == 1 && ms[0] == "fff":
+			fmt.Fprintf(w, "ggg\nhhh\niii")
+		case len(ms) == 1 && ms[0] == "iii":
+			w.WriteHeader(http.StatusNoContent)
+		default:
+			t.Errorf("Request with unexpected marker: [%v]", ms)
+		}
+	})
+
+	client := createClient()
+
+	createPage := func(r pagination.PageResult) pagination.Page {
+		p := MarkerPageResult{pagination.MarkerPageBase{PageResult: r}}
+		p.MarkerPageBase.Owner = p
+		return p
+	}
+
+	return pagination.NewPager(client, testhelper.Server.URL+"/page", createPage)
+}
+
+func ExtractMarkerStrings(page pagination.Page) ([]string, error) {
+	content := page.(MarkerPageResult).Body.([]uint8)
+	parts := strings.Split(string(content), "\n")
+	results := make([]string, 0, len(parts))
+	for _, part := range parts {
+		if len(part) > 0 {
+			results = append(results, part)
+		}
+	}
+	return results, nil
+}
+
+func TestEnumerateMarker(t *testing.T) {
+	pager := createMarkerPaged(t)
+	defer testhelper.TeardownHTTP()
+
+	callCount := 0
+	err := pager.EachPage(func(page pagination.Page) (bool, error) {
+		actual, err := ExtractMarkerStrings(page)
+		if err != nil {
+			return false, err
+		}
+
+		t.Logf("Handler invoked with %v", actual)
+
+		var expected []string
+		switch callCount {
+		case 0:
+			expected = []string{"aaa", "bbb", "ccc"}
+		case 1:
+			expected = []string{"ddd", "eee", "fff"}
+		case 2:
+			expected = []string{"ggg", "hhh", "iii"}
+		default:
+			t.Fatalf("Unexpected call count: %d", callCount)
+			return false, nil
+		}
+
+		testhelper.CheckDeepEquals(t, expected, actual)
+
+		callCount++
+		return true, nil
+	})
+	testhelper.AssertNoErr(t, err)
+	testhelper.AssertEquals(t, callCount, 3)
+}
+
+func TestAllPagesMarker(t *testing.T) {
+	pager := createMarkerPaged(t)
+	defer testhelper.TeardownHTTP()
+
+	page, err := pager.AllPages()
+	testhelper.AssertNoErr(t, err)
+
+	expected := []string{"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii"}
+	actual, err := ExtractMarkerStrings(page)
+	testhelper.AssertNoErr(t, err)
+	testhelper.CheckDeepEquals(t, expected, actual)
+}
diff --git a/pagination/testing/pagination_test.go b/pagination/testing/pagination_test.go
new file mode 100644
index 0000000..170dca4
--- /dev/null
+++ b/pagination/testing/pagination_test.go
@@ -0,0 +1,13 @@
+package testing
+
+import (
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/testhelper"
+)
+
+func createClient() *gophercloud.ServiceClient {
+	return &gophercloud.ServiceClient{
+		ProviderClient: &gophercloud.ProviderClient{TokenID: "abc123"},
+		Endpoint:       testhelper.Endpoint(),
+	}
+}
diff --git a/pagination/testing/single_test.go b/pagination/testing/single_test.go
new file mode 100644
index 0000000..8d95e94
--- /dev/null
+++ b/pagination/testing/single_test.go
@@ -0,0 +1,79 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/pagination"
+	"github.com/gophercloud/gophercloud/testhelper"
+)
+
+// SinglePage sample and test cases.
+
+type SinglePageResult struct {
+	pagination.SinglePageBase
+}
+
+func (r SinglePageResult) IsEmpty() (bool, error) {
+	is, err := ExtractSingleInts(r)
+	if err != nil {
+		return true, err
+	}
+	return len(is) == 0, nil
+}
+
+func ExtractSingleInts(r pagination.Page) ([]int, error) {
+	var s struct {
+		Ints []int `json:"ints"`
+	}
+	err := (r.(SinglePageResult)).ExtractInto(&s)
+	return s.Ints, err
+}
+
+func setupSinglePaged() pagination.Pager {
+	testhelper.SetupHTTP()
+	client := createClient()
+
+	testhelper.Mux.HandleFunc("/only", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `{ "ints": [1, 2, 3] }`)
+	})
+
+	createPage := func(r pagination.PageResult) pagination.Page {
+		return SinglePageResult{pagination.SinglePageBase(r)}
+	}
+
+	return pagination.NewPager(client, testhelper.Server.URL+"/only", createPage)
+}
+
+func TestEnumerateSinglePaged(t *testing.T) {
+	callCount := 0
+	pager := setupSinglePaged()
+	defer testhelper.TeardownHTTP()
+
+	err := pager.EachPage(func(page pagination.Page) (bool, error) {
+		callCount++
+
+		expected := []int{1, 2, 3}
+		actual, err := ExtractSingleInts(page)
+		testhelper.AssertNoErr(t, err)
+		testhelper.CheckDeepEquals(t, expected, actual)
+		return true, nil
+	})
+	testhelper.CheckNoErr(t, err)
+	testhelper.CheckEquals(t, 1, callCount)
+}
+
+func TestAllPagesSingle(t *testing.T) {
+	pager := setupSinglePaged()
+	defer testhelper.TeardownHTTP()
+
+	page, err := pager.AllPages()
+	testhelper.AssertNoErr(t, err)
+
+	expected := []int{1, 2, 3}
+	actual, err := ExtractSingleInts(page)
+	testhelper.AssertNoErr(t, err)
+	testhelper.CheckDeepEquals(t, expected, actual)
+}
diff --git a/params.go b/params.go
index 4d0f1e6..b7f9508 100644
--- a/params.go
+++ b/params.go
@@ -1,6 +1,7 @@
 package gophercloud
 
 import (
+	"encoding/json"
 	"fmt"
 	"net/url"
 	"reflect"
@@ -9,6 +10,146 @@
 	"time"
 )
 
+// BuildRequestBody builds a map[string]interface from the given `struct`. If
+// parent is not the empty string, the final map[string]interface returned will
+// encapsulate the built one
+//
+func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) {
+	optsValue := reflect.ValueOf(opts)
+	if optsValue.Kind() == reflect.Ptr {
+		optsValue = optsValue.Elem()
+	}
+
+	optsType := reflect.TypeOf(opts)
+	if optsType.Kind() == reflect.Ptr {
+		optsType = optsType.Elem()
+	}
+
+	optsMap := make(map[string]interface{})
+	if optsValue.Kind() == reflect.Struct {
+		//fmt.Printf("optsValue.Kind() is a reflect.Struct: %+v\n", optsValue.Kind())
+		for i := 0; i < optsValue.NumField(); i++ {
+			v := optsValue.Field(i)
+			f := optsType.Field(i)
+
+			if f.Name != strings.Title(f.Name) {
+				//fmt.Printf("Skipping field: %s...\n", f.Name)
+				continue
+			}
+
+			//fmt.Printf("Starting on field: %s...\n", f.Name)
+
+			zero := isZero(v)
+			//fmt.Printf("v is zero?: %v\n", zero)
+
+			// if the field has a required tag that's set to "true"
+			if requiredTag := f.Tag.Get("required"); requiredTag == "true" {
+				//fmt.Printf("Checking required field [%s]:\n\tv: %+v\n\tisZero:%v\n", f.Name, v.Interface(), zero)
+				// if the field's value is zero, return a missing-argument error
+				if zero {
+					// if the field has a 'required' tag, it can't have a zero-value
+					err := ErrMissingInput{}
+					err.Argument = f.Name
+					return nil, err
+				}
+			}
+
+			if xorTag := f.Tag.Get("xor"); xorTag != "" {
+				//fmt.Printf("Checking `xor` tag for field [%s] with value %+v:\n\txorTag: %s\n", f.Name, v, xorTag)
+				xorField := optsValue.FieldByName(xorTag)
+				var xorFieldIsZero bool
+				if reflect.ValueOf(xorField.Interface()) == reflect.Zero(xorField.Type()) {
+					xorFieldIsZero = true
+				} else {
+					if xorField.Kind() == reflect.Ptr {
+						xorField = xorField.Elem()
+					}
+					xorFieldIsZero = isZero(xorField)
+				}
+				if !(zero != xorFieldIsZero) {
+					err := ErrMissingInput{}
+					err.Argument = fmt.Sprintf("%s/%s", f.Name, xorTag)
+					err.Info = fmt.Sprintf("Exactly one of %s and %s must be provided", f.Name, xorTag)
+					return nil, err
+				}
+			}
+
+			if orTag := f.Tag.Get("or"); orTag != "" {
+				//fmt.Printf("Checking `or` tag for field with:\n\tname: %+v\n\torTag:%s\n", f.Name, orTag)
+				//fmt.Printf("field is zero?: %v\n", zero)
+				if zero {
+					orField := optsValue.FieldByName(orTag)
+					var orFieldIsZero bool
+					if reflect.ValueOf(orField.Interface()) == reflect.Zero(orField.Type()) {
+						orFieldIsZero = true
+					} else {
+						if orField.Kind() == reflect.Ptr {
+							orField = orField.Elem()
+						}
+						orFieldIsZero = isZero(orField)
+					}
+					if orFieldIsZero {
+						err := ErrMissingInput{}
+						err.Argument = fmt.Sprintf("%s/%s", f.Name, orTag)
+						err.Info = fmt.Sprintf("At least one of %s and %s must be provided", f.Name, orTag)
+						return nil, err
+					}
+				}
+			}
+
+			if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) {
+				if zero {
+					//fmt.Printf("value before change: %+v\n", optsValue.Field(i))
+					if jsonTag := f.Tag.Get("json"); jsonTag != "" {
+						jsonTagPieces := strings.Split(jsonTag, ",")
+						if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" {
+							if v.CanSet() {
+								if !v.IsNil() {
+									if v.Kind() == reflect.Ptr {
+										v.Set(reflect.Zero(v.Type()))
+									}
+								}
+								//fmt.Printf("value after change: %+v\n", optsValue.Field(i))
+							}
+						}
+					}
+					continue
+				}
+
+				//fmt.Printf("Calling BuildRequestBody with:\n\tv: %+v\n\tf.Name:%s\n", v.Interface(), f.Name)
+				_, err := BuildRequestBody(v.Interface(), f.Name)
+				if err != nil {
+					return nil, err
+				}
+			}
+		}
+
+		//fmt.Printf("opts: %+v \n", opts)
+
+		b, err := json.Marshal(opts)
+		if err != nil {
+			return nil, err
+		}
+
+		//fmt.Printf("string(b): %s\n", string(b))
+
+		err = json.Unmarshal(b, &optsMap)
+		if err != nil {
+			return nil, err
+		}
+
+		//fmt.Printf("optsMap: %+v\n", optsMap)
+
+		if parent != "" {
+			optsMap = map[string]interface{}{parent: optsMap}
+		}
+		//fmt.Printf("optsMap after parent added: %+v\n", optsMap)
+		return optsMap, nil
+	}
+	// Return an error if the underlying type of 'opts' isn't a struct.
+	return nil, fmt.Errorf("Options type is not a struct.")
+}
+
 // EnabledState is a convenience type, mostly used in Create and Update
 // operations. Because the zero value of a bool is FALSE, we need to use a
 // pointer instead to indicate zero-ness.
@@ -23,6 +164,17 @@
 	Disabled EnabledState = &iFalse
 )
 
+// IPVersion is a type for the possible IP address versions. Valid instances
+// are IPv4 and IPv6
+type IPVersion int
+
+const (
+	// IPv4 is used for IP version 4 addresses
+	IPv4 IPVersion = 4
+	// IPv6 is used for IP version 6 addresses
+	IPv6 IPVersion = 6
+)
+
 // IntToPointer is a function for converting integers into integer pointers.
 // This is useful when passing in options to operations.
 func IntToPointer(i int) *int {
@@ -60,10 +212,27 @@
 	return nil
 }
 
+/*
+func isUnderlyingStructZero(v reflect.Value) bool {
+	switch v.Kind() {
+	case reflect.Ptr:
+		return isUnderlyingStructZero(v.Elem())
+	default:
+		return isZero(v)
+	}
+}
+*/
+
 var t time.Time
 
 func isZero(v reflect.Value) bool {
+	//fmt.Printf("\n\nchecking isZero for value: %+v\n", v)
 	switch v.Kind() {
+	case reflect.Ptr:
+		if v.IsNil() {
+			return true
+		}
+		return isZero(v.Elem())
 	case reflect.Func, reflect.Map, reflect.Slice:
 		return v.IsNil()
 	case reflect.Array:
@@ -87,6 +256,7 @@
 	}
 	// Compare other types directly:
 	z := reflect.Zero(v.Type())
+	//fmt.Printf("zero type for value: %+v\n\n\n", z)
 	return v.Interface() == z.Interface()
 }
 
diff --git a/params_test.go b/params_test.go
deleted file mode 100644
index 2f40eec..0000000
--- a/params_test.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package gophercloud
-
-import (
-	"net/url"
-	"reflect"
-	"testing"
-	"time"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestMaybeString(t *testing.T) {
-	testString := ""
-	var expected *string
-	actual := MaybeString(testString)
-	th.CheckDeepEquals(t, expected, actual)
-
-	testString = "carol"
-	expected = &testString
-	actual = MaybeString(testString)
-	th.CheckDeepEquals(t, expected, actual)
-}
-
-func TestMaybeInt(t *testing.T) {
-	testInt := 0
-	var expected *int
-	actual := MaybeInt(testInt)
-	th.CheckDeepEquals(t, expected, actual)
-
-	testInt = 4
-	expected = &testInt
-	actual = MaybeInt(testInt)
-	th.CheckDeepEquals(t, expected, actual)
-}
-
-func TestBuildQueryString(t *testing.T) {
-	type testVar string
-	opts := struct {
-		J  int       `q:"j"`
-		R  string    `q:"r,required"`
-		C  bool      `q:"c"`
-		S  []string  `q:"s"`
-		TS []testVar `q:"ts"`
-		TI []int     `q:"ti"`
-	}{
-		J:  2,
-		R:  "red",
-		C:  true,
-		S:  []string{"one", "two", "three"},
-		TS: []testVar{"a", "b"},
-		TI: []int{1, 2},
-	}
-	expected := &url.URL{RawQuery: "c=true&j=2&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"}
-	actual, err := BuildQueryString(&opts)
-	if err != nil {
-		t.Errorf("Error building query string: %v", err)
-	}
-	th.CheckDeepEquals(t, expected, actual)
-
-	opts = struct {
-		J  int       `q:"j"`
-		R  string    `q:"r,required"`
-		C  bool      `q:"c"`
-		S  []string  `q:"s"`
-		TS []testVar `q:"ts"`
-		TI []int     `q:"ti"`
-	}{
-		J: 2,
-		C: true,
-	}
-	_, err = BuildQueryString(&opts)
-	if err == nil {
-		t.Errorf("Expected error: 'Required field not set'")
-	}
-	th.CheckDeepEquals(t, expected, actual)
-
-	_, err = BuildQueryString(map[string]interface{}{"Number": 4})
-	if err == nil {
-		t.Errorf("Expected error: 'Options type is not a struct'")
-	}
-}
-
-func TestBuildHeaders(t *testing.T) {
-	testStruct := struct {
-		Accept string `h:"Accept"`
-		Num    int    `h:"Number,required"`
-		Style  bool   `h:"Style"`
-	}{
-		Accept: "application/json",
-		Num:    4,
-		Style:  true,
-	}
-	expected := map[string]string{"Accept": "application/json", "Number": "4", "Style": "true"}
-	actual, err := BuildHeaders(&testStruct)
-	th.CheckNoErr(t, err)
-	th.CheckDeepEquals(t, expected, actual)
-
-	testStruct.Num = 0
-	_, err = BuildHeaders(&testStruct)
-	if err == nil {
-		t.Errorf("Expected error: 'Required header not set'")
-	}
-
-	_, err = BuildHeaders(map[string]interface{}{"Number": 4})
-	if err == nil {
-		t.Errorf("Expected error: 'Options type is not a struct'")
-	}
-}
-
-func TestIsZero(t *testing.T) {
-	var testMap map[string]interface{}
-	testMapValue := reflect.ValueOf(testMap)
-	expected := true
-	actual := isZero(testMapValue)
-	th.CheckEquals(t, expected, actual)
-	testMap = map[string]interface{}{"empty": false}
-	testMapValue = reflect.ValueOf(testMap)
-	expected = false
-	actual = isZero(testMapValue)
-	th.CheckEquals(t, expected, actual)
-
-	var testArray [2]string
-	testArrayValue := reflect.ValueOf(testArray)
-	expected = true
-	actual = isZero(testArrayValue)
-	th.CheckEquals(t, expected, actual)
-	testArray = [2]string{"one", "two"}
-	testArrayValue = reflect.ValueOf(testArray)
-	expected = false
-	actual = isZero(testArrayValue)
-	th.CheckEquals(t, expected, actual)
-
-	var testStruct struct {
-		A string
-		B time.Time
-	}
-	testStructValue := reflect.ValueOf(testStruct)
-	expected = true
-	actual = isZero(testStructValue)
-	th.CheckEquals(t, expected, actual)
-	testStruct = struct {
-		A string
-		B time.Time
-	}{
-		B: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
-	}
-	testStructValue = reflect.ValueOf(testStruct)
-	expected = false
-	actual = isZero(testStructValue)
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestQueriesAreEscaped(t *testing.T) {
-	type foo struct {
-		Name  string `q:"something"`
-		Shape string `q:"else"`
-	}
-
-	expected := &url.URL{RawQuery: "else=Triangl+e&something=blah%2B%3F%21%21foo"}
-
-	actual, err := BuildQueryString(foo{Name: "blah+?!!foo", Shape: "Triangl e"})
-	th.AssertNoErr(t, err)
-
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/provider_client.go b/provider_client.go
index 53fce73..f886823 100644
--- a/provider_client.go
+++ b/provider_client.go
@@ -3,7 +3,6 @@
 import (
 	"bytes"
 	"encoding/json"
-	"fmt"
 	"io"
 	"io/ioutil"
 	"net/http"
@@ -11,7 +10,7 @@
 )
 
 // DefaultUserAgent is the default User-Agent string set in the request header.
-const DefaultUserAgent = "gophercloud/1.0.0"
+const DefaultUserAgent = "gophercloud/2.0.0"
 
 // UserAgent represents a User-Agent header.
 type UserAgent struct {
@@ -68,6 +67,8 @@
 	// fails with a 401 HTTP response code. This a needed because there may be multiple
 	// authentication functions for different Identity service versions.
 	ReauthFunc func() error
+
+	Debug bool
 }
 
 // AuthenticatedHeaders returns a map of HTTP headers that are common for all
@@ -85,46 +86,30 @@
 	// content type of the request will default to "application/json" unless overridden by MoreHeaders.
 	// It's an error to specify both a JSONBody and a RawBody.
 	JSONBody interface{}
-	// RawBody contains an io.ReadSeeker that will be consumed by the request directly. No content-type
+	// RawBody contains an io.Reader that will be consumed by the request directly. No content-type
 	// will be set unless one is provided explicitly by MoreHeaders.
-	RawBody io.ReadSeeker
-
+	RawBody io.Reader
 	// JSONResponse, if provided, will be populated with the contents of the response body parsed as
 	// JSON.
 	JSONResponse interface{}
 	// OkCodes contains a list of numeric HTTP status codes that should be interpreted as success. If
 	// the response has a different code, an error will be returned.
 	OkCodes []int
-
 	// MoreHeaders specifies additional HTTP headers to be provide on the request. If a header is
 	// provided with a blank value (""), that header will be *omitted* instead: use this to suppress
 	// the default Accept header or an inferred Content-Type, for example.
 	MoreHeaders map[string]string
-}
-
-// UnexpectedResponseCodeError is returned by the Request method when a response code other than
-// those listed in OkCodes is encountered.
-type UnexpectedResponseCodeError struct {
-	URL      string
-	Method   string
-	Expected []int
-	Actual   int
-	Body     []byte
-}
-
-func (err *UnexpectedResponseCodeError) Error() string {
-	return fmt.Sprintf(
-		"Expected HTTP response code %v when accessing [%s %s], but got %d instead\n%s",
-		err.Expected, err.Method, err.URL, err.Actual, err.Body,
-	)
+	// ErrorContext specifies the resource error type to return if an error is encountered.
+	// This lets resources override default error messages based on the response status code.
+	ErrorContext error
 }
 
 var applicationJSON = "application/json"
 
 // Request performs an HTTP request using the ProviderClient's current HTTPClient. An authentication
 // header will automatically be provided.
-func (client *ProviderClient) Request(method, url string, options RequestOpts) (*http.Response, error) {
-	var body io.ReadSeeker
+func (client *ProviderClient) Request(method, url string, options *RequestOpts) (*http.Response, error) {
+	var body io.Reader
 	var contentType *string
 
 	// Derive the content body by either encoding an arbitrary object as JSON, or by taking a provided
@@ -179,32 +164,13 @@
 
 	// Set connection parameter to close the connection immediately when we've got the response
 	req.Close = true
-	
+
 	// Issue the request.
 	resp, err := client.HTTPClient.Do(req)
 	if err != nil {
 		return nil, err
 	}
 
-	if resp.StatusCode == http.StatusUnauthorized {
-		if client.ReauthFunc != nil {
-			err = client.ReauthFunc()
-			if err != nil {
-				return nil, fmt.Errorf("Error trying to re-authenticate: %s", err)
-			}
-			if options.RawBody != nil {
-				options.RawBody.Seek(0, 0)
-			}
-			resp.Body.Close()
-			resp, err = client.Request(method, url, options)
-			if err != nil {
-				return nil, fmt.Errorf("Successfully re-authenticated, but got error executing request: %s", err)
-			}
-
-			return resp, nil
-		}
-	}
-
 	// Allow default OkCodes if none explicitly set
 	if options.OkCodes == nil {
 		options.OkCodes = defaultOkCodes(method)
@@ -218,16 +184,98 @@
 			break
 		}
 	}
+
 	if !ok {
 		body, _ := ioutil.ReadAll(resp.Body)
 		resp.Body.Close()
-		return resp, &UnexpectedResponseCodeError{
+		//pc := make([]uintptr, 1)
+		//runtime.Callers(2, pc)
+		//f := runtime.FuncForPC(pc[0])
+		respErr := ErrUnexpectedResponseCode{
 			URL:      url,
 			Method:   method,
 			Expected: options.OkCodes,
 			Actual:   resp.StatusCode,
 			Body:     body,
 		}
+		//respErr.Function = "gophercloud.ProviderClient.Request"
+
+		errType := options.ErrorContext
+		switch resp.StatusCode {
+		case http.StatusBadRequest:
+			err = ErrDefault400{respErr}
+			if error400er, ok := errType.(Err400er); ok {
+				err = error400er.Error400(respErr)
+			}
+		case http.StatusUnauthorized:
+			if client.ReauthFunc != nil {
+				err = client.ReauthFunc()
+				if err != nil {
+					e := &ErrUnableToReauthenticate{}
+					e.ErrOriginal = respErr
+					return nil, e
+				}
+				if options.RawBody != nil {
+					if seeker, ok := options.RawBody.(io.Seeker); ok {
+						seeker.Seek(0, 0)
+					}
+				}
+				resp, err = client.Request(method, url, options)
+				if err != nil {
+					switch err.(type) {
+					case *ErrUnexpectedResponseCode:
+						e := &ErrErrorAfterReauthentication{}
+						e.ErrOriginal = err.(*ErrUnexpectedResponseCode)
+						return nil, e
+					default:
+						e := &ErrErrorAfterReauthentication{}
+						e.ErrOriginal = err
+						return nil, e
+					}
+				}
+				return resp, nil
+			}
+			err = ErrDefault401{respErr}
+			if error401er, ok := errType.(Err401er); ok {
+				err = error401er.Error401(respErr)
+			}
+		case http.StatusNotFound:
+			err = ErrDefault404{respErr}
+			if error404er, ok := errType.(Err404er); ok {
+				err = error404er.Error404(respErr)
+			}
+		case http.StatusMethodNotAllowed:
+			err = ErrDefault405{respErr}
+			if error405er, ok := errType.(Err405er); ok {
+				err = error405er.Error405(respErr)
+			}
+		case http.StatusRequestTimeout:
+			err = ErrDefault408{respErr}
+			if error408er, ok := errType.(Err408er); ok {
+				err = error408er.Error408(respErr)
+			}
+		case 429:
+			err = ErrDefault429{respErr}
+			if error429er, ok := errType.(Err429er); ok {
+				err = error429er.Error429(respErr)
+			}
+		case http.StatusInternalServerError:
+			err = ErrDefault500{respErr}
+			if error500er, ok := errType.(Err500er); ok {
+				err = error500er.Error500(respErr)
+			}
+		case http.StatusServiceUnavailable:
+			err = ErrDefault503{respErr}
+			if error503er, ok := errType.(Err503er); ok {
+				err = error503er.Error503(respErr)
+			}
+		}
+
+		if err == nil {
+			err = respErr
+		}
+
+		return resp, err
 	}
 
 	// Parse the response body as JSON, if requested to do so.
@@ -257,75 +305,3 @@
 
 	return []int{}
 }
-
-func (client *ProviderClient) Get(url string, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) {
-	if opts == nil {
-		opts = &RequestOpts{}
-	}
-	if JSONResponse != nil {
-		opts.JSONResponse = JSONResponse
-	}
-	return client.Request("GET", url, *opts)
-}
-
-func (client *ProviderClient) Post(url string, JSONBody interface{}, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) {
-	if opts == nil {
-		opts = &RequestOpts{}
-	}
-
-	if v, ok := (JSONBody).(io.ReadSeeker); ok {
-		opts.RawBody = v
-	} else if JSONBody != nil {
-		opts.JSONBody = JSONBody
-	}
-
-	if JSONResponse != nil {
-		opts.JSONResponse = JSONResponse
-	}
-
-	return client.Request("POST", url, *opts)
-}
-
-func (client *ProviderClient) Put(url string, JSONBody interface{}, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) {
-	if opts == nil {
-		opts = &RequestOpts{}
-	}
-
-	if v, ok := (JSONBody).(io.ReadSeeker); ok {
-		opts.RawBody = v
-	} else if JSONBody != nil {
-		opts.JSONBody = JSONBody
-	}
-
-	if JSONResponse != nil {
-		opts.JSONResponse = JSONResponse
-	}
-
-	return client.Request("PUT", url, *opts)
-}
-
-func (client *ProviderClient) Patch(url string, JSONBody interface{}, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) {
-	if opts == nil {
-		opts = &RequestOpts{}
-	}
-
-	if v, ok := (JSONBody).(io.ReadSeeker); ok {
-		opts.RawBody = v
-	} else if JSONBody != nil {
-		opts.JSONBody = JSONBody
-	}
-
-	if JSONResponse != nil {
-		opts.JSONResponse = JSONResponse
-	}
-
-	return client.Request("PATCH", url, *opts)
-}
-
-func (client *ProviderClient) Delete(url string, opts *RequestOpts) (*http.Response, error) {
-	if opts == nil {
-		opts = &RequestOpts{}
-	}
-
-	return client.Request("DELETE", url, *opts)
-}
diff --git a/provider_client_test.go b/provider_client_test.go
deleted file mode 100644
index d79d862..0000000
--- a/provider_client_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package gophercloud
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestAuthenticatedHeaders(t *testing.T) {
-	p := &ProviderClient{
-		TokenID: "1234",
-	}
-	expected := map[string]string{"X-Auth-Token": "1234"}
-	actual := p.AuthenticatedHeaders()
-	th.CheckDeepEquals(t, expected, actual)
-}
-
-func TestUserAgent(t *testing.T) {
-	p := &ProviderClient{}
-
-	p.UserAgent.Prepend("custom-user-agent/2.4.0")
-	expected := "custom-user-agent/2.4.0 gophercloud/1.0.0"
-	actual := p.UserAgent.Join()
-	th.CheckEquals(t, expected, actual)
-
-	p.UserAgent.Prepend("another-custom-user-agent/0.3.0", "a-third-ua/5.9.0")
-	expected = "another-custom-user-agent/0.3.0 a-third-ua/5.9.0 custom-user-agent/2.4.0 gophercloud/1.0.0"
-	actual = p.UserAgent.Join()
-	th.CheckEquals(t, expected, actual)
-
-	p.UserAgent = UserAgent{}
-	expected = "gophercloud/1.0.0"
-	actual = p.UserAgent.Join()
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/rackspace/auth_env.go b/rackspace/auth_env.go
deleted file mode 100644
index 5852c3c..0000000
--- a/rackspace/auth_env.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package rackspace
-
-import (
-	"fmt"
-	"os"
-
-	"github.com/rackspace/gophercloud"
-)
-
-var nilOptions = gophercloud.AuthOptions{}
-
-// ErrNoAuthUrl, ErrNoUsername, and ErrNoPassword errors indicate of the
-// required RS_AUTH_URL, RS_USERNAME, or RS_PASSWORD environment variables,
-// respectively, remain undefined.  See the AuthOptions() function for more details.
-var (
-	ErrNoAuthURL  = fmt.Errorf("Environment variable RS_AUTH_URL or OS_AUTH_URL need to be set.")
-	ErrNoUsername = fmt.Errorf("Environment variable RS_USERNAME or OS_USERNAME need to be set.")
-	ErrNoPassword = fmt.Errorf("Environment variable RS_API_KEY or RS_PASSWORD needs to be set.")
-)
-
-func prefixedEnv(base string) string {
-	value := os.Getenv("RS_" + base)
-	if value == "" {
-		value = os.Getenv("OS_" + base)
-	}
-	return value
-}
-
-// AuthOptionsFromEnv fills out an identity.AuthOptions structure with the
-// settings found on the various Rackspace RS_* environment variables.
-func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
-	authURL := prefixedEnv("AUTH_URL")
-	username := prefixedEnv("USERNAME")
-	password := prefixedEnv("PASSWORD")
-	apiKey := prefixedEnv("API_KEY")
-
-	if authURL == "" {
-		return nilOptions, ErrNoAuthURL
-	}
-
-	if username == "" {
-		return nilOptions, ErrNoUsername
-	}
-
-	if password == "" && apiKey == "" {
-		return nilOptions, ErrNoPassword
-	}
-
-	ao := gophercloud.AuthOptions{
-		IdentityEndpoint: authURL,
-		Username:         username,
-		Password:         password,
-		APIKey:           apiKey,
-	}
-
-	return ao, nil
-}
diff --git a/rackspace/autoscale/v1/policies/doc.go b/rackspace/autoscale/v1/policies/doc.go
deleted file mode 100644
index c4d3bab..0000000
--- a/rackspace/autoscale/v1/policies/doc.go
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
-Package policies provides information and interaction with the policy API
-resource in the Rackspace Auto Scale service.
-
-Auto Scale uses policies to define when and how scaling activity will take
-place.  Scaling policies specify how to modify the scaling group and its
-behavior.  You can specify multiple policies to manage a scaling group.
-*/
-package policies
diff --git a/rackspace/autoscale/v1/policies/fixtures.go b/rackspace/autoscale/v1/policies/fixtures.go
deleted file mode 100644
index 9334740..0000000
--- a/rackspace/autoscale/v1/policies/fixtures.go
+++ /dev/null
@@ -1,259 +0,0 @@
-// +build fixtures
-
-package policies
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// PolicyListBody contains the canned body of a policies.List response.
-const PolicyListBody = `
-{
-  "policies_links": [],
-  "policies": [
-    {
-      "name": "webhook policy",
-      "links": [
-        {
-          "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/60b15dad-5ea1-43fa-9a12-a1d737b4da07/policies/2b48d247-0282-4b9d-8775-5c4b67e8e649/",
-          "rel": "self"
-        }
-      ],
-      "changePercent": 3.3,
-      "cooldown": 300,
-      "type": "webhook",
-      "id": "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-    },
-    {
-      "cooldown": 0,
-      "name": "one time",
-      "links": [
-        {
-          "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/60b15dad-5ea1-43fa-9a12-a1d737b4da07/policies/c175c31e-65f9-41de-8b15-918420d3253e/",
-          "rel": "self"
-        }
-      ],
-      "args": {
-        "at": "2020-04-01T23:00:00.000Z"
-      },
-      "type": "schedule",
-      "id": "c175c31e-65f9-41de-8b15-918420d3253e",
-      "change": -1
-    },
-    {
-      "cooldown": 0,
-      "name": "sunday afternoon",
-      "links": [
-        {
-          "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/60b15dad-5ea1-43fa-9a12-a1d737b4da07/policies/e785e3e7-af9e-4f3c-99ae-b80a532e1663/",
-          "rel": "self"
-        }
-      ],
-      "args": {
-        "cron": "59 15 * * 0"
-      },
-      "type": "schedule",
-      "id": "e785e3e7-af9e-4f3c-99ae-b80a532e1663",
-      "desiredCapacity": 2
-    }
-  ]
-}
-`
-
-// PolicyCreateBody contains the canned body of a policies.Create response.
-const PolicyCreateBody = PolicyListBody
-
-// PolicyCreateRequest contains the canned body of a policies.Create request.
-const PolicyCreateRequest = `
-[
-  {
-    "name": "webhook policy",
-    "changePercent": 3.3,
-    "cooldown": 300,
-    "type": "webhook"
-  },
-  {
-    "cooldown": 0,
-    "name": "one time",
-    "args": {
-      "at": "2020-04-01T23:00:00Z"
-    },
-    "type": "schedule",
-    "change": -1
-  },
-  {
-    "cooldown": 0,
-    "name": "sunday afternoon",
-    "args": {
-      "cron": "59 15 * * 0"
-    },
-    "type": "schedule",
-    "desiredCapacity": 2
-  }
-]
-`
-
-// PolicyGetBody contains the canned body of a policies.Get response.
-const PolicyGetBody = `
-{
-  "policy": {
-    "name": "webhook policy",
-    "links": [
-      {
-        "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/60b15dad-5ea1-43fa-9a12-a1d737b4da07/policies/2b48d247-0282-4b9d-8775-5c4b67e8e649/",
-        "rel": "self"
-      }
-    ],
-    "changePercent": 3.3,
-    "cooldown": 300,
-    "type": "webhook",
-    "id": "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-  }
-}
-`
-
-// PolicyUpdateRequest contains the canned body of a policies.Update request.
-const PolicyUpdateRequest = `
-{
-  "name": "updated webhook policy",
-  "type": "webhook",
-  "cooldown": 600,
-  "changePercent": 6.6
-}
-`
-
-var (
-	// WebhookPolicy is a Policy corresponding to the first result in PolicyListBody.
-	WebhookPolicy = Policy{
-		ID:              "2b48d247-0282-4b9d-8775-5c4b67e8e649",
-		Name:            "webhook policy",
-		Type:            Webhook,
-		Cooldown:        300,
-		AdjustmentType:  ChangePercent,
-		AdjustmentValue: 3.3,
-	}
-
-	// OneTimePolicy is a Policy corresponding to the second result in PolicyListBody.
-	OneTimePolicy = Policy{
-		ID:              "c175c31e-65f9-41de-8b15-918420d3253e",
-		Name:            "one time",
-		Type:            Schedule,
-		AdjustmentType:  Change,
-		AdjustmentValue: float64(-1),
-		Schedule:        At(time.Date(2020, time.April, 01, 23, 0, 0, 0, time.UTC)),
-	}
-
-	// SundayAfternoonPolicy is a Policy corresponding to the third result in PolicyListBody.
-	SundayAfternoonPolicy = Policy{
-		ID:              "e785e3e7-af9e-4f3c-99ae-b80a532e1663",
-		Name:            "sunday afternoon",
-		Type:            Schedule,
-		AdjustmentType:  DesiredCapacity,
-		AdjustmentValue: float64(2),
-		Schedule:        Cron("59 15 * * 0"),
-	}
-)
-
-// HandlePolicyListSuccessfully sets up the test server to respond to a policies List request.
-func HandlePolicyListSuccessfully(t *testing.T) {
-	path := "/groups/60b15dad-5ea1-43fa-9a12-a1d737b4da07/policies"
-
-	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-
-		fmt.Fprintf(w, PolicyListBody)
-	})
-}
-
-// HandlePolicyCreateSuccessfully sets up the test server to respond to a policies Create request.
-func HandlePolicyCreateSuccessfully(t *testing.T) {
-	path := "/groups/60b15dad-5ea1-43fa-9a12-a1d737b4da07/policies"
-
-	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		th.TestJSONRequest(t, r, PolicyCreateRequest)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, PolicyCreateBody)
-	})
-}
-
-// HandlePolicyGetSuccessfully sets up the test server to respond to a policies Get request.
-func HandlePolicyGetSuccessfully(t *testing.T) {
-	groupID := "60b15dad-5ea1-43fa-9a12-a1d737b4da07"
-	policyID := "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-
-	path := fmt.Sprintf("/groups/%s/policies/%s", groupID, policyID)
-
-	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-
-		fmt.Fprintf(w, PolicyGetBody)
-	})
-}
-
-// HandlePolicyUpdateSuccessfully sets up the test server to respond to a policies Update request.
-func HandlePolicyUpdateSuccessfully(t *testing.T) {
-	groupID := "60b15dad-5ea1-43fa-9a12-a1d737b4da07"
-	policyID := "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-
-	path := fmt.Sprintf("/groups/%s/policies/%s", groupID, policyID)
-
-	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		th.TestJSONRequest(t, r, PolicyUpdateRequest)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandlePolicyDeleteSuccessfully sets up the test server to respond to a policies Delete request.
-func HandlePolicyDeleteSuccessfully(t *testing.T) {
-	groupID := "60b15dad-5ea1-43fa-9a12-a1d737b4da07"
-	policyID := "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-
-	path := fmt.Sprintf("/groups/%s/policies/%s", groupID, policyID)
-
-	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandlePolicyExecuteSuccessfully sets up the test server to respond to a policies Execute request.
-func HandlePolicyExecuteSuccessfully(t *testing.T) {
-	groupID := "60b15dad-5ea1-43fa-9a12-a1d737b4da07"
-	policyID := "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-
-	path := fmt.Sprintf("/groups/%s/policies/%s/execute", groupID, policyID)
-
-	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusAccepted)
-		fmt.Fprintf(w, "{}")
-	})
-}
diff --git a/rackspace/autoscale/v1/policies/requests.go b/rackspace/autoscale/v1/policies/requests.go
deleted file mode 100644
index 7aadf98..0000000
--- a/rackspace/autoscale/v1/policies/requests.go
+++ /dev/null
@@ -1,302 +0,0 @@
-package policies
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Validation errors returned by create or update operations.
-var (
-	ErrNoName            = errors.New("Policy name cannot be empty.")
-	ErrNoSchedule        = errors.New("Schedule cannot be nil for schedule policies.")
-	ErrCooldownRange     = errors.New("Cooldown is out of range (0, 86400).")
-	ErrUnknownType       = errors.New("Unknown policy type.")
-	ErrUnknownAdjustment = errors.New("Unknown adjustment type.")
-	ErrEmptyCron         = errors.New("Cron argument cannot be empty.")
-)
-
-// List returns all scaling policies for a group.
-func List(client *gophercloud.ServiceClient, groupID string) pagination.Pager {
-	url := listURL(client, groupID)
-
-	createPageFn := func(r pagination.PageResult) pagination.Page {
-		return PolicyPage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, url, createPageFn)
-}
-
-// CreateOptsBuilder is the interface responsible for generating the map that
-// will be marshalled to JSON for a Create operation.
-type CreateOptsBuilder interface {
-	ToPolicyCreateMap() ([]map[string]interface{}, error)
-}
-
-// CreateOpts is a slice of CreateOpt structs that allow the user to create
-// multiple policies in a single operation.
-type CreateOpts []CreateOpt
-
-// CreateOpt represents the options to create a policy.
-type CreateOpt struct {
-	// Name [required] is a name for the policy.
-	Name string
-
-	// Type [required] of policy, i.e. either "webhook" or "schedule".
-	Type Type
-
-	// Cooldown [required] period in seconds.
-	Cooldown int
-
-	// AdjustmentType [requried] is the method used to change the capacity of
-	// the group, i.e. one of: Change, ChangePercent, or DesiredCapacity.
-	AdjustmentType AdjustmentType
-
-	// AdjustmentValue [required] is the numeric value of the adjustment.  For
-	// adjustments of type Change or DesiredCapacity, this will be converted to
-	// an integer.
-	AdjustmentValue float64
-
-	// Value determining Schedule policy behavior, or nil for Webhook policies.
-	// This should be an appropriately configured Cron or an At value.
-	Schedule ScheduleArgs
-}
-
-// ToPolicyCreateMap converts a slice of CreateOpt structs into a map for use
-// in the request body of a Create operation.
-func (opts CreateOpts) ToPolicyCreateMap() ([]map[string]interface{}, error) {
-	var policies []map[string]interface{}
-
-	for _, o := range opts {
-		if o.Name == "" {
-			return nil, ErrNoName
-		}
-
-		if o.Type == Schedule && o.Schedule == nil {
-			return nil, ErrNoSchedule
-		}
-
-		if ok := validateType(o.Type); !ok {
-			return nil, ErrUnknownType
-		}
-
-		if ok := validateCooldown(o.Cooldown); !ok {
-			return nil, ErrCooldownRange
-		}
-
-		policy := make(map[string]interface{})
-
-		policy["name"] = o.Name
-		policy["type"] = o.Type
-		policy["cooldown"] = o.Cooldown
-
-		err := setAdjustment(o.AdjustmentType, o.AdjustmentValue, policy)
-
-		if err != nil {
-			return nil, err
-		}
-
-		if o.Schedule != nil {
-			args, err := o.Schedule.ToPolicyArgs()
-
-			if err != nil {
-				return nil, err
-			}
-
-			policy["args"] = args
-		}
-
-		policies = append(policies, policy)
-	}
-
-	return policies, nil
-}
-
-// Create requests a new policy be created and associated with the given group.
-func Create(client *gophercloud.ServiceClient, groupID string, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToPolicyCreateMap()
-
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = client.Post(createURL(client, groupID), reqBody, &res.Body, nil)
-
-	return res
-}
-
-// Get requests the details of a single policy with the given ID.
-func Get(client *gophercloud.ServiceClient, groupID, policyID string) GetResult {
-	var result GetResult
-
-	_, result.Err = client.Get(getURL(client, groupID, policyID), &result.Body, nil)
-
-	return result
-}
-
-// UpdateOptsBuilder is the interface responsible for generating the map
-// structure for producing JSON for an Update operation.
-type UpdateOptsBuilder interface {
-	ToPolicyUpdateMap() (map[string]interface{}, error)
-}
-
-// UpdateOpts represents the options for updating an existing policy.
-//
-// Update operations completely replace the configuration being updated. Empty
-// values in the update are accepted and overwrite previously specified
-// parameters.
-type UpdateOpts struct {
-	// Name [required] is a name for the policy.
-	Name string
-
-	// Type [required] of policy, i.e. either "webhook" or "schedule".
-	Type Type
-
-	// Cooldown [required] period in seconds.  If you don't specify a cooldown,
-	// it will default to zero, and the policy will be configured as such.
-	Cooldown int
-
-	// AdjustmentType [requried] is the method used to change the capacity of
-	// the group, i.e. one of: Change, ChangePercent, or DesiredCapacity.
-	AdjustmentType AdjustmentType
-
-	// AdjustmentValue [required] is the numeric value of the adjustment.  For
-	// adjustments of type Change or DesiredCapacity, this will be converted to
-	// an integer.
-	AdjustmentValue float64
-
-	// Value determining Schedule policy behavior, or nil for Webhook policies.
-	// This should be an appropriately configured Cron or an At value.
-	Schedule ScheduleArgs
-}
-
-// ToPolicyUpdateMap converts an UpdateOpts struct into a map for use as the
-// request body in an Update request.
-func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) {
-	if opts.Name == "" {
-		return nil, ErrNoName
-	}
-
-	if opts.Type == Schedule && opts.Schedule == nil {
-		return nil, ErrNoSchedule
-	}
-
-	if ok := validateType(opts.Type); !ok {
-		return nil, ErrUnknownType
-	}
-
-	if ok := validateCooldown(opts.Cooldown); !ok {
-		return nil, ErrCooldownRange
-	}
-
-	policy := make(map[string]interface{})
-
-	policy["name"] = opts.Name
-	policy["type"] = opts.Type
-	policy["cooldown"] = opts.Cooldown
-
-	err := setAdjustment(opts.AdjustmentType, opts.AdjustmentValue, policy)
-
-	if err != nil {
-		return nil, err
-	}
-
-	if opts.Schedule != nil {
-		args, err := opts.Schedule.ToPolicyArgs()
-
-		if err != nil {
-			return nil, err
-		}
-
-		policy["args"] = args
-	}
-
-	return policy, nil
-}
-
-// Update requests the configuration of the given policy be updated.
-func Update(client *gophercloud.ServiceClient, groupID, policyID string, opts UpdateOptsBuilder) UpdateResult {
-	var result UpdateResult
-
-	url := updateURL(client, groupID, policyID)
-	reqBody, err := opts.ToPolicyUpdateMap()
-
-	if err != nil {
-		result.Err = err
-		return result
-	}
-
-	_, result.Err = client.Put(url, reqBody, nil, &gophercloud.RequestOpts{
-		OkCodes: []int{204},
-	})
-
-	return result
-}
-
-// Delete requests the given policy be permanently deleted.
-func Delete(client *gophercloud.ServiceClient, groupID, policyID string) DeleteResult {
-	var result DeleteResult
-
-	url := deleteURL(client, groupID, policyID)
-	_, result.Err = client.Delete(url, &gophercloud.RequestOpts{
-		OkCodes: []int{204},
-	})
-
-	return result
-}
-
-// Execute requests the given policy be executed immediately.
-func Execute(client *gophercloud.ServiceClient, groupID, policyID string) ExecuteResult {
-	var result ExecuteResult
-
-	url := executeURL(client, groupID, policyID)
-	_, result.Err = client.Post(url, nil, &result.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{202},
-	})
-
-	return result
-}
-
-// Validate and set an adjustment on the given request body.
-func setAdjustment(t AdjustmentType, v float64, body map[string]interface{}) error {
-	key := string(t)
-
-	switch t {
-	case ChangePercent:
-		body[key] = v
-
-	case Change, DesiredCapacity:
-		body[key] = int(v)
-
-	default:
-		return ErrUnknownAdjustment
-	}
-
-	return nil
-}
-
-func validateType(t Type) (ok bool) {
-	switch t {
-	case Schedule, Webhook:
-		ok = true
-		return
-
-	default:
-		ok = false
-		return
-	}
-}
-
-func validateCooldown(cooldown int) (ok bool) {
-	if cooldown < 0 || cooldown > 86400 {
-		ok = false
-		return
-	}
-
-	ok = true
-	return
-}
diff --git a/rackspace/autoscale/v1/policies/requests_test.go b/rackspace/autoscale/v1/policies/requests_test.go
deleted file mode 100644
index 88a52a7..0000000
--- a/rackspace/autoscale/v1/policies/requests_test.go
+++ /dev/null
@@ -1,168 +0,0 @@
-package policies
-
-import (
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const (
-	groupID         = "60b15dad-5ea1-43fa-9a12-a1d737b4da07"
-	webhookPolicyID = "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePolicyListSuccessfully(t)
-
-	pages := 0
-	pager := List(client.ServiceClient(), "60b15dad-5ea1-43fa-9a12-a1d737b4da07")
-
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		policies, err := ExtractPolicies(page)
-
-		if err != nil {
-			return false, err
-		}
-
-		if len(policies) != 3 {
-			t.Fatalf("Expected 3 policies, got %d", len(policies))
-		}
-
-		th.CheckDeepEquals(t, WebhookPolicy, policies[0])
-		th.CheckDeepEquals(t, OneTimePolicy, policies[1])
-		th.CheckDeepEquals(t, SundayAfternoonPolicy, policies[2])
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-
-	if pages != 1 {
-		t.Errorf("Expected 1 page, saw %d", pages)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePolicyCreateSuccessfully(t)
-
-	oneTime := time.Date(2020, time.April, 01, 23, 0, 0, 0, time.UTC)
-	client := client.ServiceClient()
-	opts := CreateOpts{
-		{
-			Name:            "webhook policy",
-			Type:            Webhook,
-			Cooldown:        300,
-			AdjustmentType:  ChangePercent,
-			AdjustmentValue: 3.3,
-		},
-		{
-			Name:            "one time",
-			Type:            Schedule,
-			AdjustmentType:  Change,
-			AdjustmentValue: -1,
-			Schedule:        At(oneTime),
-		},
-		{
-			Name:            "sunday afternoon",
-			Type:            Schedule,
-			AdjustmentType:  DesiredCapacity,
-			AdjustmentValue: 2,
-			Schedule:        Cron("59 15 * * 0"),
-		},
-	}
-
-	policies, err := Create(client, groupID, opts).Extract()
-
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, WebhookPolicy, policies[0])
-	th.CheckDeepEquals(t, OneTimePolicy, policies[1])
-	th.CheckDeepEquals(t, SundayAfternoonPolicy, policies[2])
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePolicyGetSuccessfully(t)
-
-	client := client.ServiceClient()
-
-	policy, err := Get(client, groupID, webhookPolicyID).Extract()
-
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, WebhookPolicy, *policy)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePolicyUpdateSuccessfully(t)
-
-	client := client.ServiceClient()
-	opts := UpdateOpts{
-		Name:            "updated webhook policy",
-		Type:            Webhook,
-		Cooldown:        600,
-		AdjustmentType:  ChangePercent,
-		AdjustmentValue: 6.6,
-	}
-
-	err := Update(client, groupID, webhookPolicyID, opts).ExtractErr()
-
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePolicyDeleteSuccessfully(t)
-
-	client := client.ServiceClient()
-	err := Delete(client, groupID, webhookPolicyID).ExtractErr()
-
-	th.AssertNoErr(t, err)
-}
-
-func TestExecute(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandlePolicyExecuteSuccessfully(t)
-
-	client := client.ServiceClient()
-	err := Execute(client, groupID, webhookPolicyID).ExtractErr()
-
-	th.AssertNoErr(t, err)
-}
-
-func TestValidateType(t *testing.T) {
-	ok := validateType(Schedule)
-	th.AssertEquals(t, true, ok)
-
-	ok = validateType(Webhook)
-	th.AssertEquals(t, true, ok)
-
-	ok = validateType("BAD")
-	th.AssertEquals(t, false, ok)
-}
-
-func TestValidateCooldown(t *testing.T) {
-	ok := validateCooldown(0)
-	th.AssertEquals(t, true, ok)
-
-	ok = validateCooldown(86400)
-	th.AssertEquals(t, true, ok)
-
-	ok = validateCooldown(-1)
-	th.AssertEquals(t, false, ok)
-
-	ok = validateCooldown(172800)
-	th.AssertEquals(t, false, ok)
-}
diff --git a/rackspace/autoscale/v1/policies/results.go b/rackspace/autoscale/v1/policies/results.go
deleted file mode 100644
index 99d8b1a..0000000
--- a/rackspace/autoscale/v1/policies/results.go
+++ /dev/null
@@ -1,246 +0,0 @@
-package policies
-
-import (
-	"time"
-
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-type policyResult struct {
-	gophercloud.Result
-}
-
-// Extract interprets any policyResult as a Policy, if possible.
-func (r policyResult) Extract() (*Policy, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		Policy policy `mapstructure:"policy"`
-	}
-
-	if err := mapstructure.Decode(r.Body, &response); err != nil {
-		return nil, err
-	}
-
-	policy := response.Policy.toExported()
-
-	return &policy, nil
-}
-
-// CreateResult represents the result of a create operation.
-type CreateResult struct {
-	policyResult
-}
-
-// Extract extracts a slice of Policies from a CreateResult.  Multiple policies
-// can be created in a single operation, so the result of a create is always a
-// list of policies.
-func (res CreateResult) Extract() ([]Policy, error) {
-	if res.Err != nil {
-		return nil, res.Err
-	}
-
-	return commonExtractPolicies(res.Body)
-}
-
-// GetResult temporarily contains the response from a Get call.
-type GetResult struct {
-	policyResult
-}
-
-// UpdateResult represents the result of an update operation.
-type UpdateResult struct {
-	gophercloud.ErrResult
-}
-
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-// ExecuteResult represents the result of an execute operation.
-type ExecuteResult struct {
-	gophercloud.ErrResult
-}
-
-// Type represents a type of scaling policy.
-type Type string
-
-const (
-	// Schedule policies run at given times.
-	Schedule Type = "schedule"
-
-	// Webhook policies are triggered by HTTP requests.
-	Webhook Type = "webhook"
-)
-
-// AdjustmentType represents the way in which a policy will change a group.
-type AdjustmentType string
-
-// Valid types of adjustments for a policy.
-const (
-	Change          AdjustmentType = "change"
-	ChangePercent   AdjustmentType = "changePercent"
-	DesiredCapacity AdjustmentType = "desiredCapacity"
-)
-
-// ScheduleArgs is implemented by types that can be converted into arguments for
-// policies with type Schedule.
-type ScheduleArgs interface {
-	ToPolicyArgs() (map[string]string, error)
-}
-
-// At satisfies the ScheduleArgs interface and can be used to configure a policy
-// to execute a particular time.
-type At time.Time
-
-// ToPolicyArgs returns a key and value for use in constructing arguments to
-// schedule policies.
-func (at At) ToPolicyArgs() (map[string]string, error) {
-	t := time.Time(at)
-
-	args := make(map[string]string)
-	args["at"] = t.UTC().Format(time.RFC3339)
-
-	return args, nil
-}
-
-// Cron satisfies the ScheduleArgs interface and can be used to configure a
-// policy that executes at regular intervals.
-type Cron string
-
-// ToPolicyArgs returns a key and value for use in constructing arguments to
-// schedule policies.
-func (cron Cron) ToPolicyArgs() (map[string]string, error) {
-	if cron == "" {
-		return nil, ErrEmptyCron
-	}
-
-	args := make(map[string]string)
-	args["cron"] = string(cron)
-
-	return args, nil
-}
-
-// Policy represents a scaling policy.
-type Policy struct {
-	// UUID for the policy.
-	ID string
-
-	// Name of the policy.
-	Name string
-
-	// Type of scaling policy.
-	Type Type
-
-	// Cooldown period, in seconds.
-	Cooldown int
-
-	// The type of adjustment in capacity to be made.
-	AdjustmentType AdjustmentType
-
-	// The numeric value of the adjustment in capacity.
-	AdjustmentValue float64
-
-	// Arguments determining Schedule policy behavior, or nil for Webhook
-	// policies.
-	Schedule ScheduleArgs
-}
-
-// This is an intermediate representation of the exported Policy type.  The
-// fields in API responses vary by policy type and configuration.  This lets us
-// decode responses then normalize them into a Policy.
-type policy struct {
-	ID       string `mapstructure:"id"`
-	Name     string `mapstructure:"name"`
-	Type     Type   `mapstructure:"type"`
-	Cooldown int    `mapstructure:"cooldown"`
-
-	// The API will respond with exactly one of these omitting the others.
-	Change          interface{} `mapstructure:"change"`
-	ChangePercent   interface{} `mapstructure:"changePercent"`
-	DesiredCapacity interface{} `mapstructure:"desiredCapacity"`
-
-	// Additional configuration options for schedule policies.
-	Args map[string]string `mapstructure:"args"`
-}
-
-// Assemble a Policy from the intermediate policy struct.
-func (p policy) toExported() Policy {
-	policy := Policy{}
-
-	policy.ID = p.ID
-	policy.Name = p.Name
-	policy.Type = p.Type
-	policy.Cooldown = p.Cooldown
-
-	if cron, ok := p.Args["cron"]; ok {
-		policy.Schedule = Cron(cron)
-	} else if at, ok := p.Args["at"]; ok {
-		// Set an At schedule if the "at" argument parses as an RFC3339 time.
-		if t, err := time.Parse(time.RFC3339, at); err == nil {
-			policy.Schedule = At(t)
-		}
-	}
-
-	if v, ok := p.Change.(float64); ok {
-		policy.AdjustmentType = Change
-		policy.AdjustmentValue = v
-	} else if v, ok := p.ChangePercent.(float64); ok {
-		policy.AdjustmentType = ChangePercent
-		policy.AdjustmentValue = v
-	} else if v, ok := p.DesiredCapacity.(float64); ok {
-		policy.AdjustmentType = DesiredCapacity
-		policy.AdjustmentValue = v
-	}
-
-	return policy
-}
-
-// PolicyPage is the page returned by a pager when traversing over a collection
-// of scaling policies.
-type PolicyPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty returns true if a page contains no Policy results.
-func (page PolicyPage) IsEmpty() (bool, error) {
-	policies, err := ExtractPolicies(page)
-
-	if err != nil {
-		return true, err
-	}
-
-	return len(policies) == 0, nil
-}
-
-// ExtractPolicies interprets the results of a single page from a List() call,
-// producing a slice of Policies.
-func ExtractPolicies(page pagination.Page) ([]Policy, error) {
-	return commonExtractPolicies(page.(PolicyPage).Body)
-}
-
-func commonExtractPolicies(body interface{}) ([]Policy, error) {
-	var response struct {
-		Policies []policy `mapstructure:"policies"`
-	}
-
-	err := mapstructure.Decode(body, &response)
-
-	if err != nil {
-		return nil, err
-	}
-
-	policies := make([]Policy, len(response.Policies))
-
-	for i, p := range response.Policies {
-		policies[i] = p.toExported()
-	}
-
-	return policies, nil
-}
diff --git a/rackspace/autoscale/v1/policies/urls.go b/rackspace/autoscale/v1/policies/urls.go
deleted file mode 100644
index 8ee3207..0000000
--- a/rackspace/autoscale/v1/policies/urls.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package policies
-
-import "github.com/rackspace/gophercloud"
-
-func listURL(c *gophercloud.ServiceClient, groupID string) string {
-	return c.ServiceURL("groups", groupID, "policies")
-}
-
-func createURL(c *gophercloud.ServiceClient, groupID string) string {
-	return c.ServiceURL("groups", groupID, "policies")
-}
-
-func getURL(c *gophercloud.ServiceClient, groupID, policyID string) string {
-	return c.ServiceURL("groups", groupID, "policies", policyID)
-}
-
-func updateURL(c *gophercloud.ServiceClient, groupID, policyID string) string {
-	return getURL(c, groupID, policyID)
-}
-
-func deleteURL(c *gophercloud.ServiceClient, groupID, policyID string) string {
-	return getURL(c, groupID, policyID)
-}
-
-func executeURL(c *gophercloud.ServiceClient, groupID, policyID string) string {
-	return c.ServiceURL("groups", groupID, "policies", policyID, "execute")
-}
diff --git a/rackspace/autoscale/v1/policies/urls_test.go b/rackspace/autoscale/v1/policies/urls_test.go
deleted file mode 100644
index 774e2e7..0000000
--- a/rackspace/autoscale/v1/policies/urls_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package policies
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient(), "123")
-	expected := endpoint + "groups/123/policies"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient(), "123")
-	expected := endpoint + "groups/123/policies"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "123", "456")
-	expected := endpoint + "groups/123/policies/456"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient(), "123", "456")
-	expected := endpoint + "groups/123/policies/456"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "123", "456")
-	expected := endpoint + "groups/123/policies/456"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestExecuteURL(t *testing.T) {
-	actual := executeURL(endpointClient(), "123", "456")
-	expected := endpoint + "groups/123/policies/456/execute"
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/rackspace/autoscale/v1/webhooks/doc.go b/rackspace/autoscale/v1/webhooks/doc.go
deleted file mode 100644
index 0b043a9..0000000
--- a/rackspace/autoscale/v1/webhooks/doc.go
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
-Package webhooks provides information and interaction with the webhook API resource
-in the Rackspace Auto Scale service.
-
-Auto Scale uses webhooks to initiate scaling events.  Webhooks are associated
-with scaling policies and provide capability URLs which can be accessed
-anonymously to trigger execution of those policies.  The Auto Scale webhook
-architecture allows Auto Scale to be integrated with other systems, for example,
-monitoring systems.
-*/
-package webhooks
diff --git a/rackspace/autoscale/v1/webhooks/fixtures.go b/rackspace/autoscale/v1/webhooks/fixtures.go
deleted file mode 100644
index daa4c6c..0000000
--- a/rackspace/autoscale/v1/webhooks/fixtures.go
+++ /dev/null
@@ -1,226 +0,0 @@
-// +build fixtures
-
-package webhooks
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// WebhookListBody contains the canned body of a webhooks.List response.
-const WebhookListBody = `
-{
-  "webhooks": [
-    {
-      "id": "2bd1822c-58c5-49fd-8b3d-ed44781a58d1",
-      "name": "first hook",
-      "links": [
-        {
-          "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/60b15dad-5ea1-43fa-9a12-a1d737b4da07/policies/2b48d247-0282-4b9d-8775-5c4b67e8e649/webhooks/2bd1822c-58c5-49fd-8b3d-ed44781a58d1/",
-          "rel": "self"
-        },
-        {
-          "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/execute/1/714c1c17c5e6ea5ef1e710d5ccc62e492575bab5216184d4c27dc0164db1bc06/",
-          "rel": "capability"
-        }
-      ],
-      "metadata": {}
-    },
-    {
-      "id": "76711c36-dfbe-4f5e-bea6-cded99690515",
-      "name": "second hook",
-      "links": [
-        {
-          "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/60b15dad-5ea1-43fa-9a12-a1d737b4da07/policies/2b48d247-0282-4b9d-8775-5c4b67e8e649/webhooks/76711c36-dfbe-4f5e-bea6-cded99690515/",
-          "rel": "self"
-        },
-        {
-          "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/execute/1/982e24858723f9e8bc2afea42a73a3c357c8f518857735400a7f7d8b3f14ccdb/",
-          "rel": "capability"
-        }
-      ],
-      "metadata": {
-        "notes": "a note about this webhook"
-      }
-    }
-  ],
-  "webhooks_links": []
-}
-`
-
-// WebhookCreateBody contains the canned body of a webhooks.Create response.
-const WebhookCreateBody = WebhookListBody
-
-// WebhookCreateRequest contains the canned body of a webhooks.Create request.
-const WebhookCreateRequest = `
-[
-  {
-    "name": "first hook"
-  },
-  {
-    "name": "second hook",
-    "metadata": {
-      "notes": "a note about this webhook"
-    }
-  }
-]
-`
-
-// WebhookGetBody contains the canned body of a webhooks.Get response.
-const WebhookGetBody = `
-{
-  "webhook": {
-    "id": "2bd1822c-58c5-49fd-8b3d-ed44781a58d1",
-    "name": "first hook",
-    "links": [
-      {
-        "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/60b15dad-5ea1-43fa-9a12-a1d737b4da07/policies/2b48d247-0282-4b9d-8775-5c4b67e8e649/webhooks/2bd1822c-58c5-49fd-8b3d-ed44781a58d1/",
-        "rel": "self"
-      },
-      {
-        "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/execute/1/714c1c17c5e6ea5ef1e710d5ccc62e492575bab5216184d4c27dc0164db1bc06/",
-        "rel": "capability"
-      }
-    ],
-    "metadata": {}
-  }
-}
-`
-
-// WebhookUpdateRequest contains the canned body of a webhooks.Update request.
-const WebhookUpdateRequest = `
-{
-  "name": "updated hook",
-  "metadata": {
-    "new-key": "some data"
-  }
-}
-`
-
-var (
-	// FirstWebhook is a Webhook corresponding to the first result in WebhookListBody.
-	FirstWebhook = Webhook{
-		ID:   "2bd1822c-58c5-49fd-8b3d-ed44781a58d1",
-		Name: "first hook",
-		Links: []gophercloud.Link{
-			{
-				Href: "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/60b15dad-5ea1-43fa-9a12-a1d737b4da07/policies/2b48d247-0282-4b9d-8775-5c4b67e8e649/webhooks/2bd1822c-58c5-49fd-8b3d-ed44781a58d1/",
-				Rel:  "self",
-			},
-			{
-				Href: "https://dfw.autoscale.api.rackspacecloud.com/v1.0/execute/1/714c1c17c5e6ea5ef1e710d5ccc62e492575bab5216184d4c27dc0164db1bc06/",
-				Rel:  "capability",
-			},
-		},
-		Metadata: map[string]string{},
-	}
-
-	// SecondWebhook is a Webhook corresponding to the second result in WebhookListBody.
-	SecondWebhook = Webhook{
-		ID:   "76711c36-dfbe-4f5e-bea6-cded99690515",
-		Name: "second hook",
-		Links: []gophercloud.Link{
-			{
-				Href: "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/60b15dad-5ea1-43fa-9a12-a1d737b4da07/policies/2b48d247-0282-4b9d-8775-5c4b67e8e649/webhooks/76711c36-dfbe-4f5e-bea6-cded99690515/",
-				Rel:  "self",
-			},
-			{
-				Href: "https://dfw.autoscale.api.rackspacecloud.com/v1.0/execute/1/982e24858723f9e8bc2afea42a73a3c357c8f518857735400a7f7d8b3f14ccdb/",
-				Rel:  "capability",
-			},
-		},
-		Metadata: map[string]string{
-			"notes": "a note about this webhook",
-		},
-	}
-)
-
-// HandleWebhookListSuccessfully sets up the test server to respond to a webhooks List request.
-func HandleWebhookListSuccessfully(t *testing.T) {
-	path := "/groups/10eb3219-1b12-4b34-b1e4-e10ee4f24c65/policies/2b48d247-0282-4b9d-8775-5c4b67e8e649/webhooks"
-
-	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-
-		fmt.Fprintf(w, WebhookListBody)
-	})
-}
-
-// HandleWebhookCreateSuccessfully sets up the test server to respond to a webhooks Create request.
-func HandleWebhookCreateSuccessfully(t *testing.T) {
-	path := "/groups/10eb3219-1b12-4b34-b1e4-e10ee4f24c65/policies/2b48d247-0282-4b9d-8775-5c4b67e8e649/webhooks"
-
-	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		th.TestJSONRequest(t, r, WebhookCreateRequest)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, WebhookCreateBody)
-	})
-}
-
-// HandleWebhookGetSuccessfully sets up the test server to respond to a webhooks Get request.
-func HandleWebhookGetSuccessfully(t *testing.T) {
-	groupID := "10eb3219-1b12-4b34-b1e4-e10ee4f24c65"
-	policyID := "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-	webhookID := "2bd1822c-58c5-49fd-8b3d-ed44781a58d1"
-
-	path := fmt.Sprintf("/groups/%s/policies/%s/webhooks/%s", groupID, policyID, webhookID)
-
-	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-
-		fmt.Fprintf(w, WebhookGetBody)
-	})
-}
-
-// HandleWebhookUpdateSuccessfully sets up the test server to respond to a webhooks Update request.
-func HandleWebhookUpdateSuccessfully(t *testing.T) {
-	groupID := "10eb3219-1b12-4b34-b1e4-e10ee4f24c65"
-	policyID := "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-	webhookID := "2bd1822c-58c5-49fd-8b3d-ed44781a58d1"
-
-	path := fmt.Sprintf("/groups/%s/policies/%s/webhooks/%s", groupID, policyID, webhookID)
-
-	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		th.TestJSONRequest(t, r, WebhookUpdateRequest)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-// HandleWebhookDeleteSuccessfully sets up the test server to respond to a webhooks Delete request.
-func HandleWebhookDeleteSuccessfully(t *testing.T) {
-	groupID := "10eb3219-1b12-4b34-b1e4-e10ee4f24c65"
-	policyID := "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-	webhookID := "2bd1822c-58c5-49fd-8b3d-ed44781a58d1"
-
-	path := fmt.Sprintf("/groups/%s/policies/%s/webhooks/%s", groupID, policyID, webhookID)
-
-	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
diff --git a/rackspace/autoscale/v1/webhooks/requests.go b/rackspace/autoscale/v1/webhooks/requests.go
deleted file mode 100644
index 2a88959..0000000
--- a/rackspace/autoscale/v1/webhooks/requests.go
+++ /dev/null
@@ -1,164 +0,0 @@
-package webhooks
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Validation errors returned by create or update operations.
-var (
-	ErrNoName     = errors.New("Webhook name cannot by empty.")
-	ErrNoMetadata = errors.New("Webhook metadata cannot be nil.")
-)
-
-// List returns all webhooks for a scaling policy.
-func List(client *gophercloud.ServiceClient, groupID, policyID string) pagination.Pager {
-	url := listURL(client, groupID, policyID)
-
-	createPageFn := func(r pagination.PageResult) pagination.Page {
-		return WebhookPage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, url, createPageFn)
-}
-
-// CreateOptsBuilder is the interface responsible for generating the JSON
-// for a Create operation.
-type CreateOptsBuilder interface {
-	ToWebhookCreateMap() ([]map[string]interface{}, error)
-}
-
-// CreateOpts is a slice of CreateOpt structs, that allow the user to create
-// multiple webhooks in a single operation.
-type CreateOpts []CreateOpt
-
-// CreateOpt represents the options to create a webhook.
-type CreateOpt struct {
-	// Name [required] is a name for the webhook.
-	Name string
-
-	// Metadata [optional] is user-provided key-value metadata.
-	// Maximum length for keys and values is 256 characters.
-	Metadata map[string]string
-}
-
-// ToWebhookCreateMap converts a slice of CreateOpt structs into a map for use
-// in the request body of a Create operation.
-func (opts CreateOpts) ToWebhookCreateMap() ([]map[string]interface{}, error) {
-	var webhooks []map[string]interface{}
-
-	for _, o := range opts {
-		if o.Name == "" {
-			return nil, ErrNoName
-		}
-
-		hook := make(map[string]interface{})
-
-		hook["name"] = o.Name
-
-		if o.Metadata != nil {
-			hook["metadata"] = o.Metadata
-		}
-
-		webhooks = append(webhooks, hook)
-	}
-
-	return webhooks, nil
-}
-
-// Create requests a new webhook be created and associated with the given group
-// and scaling policy.
-func Create(client *gophercloud.ServiceClient, groupID, policyID string, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToWebhookCreateMap()
-
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = client.Post(createURL(client, groupID, policyID), reqBody, &res.Body, nil)
-
-	return res
-}
-
-// Get requests the details of a single webhook with the given ID.
-func Get(client *gophercloud.ServiceClient, groupID, policyID, webhookID string) GetResult {
-	var result GetResult
-
-	_, result.Err = client.Get(getURL(client, groupID, policyID, webhookID), &result.Body, nil)
-
-	return result
-}
-
-// UpdateOptsBuilder is the interface responsible for generating the map
-// structure for producing JSON for an Update operation.
-type UpdateOptsBuilder interface {
-	ToWebhookUpdateMap() (map[string]interface{}, error)
-}
-
-// UpdateOpts represents the options for updating an existing webhook.
-//
-// Update operations completely replace the configuration being updated. Empty
-// values in the update are accepted and overwrite previously specified
-// parameters.
-type UpdateOpts struct {
-	// Name of the webhook.
-	Name string `mapstructure:"name" json:"name"`
-
-	// Metadata associated with the webhook.
-	Metadata map[string]string `mapstructure:"metadata" json:"metadata"`
-}
-
-// ToWebhookUpdateMap converts an UpdateOpts struct into a map for use as the
-// request body in an Update request.
-func (opts UpdateOpts) ToWebhookUpdateMap() (map[string]interface{}, error) {
-	if opts.Name == "" {
-		return nil, ErrNoName
-	}
-
-	if opts.Metadata == nil {
-		return nil, ErrNoMetadata
-	}
-
-	hook := make(map[string]interface{})
-
-	hook["name"] = opts.Name
-	hook["metadata"] = opts.Metadata
-
-	return hook, nil
-}
-
-// Update requests the configuration of the given webhook be updated.
-func Update(client *gophercloud.ServiceClient, groupID, policyID, webhookID string, opts UpdateOptsBuilder) UpdateResult {
-	var result UpdateResult
-
-	url := updateURL(client, groupID, policyID, webhookID)
-	reqBody, err := opts.ToWebhookUpdateMap()
-
-	if err != nil {
-		result.Err = err
-		return result
-	}
-
-	_, result.Err = client.Put(url, reqBody, nil, &gophercloud.RequestOpts{
-		OkCodes: []int{204},
-	})
-
-	return result
-}
-
-// Delete requests the given webhook be permanently deleted.
-func Delete(client *gophercloud.ServiceClient, groupID, policyID, webhookID string) DeleteResult {
-	var result DeleteResult
-
-	url := deleteURL(client, groupID, policyID, webhookID)
-	_, result.Err = client.Delete(url, &gophercloud.RequestOpts{
-		OkCodes: []int{204},
-	})
-
-	return result
-}
diff --git a/rackspace/autoscale/v1/webhooks/requests_test.go b/rackspace/autoscale/v1/webhooks/requests_test.go
deleted file mode 100644
index 950c90d..0000000
--- a/rackspace/autoscale/v1/webhooks/requests_test.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package webhooks
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const (
-	groupID  = "10eb3219-1b12-4b34-b1e4-e10ee4f24c65"
-	policyID = "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-	firstID  = "2bd1822c-58c5-49fd-8b3d-ed44781a58d1" // FirstWebhook
-	secondID = "76711c36-dfbe-4f5e-bea6-cded99690515" // SecondWebhook
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleWebhookListSuccessfully(t)
-
-	pages := 0
-	pager := List(client.ServiceClient(), groupID, policyID)
-
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		webhooks, err := ExtractWebhooks(page)
-
-		if err != nil {
-			return false, err
-		}
-
-		if len(webhooks) != 2 {
-			t.Fatalf("Expected 2 policies, got %d", len(webhooks))
-		}
-
-		th.CheckDeepEquals(t, FirstWebhook, webhooks[0])
-		th.CheckDeepEquals(t, SecondWebhook, webhooks[1])
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-
-	if pages != 1 {
-		t.Errorf("Expected 1 page, saw %d", pages)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleWebhookCreateSuccessfully(t)
-
-	client := client.ServiceClient()
-	opts := CreateOpts{
-		{
-			Name: "first hook",
-		},
-		{
-			Name: "second hook",
-			Metadata: map[string]string{
-				"notes": "a note about this webhook",
-			},
-		},
-	}
-
-	webhooks, err := Create(client, groupID, policyID, opts).Extract()
-
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, FirstWebhook, webhooks[0])
-	th.CheckDeepEquals(t, SecondWebhook, webhooks[1])
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleWebhookGetSuccessfully(t)
-
-	client := client.ServiceClient()
-
-	webhook, err := Get(client, groupID, policyID, firstID).Extract()
-
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, FirstWebhook, *webhook)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleWebhookUpdateSuccessfully(t)
-
-	client := client.ServiceClient()
-	opts := UpdateOpts{
-		Name: "updated hook",
-		Metadata: map[string]string{
-			"new-key": "some data",
-		},
-	}
-
-	err := Update(client, groupID, policyID, firstID, opts).ExtractErr()
-
-	th.AssertNoErr(t, err)
-}
-
-func TestUpdateNoMetadata(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleWebhookUpdateSuccessfully(t)
-
-	client := client.ServiceClient()
-	opts := UpdateOpts{
-		Name: "updated hook",
-	}
-
-	err := Update(client, groupID, policyID, firstID, opts).ExtractErr()
-
-	th.AssertEquals(t, ErrNoMetadata, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleWebhookDeleteSuccessfully(t)
-
-	client := client.ServiceClient()
-	err := Delete(client, groupID, policyID, firstID).ExtractErr()
-
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/autoscale/v1/webhooks/results.go b/rackspace/autoscale/v1/webhooks/results.go
deleted file mode 100644
index ac64d56..0000000
--- a/rackspace/autoscale/v1/webhooks/results.go
+++ /dev/null
@@ -1,110 +0,0 @@
-package webhooks
-
-import (
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-type webhookResult struct {
-	gophercloud.Result
-}
-
-// Extract interprets any webhookResult as a Webhook, if possible.
-func (r webhookResult) Extract() (*Webhook, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		Webhook Webhook `mapstructure:"webhook"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.Webhook, err
-}
-
-// CreateResult represents the result of a create operation.
-type CreateResult struct {
-	webhookResult
-}
-
-// Extract extracts a slice of Webhooks from a CreateResult.  Multiple webhooks
-// can be created in a single operation, so the result of a create is always a
-// list of webhooks.
-func (res CreateResult) Extract() ([]Webhook, error) {
-	if res.Err != nil {
-		return nil, res.Err
-	}
-
-	return commonExtractWebhooks(res.Body)
-}
-
-// GetResult temporarily contains the response from a Get call.
-type GetResult struct {
-	webhookResult
-}
-
-// UpdateResult represents the result of an update operation.
-type UpdateResult struct {
-	gophercloud.ErrResult
-}
-
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-// Webhook represents a webhook associted with a scaling policy.
-type Webhook struct {
-	// UUID for the webhook.
-	ID string `mapstructure:"id" json:"id"`
-
-	// Name of the webhook.
-	Name string `mapstructure:"name" json:"name"`
-
-	// Links associated with the webhook, including the capability URL.
-	Links []gophercloud.Link `mapstructure:"links" json:"links"`
-
-	// Metadata associated with the webhook.
-	Metadata map[string]string `mapstructure:"metadata" json:"metadata"`
-}
-
-// WebhookPage is the page returned by a pager when traversing over a collection
-// of webhooks.
-type WebhookPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty returns true if a page contains no Webhook results.
-func (page WebhookPage) IsEmpty() (bool, error) {
-	hooks, err := ExtractWebhooks(page)
-
-	if err != nil {
-		return true, err
-	}
-
-	return len(hooks) == 0, nil
-}
-
-// ExtractWebhooks interprets the results of a single page from a List() call,
-// producing a slice of Webhooks.
-func ExtractWebhooks(page pagination.Page) ([]Webhook, error) {
-	return commonExtractWebhooks(page.(WebhookPage).Body)
-}
-
-func commonExtractWebhooks(body interface{}) ([]Webhook, error) {
-	var response struct {
-		Webhooks []Webhook `mapstructure:"webhooks"`
-	}
-
-	err := mapstructure.Decode(body, &response)
-
-	if err != nil {
-		return nil, err
-	}
-
-	return response.Webhooks, err
-}
diff --git a/rackspace/autoscale/v1/webhooks/urls.go b/rackspace/autoscale/v1/webhooks/urls.go
deleted file mode 100644
index f5432e1..0000000
--- a/rackspace/autoscale/v1/webhooks/urls.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package webhooks
-
-import "github.com/rackspace/gophercloud"
-
-func listURL(c *gophercloud.ServiceClient, groupID, policyID string) string {
-	return c.ServiceURL("groups", groupID, "policies", policyID, "webhooks")
-}
-
-func createURL(c *gophercloud.ServiceClient, groupID, policyID string) string {
-	return c.ServiceURL("groups", groupID, "policies", policyID, "webhooks")
-}
-
-func getURL(c *gophercloud.ServiceClient, groupID, policyID, webhookID string) string {
-	return c.ServiceURL("groups", groupID, "policies", policyID, "webhooks", webhookID)
-}
-
-func updateURL(c *gophercloud.ServiceClient, groupID, policyID, webhookID string) string {
-	return getURL(c, groupID, policyID, webhookID)
-}
-
-func deleteURL(c *gophercloud.ServiceClient, groupID, policyID, webhookID string) string {
-	return getURL(c, groupID, policyID, webhookID)
-}
diff --git a/rackspace/autoscale/v1/webhooks/urls_test.go b/rackspace/autoscale/v1/webhooks/urls_test.go
deleted file mode 100644
index 44661e6..0000000
--- a/rackspace/autoscale/v1/webhooks/urls_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package webhooks
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestListURL(t *testing.T) {
-	actual := listURL(endpointClient(), "123", "456")
-	expected := endpoint + "groups/123/policies/456/webhooks"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient(), "123", "456")
-	expected := endpoint + "groups/123/policies/456/webhooks"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "123", "456", "789")
-	expected := endpoint + "groups/123/policies/456/webhooks/789"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient(), "123", "456", "789")
-	expected := endpoint + "groups/123/policies/456/webhooks/789"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "123", "456", "789")
-	expected := endpoint + "groups/123/policies/456/webhooks/789"
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/rackspace/blockstorage/v1/snapshots/delegate.go b/rackspace/blockstorage/v1/snapshots/delegate.go
deleted file mode 100644
index 1cd1b6e..0000000
--- a/rackspace/blockstorage/v1/snapshots/delegate.go
+++ /dev/null
@@ -1,131 +0,0 @@
-package snapshots
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-
-	os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots"
-)
-
-func updateURL(c *gophercloud.ServiceClient, id string) string {
-	return c.ServiceURL("snapshots", id)
-}
-
-// CreateOptsBuilder allows extensions to add additional parameters to the
-// Create request.
-type CreateOptsBuilder interface {
-	ToSnapshotCreateMap() (map[string]interface{}, error)
-}
-
-// CreateOpts contains options for creating a Snapshot. This object is passed to
-// the snapshots.Create function. For more information about these parameters,
-// see the Snapshot object.
-type CreateOpts struct {
-	// REQUIRED
-	VolumeID string
-	// OPTIONAL
-	Description string
-	// OPTIONAL
-	Force bool
-	// OPTIONAL
-	Name string
-}
-
-// ToSnapshotCreateMap assembles a request body based on the contents of a
-// CreateOpts.
-func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-
-	if opts.VolumeID == "" {
-		return nil, errors.New("Required CreateOpts field 'VolumeID' not set.")
-	}
-
-	s["volume_id"] = opts.VolumeID
-
-	if opts.Description != "" {
-		s["display_description"] = opts.Description
-	}
-	if opts.Name != "" {
-		s["display_name"] = opts.Name
-	}
-	if opts.Force {
-		s["force"] = opts.Force
-	}
-
-	return map[string]interface{}{"snapshot": s}, nil
-}
-
-// Create will create a new Snapshot based on the values in CreateOpts. To
-// extract the Snapshot object from the response, call the Extract method on the
-// CreateResult.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	return CreateResult{os.Create(client, opts)}
-}
-
-// Delete will delete the existing Snapshot with the provided ID.
-func Delete(client *gophercloud.ServiceClient, id string) os.DeleteResult {
-	return os.Delete(client, id)
-}
-
-// Get retrieves the Snapshot with the provided ID. To extract the Snapshot
-// object from the response, call the Extract method on the GetResult.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	return GetResult{os.Get(client, id)}
-}
-
-// List returns Snapshots.
-func List(client *gophercloud.ServiceClient) pagination.Pager {
-	return os.List(client, os.ListOpts{})
-}
-
-// UpdateOptsBuilder is the interface options structs have to satisfy in order
-// to be used in the main Update operation in this package. Since many
-// extensions decorate or modify the common logic, it is useful for them to
-// satisfy a basic interface in order for them to be used.
-type UpdateOptsBuilder interface {
-	ToSnapshotUpdateMap() (map[string]interface{}, error)
-}
-
-// UpdateOpts is the common options struct used in this package's Update
-// operation.
-type UpdateOpts struct {
-	Name        string
-	Description string
-}
-
-// ToSnapshotUpdateMap casts a UpdateOpts struct to a map.
-func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
-
-	if opts.Name != "" {
-		s["display_name"] = opts.Name
-	}
-	if opts.Description != "" {
-		s["display_description"] = opts.Description
-	}
-
-	return map[string]interface{}{"snapshot": s}, nil
-}
-
-// Update accepts a UpdateOpts struct and updates an existing snapshot using the
-// values provided.
-func Update(c *gophercloud.ServiceClient, snapshotID string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToSnapshotUpdateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	// Send request to API
-	_, res.Err = c.Request("PUT", updateURL(c, snapshotID), gophercloud.RequestOpts{
-		JSONBody:     &reqBody,
-		JSONResponse: &res.Body,
-		OkCodes:      []int{200, 201},
-	})
-
-	return res
-}
diff --git a/rackspace/blockstorage/v1/snapshots/delegate_test.go b/rackspace/blockstorage/v1/snapshots/delegate_test.go
deleted file mode 100644
index 1a02b46..0000000
--- a/rackspace/blockstorage/v1/snapshots/delegate_test.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package snapshots
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const endpoint = "http://localhost:57909/v1/12345"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestUpdateURL(t *testing.T) {
-	actual := updateURL(endpointClient(), "foo")
-	expected := endpoint + "snapshots/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.MockListResponse(t)
-
-	count := 0
-
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractSnapshots(page)
-		if err != nil {
-			t.Errorf("Failed to extract snapshots: %v", err)
-			return false, err
-		}
-
-		expected := []Snapshot{
-			Snapshot{
-				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
-				Name: "snapshot-001",
-			},
-			Snapshot{
-				ID:   "96c3bda7-c82a-4f50-be73-ca7621794835",
-				Name: "snapshot-002",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertEquals(t, 1, count)
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.MockGetResponse(t)
-
-	v, err := Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, v.Name, "snapshot-001")
-	th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.MockCreateResponse(t)
-
-	options := &CreateOpts{VolumeID: "1234", Name: "snapshot-001"}
-	n, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.VolumeID, "1234")
-	th.AssertEquals(t, n.Name, "snapshot-001")
-	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.MockDeleteResponse(t)
-
-	res := Delete(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/rackspace/blockstorage/v1/snapshots/doc.go b/rackspace/blockstorage/v1/snapshots/doc.go
deleted file mode 100644
index ad6064f..0000000
--- a/rackspace/blockstorage/v1/snapshots/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package snapshots provides information and interaction with the snapshot
-// API resource for the Rackspace Block Storage service.
-package snapshots
diff --git a/rackspace/blockstorage/v1/snapshots/results.go b/rackspace/blockstorage/v1/snapshots/results.go
deleted file mode 100644
index c81644c..0000000
--- a/rackspace/blockstorage/v1/snapshots/results.go
+++ /dev/null
@@ -1,147 +0,0 @@
-package snapshots
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots"
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
-)
-
-// Status is the type used to represent a snapshot's status
-type Status string
-
-// Constants to use for supported statuses
-const (
-	Creating    Status = "CREATING"
-	Available   Status = "AVAILABLE"
-	Deleting    Status = "DELETING"
-	Error       Status = "ERROR"
-	DeleteError Status = "ERROR_DELETING"
-)
-
-// Snapshot is the Rackspace representation of an external block storage device.
-type Snapshot struct {
-	// The timestamp when this snapshot was created.
-	CreatedAt string `mapstructure:"created_at"`
-
-	// The human-readable description for this snapshot.
-	Description string `mapstructure:"display_description"`
-
-	// The human-readable name for this snapshot.
-	Name string `mapstructure:"display_name"`
-
-	// The UUID for this snapshot.
-	ID string `mapstructure:"id"`
-
-	// The random metadata associated with this snapshot. Note: unlike standard
-	// OpenStack snapshots, this cannot actually be set.
-	Metadata map[string]string `mapstructure:"metadata"`
-
-	// Indicates the current progress of the snapshot's backup procedure.
-	Progress string `mapstructure:"os-extended-snapshot-attributes:progress"`
-
-	// The project ID.
-	ProjectID string `mapstructure:"os-extended-snapshot-attributes:project_id"`
-
-	// The size of the volume which this snapshot backs up.
-	Size int `mapstructure:"size"`
-
-	// The status of the snapshot.
-	Status Status `mapstructure:"status"`
-
-	// The ID of the volume which this snapshot seeks to back up.
-	VolumeID string `mapstructure:"volume_id"`
-}
-
-// CreateResult represents the result of a create operation
-type CreateResult struct {
-	os.CreateResult
-}
-
-// GetResult represents the result of a get operation
-type GetResult struct {
-	os.GetResult
-}
-
-// UpdateResult represents the result of an update operation
-type UpdateResult struct {
-	gophercloud.Result
-}
-
-func commonExtract(resp interface{}, err error) (*Snapshot, error) {
-	if err != nil {
-		return nil, err
-	}
-
-	var respStruct struct {
-		Snapshot *Snapshot `json:"snapshot"`
-	}
-
-	err = mapstructure.Decode(resp, &respStruct)
-
-	return respStruct.Snapshot, err
-}
-
-// Extract will get the Snapshot object out of the GetResult object.
-func (r GetResult) Extract() (*Snapshot, error) {
-	return commonExtract(r.Body, r.Err)
-}
-
-// Extract will get the Snapshot object out of the CreateResult object.
-func (r CreateResult) Extract() (*Snapshot, error) {
-	return commonExtract(r.Body, r.Err)
-}
-
-// Extract will get the Snapshot object out of the UpdateResult object.
-func (r UpdateResult) Extract() (*Snapshot, error) {
-	return commonExtract(r.Body, r.Err)
-}
-
-// ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call.
-func ExtractSnapshots(page pagination.Page) ([]Snapshot, error) {
-	var response struct {
-		Snapshots []Snapshot `json:"snapshots"`
-	}
-
-	err := mapstructure.Decode(page.(os.ListResult).Body, &response)
-	return response.Snapshots, err
-}
-
-// WaitUntilComplete will continually poll a snapshot until it successfully
-// transitions to a specified state. It will do this for at most the number of
-// seconds specified.
-func (snapshot Snapshot) WaitUntilComplete(c *gophercloud.ServiceClient, timeout int) error {
-	return gophercloud.WaitFor(timeout, func() (bool, error) {
-		// Poll resource
-		current, err := Get(c, snapshot.ID).Extract()
-		if err != nil {
-			return false, err
-		}
-
-		// Has it been built yet?
-		if current.Progress == "100%" {
-			return true, nil
-		}
-
-		return false, nil
-	})
-}
-
-// WaitUntilDeleted will continually poll a snapshot until it has been
-// successfully deleted, i.e. returns a 404 status.
-func (snapshot Snapshot) WaitUntilDeleted(c *gophercloud.ServiceClient, timeout int) error {
-	return gophercloud.WaitFor(timeout, func() (bool, error) {
-		// Poll resource
-		_, err := Get(c, snapshot.ID).Extract()
-
-		// Check for a 404
-		if casted, ok := err.(*gophercloud.UnexpectedResponseCodeError); ok && casted.Actual == 404 {
-			return true, nil
-		} else if err != nil {
-			return false, err
-		}
-
-		return false, nil
-	})
-}
diff --git a/rackspace/blockstorage/v1/volumes/delegate.go b/rackspace/blockstorage/v1/volumes/delegate.go
deleted file mode 100644
index 4383494..0000000
--- a/rackspace/blockstorage/v1/volumes/delegate.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package volumes
-
-import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-type CreateOpts struct {
-	os.CreateOpts
-}
-
-func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) {
-	if opts.Size < 75 || opts.Size > 1024 {
-		return nil, fmt.Errorf("Size field must be between 75 and 1024")
-	}
-
-	return opts.CreateOpts.ToVolumeCreateMap()
-}
-
-// Create will create a new Volume based on the values in CreateOpts. To extract
-// the Volume object from the response, call the Extract method on the
-// CreateResult.
-func Create(client *gophercloud.ServiceClient, opts os.CreateOptsBuilder) CreateResult {
-	return CreateResult{os.Create(client, opts)}
-}
-
-// Delete will delete the existing Volume with the provided ID.
-func Delete(client *gophercloud.ServiceClient, id string) os.DeleteResult {
-	return os.Delete(client, id)
-}
-
-// Get retrieves the Volume with the provided ID. To extract the Volume object
-// from the response, call the Extract method on the GetResult.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	return GetResult{os.Get(client, id)}
-}
-
-// List returns volumes optionally limited by the conditions provided in ListOpts.
-func List(client *gophercloud.ServiceClient) pagination.Pager {
-	return os.List(client, os.ListOpts{})
-}
-
-// UpdateOpts contain options for updating an existing Volume. This object is passed
-// to the volumes.Update function. For more information about the parameters, see
-// the Volume object.
-type UpdateOpts struct {
-	// OPTIONAL
-	Name string
-	// OPTIONAL
-	Description string
-}
-
-// ToVolumeUpdateMap assembles a request body based on the contents of an
-// UpdateOpts.
-func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) {
-	v := make(map[string]interface{})
-
-	if opts.Description != "" {
-		v["display_description"] = opts.Description
-	}
-	if opts.Name != "" {
-		v["display_name"] = opts.Name
-	}
-
-	return map[string]interface{}{"volume": v}, nil
-}
-
-// Update will update the Volume with provided information. To extract the updated
-// Volume from the response, call the Extract method on the UpdateResult.
-func Update(client *gophercloud.ServiceClient, id string, opts os.UpdateOptsBuilder) UpdateResult {
-	return UpdateResult{os.Update(client, id, opts)}
-}
diff --git a/rackspace/blockstorage/v1/volumes/delegate_test.go b/rackspace/blockstorage/v1/volumes/delegate_test.go
deleted file mode 100644
index b6831f2..0000000
--- a/rackspace/blockstorage/v1/volumes/delegate_test.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package volumes
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
-	os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/testing"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.MockListResponse(t)
-
-	count := 0
-
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractVolumes(page)
-		if err != nil {
-			t.Errorf("Failed to extract volumes: %v", err)
-			return false, err
-		}
-
-		expected := []Volume{
-			Volume{
-				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
-				Name: "vol-001",
-			},
-			Volume{
-				ID:   "96c3bda7-c82a-4f50-be73-ca7621794835",
-				Name: "vol-002",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertEquals(t, 1, count)
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.MockGetResponse(t)
-
-	v, err := Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, v.Name, "vol-001")
-	th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.MockCreateResponse(t)
-
-	n, err := Create(fake.ServiceClient(), CreateOpts{volumes.CreateOpts{Size: 75}}).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Size, 4)
-	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestSizeRange(t *testing.T) {
-	_, err := Create(fake.ServiceClient(), CreateOpts{volumes.CreateOpts{Size: 1}}).Extract()
-	if err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-
-	_, err = Create(fake.ServiceClient(), CreateOpts{volumes.CreateOpts{Size: 2000}}).Extract()
-	if err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.MockDeleteResponse(t)
-
-	res := Delete(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.MockUpdateResponse(t)
-
-	options := &UpdateOpts{Name: "vol-002"}
-	v, err := Update(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, "vol-002", v.Name)
-}
diff --git a/rackspace/blockstorage/v1/volumes/doc.go b/rackspace/blockstorage/v1/volumes/doc.go
deleted file mode 100644
index b2be25c..0000000
--- a/rackspace/blockstorage/v1/volumes/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package volumes provides information and interaction with the volume
-// API resource for the Rackspace Block Storage service.
-package volumes
diff --git a/rackspace/blockstorage/v1/volumes/results.go b/rackspace/blockstorage/v1/volumes/results.go
deleted file mode 100644
index c7c2cc4..0000000
--- a/rackspace/blockstorage/v1/volumes/results.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package volumes
-
-import (
-	os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
-	"github.com/rackspace/gophercloud/pagination"
-
-	"github.com/mitchellh/mapstructure"
-)
-
-// Volume wraps an Openstack volume
-type Volume os.Volume
-
-// CreateResult represents the result of a create operation
-type CreateResult struct {
-	os.CreateResult
-}
-
-// GetResult represents the result of a get operation
-type GetResult struct {
-	os.GetResult
-}
-
-// UpdateResult represents the result of an update operation
-type UpdateResult struct {
-	os.UpdateResult
-}
-
-func commonExtract(resp interface{}, err error) (*Volume, error) {
-	if err != nil {
-		return nil, err
-	}
-
-	var respStruct struct {
-		Volume *Volume `json:"volume"`
-	}
-
-	err = mapstructure.Decode(resp, &respStruct)
-
-	return respStruct.Volume, err
-}
-
-// Extract will get the Volume object out of the GetResult object.
-func (r GetResult) Extract() (*Volume, error) {
-	return commonExtract(r.Body, r.Err)
-}
-
-// Extract will get the Volume object out of the CreateResult object.
-func (r CreateResult) Extract() (*Volume, error) {
-	return commonExtract(r.Body, r.Err)
-}
-
-// Extract will get the Volume object out of the UpdateResult object.
-func (r UpdateResult) Extract() (*Volume, error) {
-	return commonExtract(r.Body, r.Err)
-}
-
-// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call.
-func ExtractVolumes(page pagination.Page) ([]Volume, error) {
-	var response struct {
-		Volumes []Volume `json:"volumes"`
-	}
-
-	err := mapstructure.Decode(page.(os.ListResult).Body, &response)
-
-	return response.Volumes, err
-}
diff --git a/rackspace/blockstorage/v1/volumetypes/delegate.go b/rackspace/blockstorage/v1/volumetypes/delegate.go
deleted file mode 100644
index c96b3e4..0000000
--- a/rackspace/blockstorage/v1/volumetypes/delegate.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package volumetypes
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumetypes"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns all volume types.
-func List(client *gophercloud.ServiceClient) pagination.Pager {
-	return os.List(client)
-}
-
-// Get will retrieve the volume type with the provided ID. To extract the volume
-// type from the result, call the Extract method on the GetResult.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	return GetResult{os.Get(client, id)}
-}
diff --git a/rackspace/blockstorage/v1/volumetypes/delegate_test.go b/rackspace/blockstorage/v1/volumetypes/delegate_test.go
deleted file mode 100644
index 6e65c90..0000000
--- a/rackspace/blockstorage/v1/volumetypes/delegate_test.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package volumetypes
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumetypes"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.MockListResponse(t)
-
-	count := 0
-
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractVolumeTypes(page)
-		if err != nil {
-			t.Errorf("Failed to extract volume types: %v", err)
-			return false, err
-		}
-
-		expected := []VolumeType{
-			VolumeType{
-				ID:   "289da7f8-6440-407c-9fb4-7db01ec49164",
-				Name: "vol-type-001",
-				ExtraSpecs: map[string]interface{}{
-					"capabilities": "gpu",
-				},
-			},
-			VolumeType{
-				ID:         "96c3bda7-c82a-4f50-be73-ca7621794835",
-				Name:       "vol-type-002",
-				ExtraSpecs: map[string]interface{}{},
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertEquals(t, 1, count)
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.MockGetResponse(t)
-
-	vt, err := Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertDeepEquals(t, vt.ExtraSpecs, map[string]interface{}{"serverNumber": "2"})
-	th.AssertEquals(t, vt.Name, "vol-type-001")
-	th.AssertEquals(t, vt.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
diff --git a/rackspace/blockstorage/v1/volumetypes/doc.go b/rackspace/blockstorage/v1/volumetypes/doc.go
deleted file mode 100644
index 70122b7..0000000
--- a/rackspace/blockstorage/v1/volumetypes/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package volumetypes provides information and interaction with the volume type
-// API resource for the Rackspace Block Storage service.
-package volumetypes
diff --git a/rackspace/blockstorage/v1/volumetypes/results.go b/rackspace/blockstorage/v1/volumetypes/results.go
deleted file mode 100644
index 39c8d6f..0000000
--- a/rackspace/blockstorage/v1/volumetypes/results.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package volumetypes
-
-import (
-	"github.com/mitchellh/mapstructure"
-	os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumetypes"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-type VolumeType os.VolumeType
-
-type GetResult struct {
-	os.GetResult
-}
-
-// Extract will get the Volume Type struct out of the response.
-func (r GetResult) Extract() (*VolumeType, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
-		VolumeType *VolumeType `json:"volume_type" mapstructure:"volume_type"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.VolumeType, err
-}
-
-func ExtractVolumeTypes(page pagination.Page) ([]VolumeType, error) {
-	var response struct {
-		VolumeTypes []VolumeType `mapstructure:"volume_types"`
-	}
-
-	err := mapstructure.Decode(page.(os.ListResult).Body, &response)
-	return response.VolumeTypes, err
-}
diff --git a/rackspace/cdn/v1/base/delegate.go b/rackspace/cdn/v1/base/delegate.go
deleted file mode 100644
index 5af7e07..0000000
--- a/rackspace/cdn/v1/base/delegate.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package base
-
-import (
-	"github.com/rackspace/gophercloud"
-
-	os "github.com/rackspace/gophercloud/openstack/cdn/v1/base"
-)
-
-// Get retrieves the home document, allowing the user to discover the
-// entire API.
-func Get(c *gophercloud.ServiceClient) os.GetResult {
-	return os.Get(c)
-}
-
-// Ping retrieves a ping to the server.
-func Ping(c *gophercloud.ServiceClient) os.PingResult {
-	return os.Ping(c)
-}
diff --git a/rackspace/cdn/v1/base/delegate_test.go b/rackspace/cdn/v1/base/delegate_test.go
deleted file mode 100644
index 731fc6d..0000000
--- a/rackspace/cdn/v1/base/delegate_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package base
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/cdn/v1/base"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestGetHomeDocument(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGetSuccessfully(t)
-
-	actual, err := Get(fake.ServiceClient()).Extract()
-	th.CheckNoErr(t, err)
-
-	expected := os.HomeDocument{
-		"rel/cdn": map[string]interface{}{
-			"href-template": "services{?marker,limit}",
-			"href-vars": map[string]interface{}{
-				"marker": "param/marker",
-				"limit":  "param/limit",
-			},
-			"hints": map[string]interface{}{
-				"allow": []string{"GET"},
-				"formats": map[string]interface{}{
-					"application/json": map[string]interface{}{},
-				},
-			},
-		},
-	}
-	th.CheckDeepEquals(t, expected, *actual)
-}
-
-func TestPing(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandlePingSuccessfully(t)
-
-	err := Ping(fake.ServiceClient()).ExtractErr()
-	th.CheckNoErr(t, err)
-}
diff --git a/rackspace/cdn/v1/base/doc.go b/rackspace/cdn/v1/base/doc.go
deleted file mode 100644
index 5582306..0000000
--- a/rackspace/cdn/v1/base/doc.go
+++ /dev/null
@@ -1,4 +0,0 @@
-// Package base provides information and interaction with the base API
-// resource in the Rackspace CDN service. This API resource allows for
-// retrieving the Home Document and pinging the root URL.
-package base
diff --git a/rackspace/cdn/v1/flavors/delegate.go b/rackspace/cdn/v1/flavors/delegate.go
deleted file mode 100644
index 7152fa2..0000000
--- a/rackspace/cdn/v1/flavors/delegate.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package flavors
-
-import (
-	"github.com/rackspace/gophercloud"
-
-	os "github.com/rackspace/gophercloud/openstack/cdn/v1/flavors"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a single page of CDN flavors.
-func List(c *gophercloud.ServiceClient) pagination.Pager {
-	return os.List(c)
-}
-
-// Get retrieves a specific flavor based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) os.GetResult {
-	return os.Get(c, id)
-}
diff --git a/rackspace/cdn/v1/flavors/delegate_test.go b/rackspace/cdn/v1/flavors/delegate_test.go
deleted file mode 100644
index d6d299d..0000000
--- a/rackspace/cdn/v1/flavors/delegate_test.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package flavors
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/cdn/v1/flavors"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.HandleListCDNFlavorsSuccessfully(t)
-
-	count := 0
-
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractFlavors(page)
-		if err != nil {
-			t.Errorf("Failed to extract flavors: %v", err)
-			return false, err
-		}
-
-		expected := []os.Flavor{
-			os.Flavor{
-				ID: "europe",
-				Providers: []os.Provider{
-					os.Provider{
-						Provider: "Fastly",
-						Links: []gophercloud.Link{
-							gophercloud.Link{
-								Href: "http://www.fastly.com",
-								Rel:  "provider_url",
-							},
-						},
-					},
-				},
-				Links: []gophercloud.Link{
-					gophercloud.Link{
-						Href: "https://www.poppycdn.io/v1.0/flavors/europe",
-						Rel:  "self",
-					},
-				},
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.HandleGetCDNFlavorSuccessfully(t)
-
-	expected := &os.Flavor{
-		ID: "asia",
-		Providers: []os.Provider{
-			os.Provider{
-				Provider: "ChinaCache",
-				Links: []gophercloud.Link{
-					gophercloud.Link{
-						Href: "http://www.chinacache.com",
-						Rel:  "provider_url",
-					},
-				},
-			},
-		},
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "https://www.poppycdn.io/v1.0/flavors/asia",
-				Rel:  "self",
-			},
-		},
-	}
-
-	actual, err := Get(fake.ServiceClient(), "asia").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/rackspace/cdn/v1/flavors/doc.go b/rackspace/cdn/v1/flavors/doc.go
deleted file mode 100644
index 4ad966e..0000000
--- a/rackspace/cdn/v1/flavors/doc.go
+++ /dev/null
@@ -1,6 +0,0 @@
-// Package flavors provides information and interaction with the flavors API
-// resource in the Rackspace CDN service. This API resource allows for
-// listing flavors and retrieving a specific flavor.
-//
-// A flavor is a mapping configuration to a CDN provider.
-package flavors
diff --git a/rackspace/cdn/v1/serviceassets/delegate.go b/rackspace/cdn/v1/serviceassets/delegate.go
deleted file mode 100644
index 07c93a8..0000000
--- a/rackspace/cdn/v1/serviceassets/delegate.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package serviceassets
-
-import (
-	"github.com/rackspace/gophercloud"
-
-	os "github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets"
-)
-
-// Delete accepts a unique ID and deletes the CDN service asset associated with
-// it.
-func Delete(c *gophercloud.ServiceClient, id string, opts os.DeleteOptsBuilder) os.DeleteResult {
-	return os.Delete(c, id, opts)
-}
diff --git a/rackspace/cdn/v1/serviceassets/delegate_test.go b/rackspace/cdn/v1/serviceassets/delegate_test.go
deleted file mode 100644
index 328e168..0000000
--- a/rackspace/cdn/v1/serviceassets/delegate_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package serviceassets
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.HandleDeleteCDNAssetSuccessfully(t)
-
-	err := Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", nil).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/cdn/v1/serviceassets/doc.go b/rackspace/cdn/v1/serviceassets/doc.go
deleted file mode 100644
index 46b3d50..0000000
--- a/rackspace/cdn/v1/serviceassets/doc.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// Package serviceassets provides information and interaction with the
-// serviceassets API resource in the Rackspace CDN service. This API resource
-// allows for deleting cached assets.
-//
-// A service distributes assets across the network. Service assets let you
-// interrogate properties about these assets and perform certain actions on them.
-package serviceassets
diff --git a/rackspace/cdn/v1/services/delegate.go b/rackspace/cdn/v1/services/delegate.go
deleted file mode 100644
index e3f1459..0000000
--- a/rackspace/cdn/v1/services/delegate.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package services
-
-import (
-	"github.com/rackspace/gophercloud"
-
-	os "github.com/rackspace/gophercloud/openstack/cdn/v1/services"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a Pager which allows you to iterate over a collection of
-// CDN services. It accepts a ListOpts struct, which allows for pagination via
-// marker and limit.
-func List(c *gophercloud.ServiceClient, opts os.ListOptsBuilder) pagination.Pager {
-	return os.List(c, opts)
-}
-
-// Create accepts a CreateOpts struct and creates a new CDN service using the
-// values provided.
-func Create(c *gophercloud.ServiceClient, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(c, opts)
-}
-
-// Get retrieves a specific service based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) os.GetResult {
-	return os.Get(c, id)
-}
-
-// Update accepts a UpdateOpts struct and updates an existing CDN service using
-// the values provided.
-func Update(c *gophercloud.ServiceClient, id string, patches []os.Patch) os.UpdateResult {
-	return os.Update(c, id, patches)
-}
-
-// Delete accepts a unique ID and deletes the CDN service associated with it.
-func Delete(c *gophercloud.ServiceClient, id string) os.DeleteResult {
-	return os.Delete(c, id)
-}
diff --git a/rackspace/cdn/v1/services/delegate_test.go b/rackspace/cdn/v1/services/delegate_test.go
deleted file mode 100644
index 6c48365..0000000
--- a/rackspace/cdn/v1/services/delegate_test.go
+++ /dev/null
@@ -1,359 +0,0 @@
-package services
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/cdn/v1/services"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.HandleListCDNServiceSuccessfully(t)
-
-	count := 0
-
-	err := List(fake.ServiceClient(), &os.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractServices(page)
-		if err != nil {
-			t.Errorf("Failed to extract services: %v", err)
-			return false, err
-		}
-
-		expected := []os.Service{
-			os.Service{
-				ID:   "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-				Name: "mywebsite.com",
-				Domains: []os.Domain{
-					os.Domain{
-						Domain: "www.mywebsite.com",
-					},
-				},
-				Origins: []os.Origin{
-					os.Origin{
-						Origin: "mywebsite.com",
-						Port:   80,
-						SSL:    false,
-					},
-				},
-				Caching: []os.CacheRule{
-					os.CacheRule{
-						Name: "default",
-						TTL:  3600,
-					},
-					os.CacheRule{
-						Name: "home",
-						TTL:  17200,
-						Rules: []os.TTLRule{
-							os.TTLRule{
-								Name:       "index",
-								RequestURL: "/index.htm",
-							},
-						},
-					},
-					os.CacheRule{
-						Name: "images",
-						TTL:  12800,
-						Rules: []os.TTLRule{
-							os.TTLRule{
-								Name:       "images",
-								RequestURL: "*.png",
-							},
-						},
-					},
-				},
-				Restrictions: []os.Restriction{
-					os.Restriction{
-						Name: "website only",
-						Rules: []os.RestrictionRule{
-							os.RestrictionRule{
-								Name:     "mywebsite.com",
-								Referrer: "www.mywebsite.com",
-							},
-						},
-					},
-				},
-				FlavorID: "asia",
-				Status:   "deployed",
-				Errors:   []os.Error{},
-				Links: []gophercloud.Link{
-					gophercloud.Link{
-						Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-						Rel:  "self",
-					},
-					gophercloud.Link{
-						Href: "mywebsite.com.cdn123.poppycdn.net",
-						Rel:  "access_url",
-					},
-					gophercloud.Link{
-						Href: "https://www.poppycdn.io/v1.0/flavors/asia",
-						Rel:  "flavor",
-					},
-				},
-			},
-			os.Service{
-				ID:   "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
-				Name: "myothersite.com",
-				Domains: []os.Domain{
-					os.Domain{
-						Domain: "www.myothersite.com",
-					},
-				},
-				Origins: []os.Origin{
-					os.Origin{
-						Origin: "44.33.22.11",
-						Port:   80,
-						SSL:    false,
-					},
-					os.Origin{
-						Origin: "77.66.55.44",
-						Port:   80,
-						SSL:    false,
-						Rules: []os.OriginRule{
-							os.OriginRule{
-								Name:       "videos",
-								RequestURL: "^/videos/*.m3u",
-							},
-						},
-					},
-				},
-				Caching: []os.CacheRule{
-					os.CacheRule{
-						Name: "default",
-						TTL:  3600,
-					},
-				},
-				Restrictions: []os.Restriction{},
-				FlavorID:     "europe",
-				Status:       "deployed",
-				Links: []gophercloud.Link{
-					gophercloud.Link{
-						Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
-						Rel:  "self",
-					},
-					gophercloud.Link{
-						Href: "myothersite.com.poppycdn.net",
-						Rel:  "access_url",
-					},
-					gophercloud.Link{
-						Href: "https://www.poppycdn.io/v1.0/flavors/europe",
-						Rel:  "flavor",
-					},
-				},
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.HandleCreateCDNServiceSuccessfully(t)
-
-	createOpts := os.CreateOpts{
-		Name: "mywebsite.com",
-		Domains: []os.Domain{
-			os.Domain{
-				Domain: "www.mywebsite.com",
-			},
-			os.Domain{
-				Domain: "blog.mywebsite.com",
-			},
-		},
-		Origins: []os.Origin{
-			os.Origin{
-				Origin: "mywebsite.com",
-				Port:   80,
-				SSL:    false,
-			},
-		},
-		Restrictions: []os.Restriction{
-			os.Restriction{
-				Name: "website only",
-				Rules: []os.RestrictionRule{
-					os.RestrictionRule{
-						Name:     "mywebsite.com",
-						Referrer: "www.mywebsite.com",
-					},
-				},
-			},
-		},
-		Caching: []os.CacheRule{
-			os.CacheRule{
-				Name: "default",
-				TTL:  3600,
-			},
-		},
-		FlavorID: "cdn",
-	}
-
-	expected := "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
-	actual, err := Create(fake.ServiceClient(), createOpts).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.HandleGetCDNServiceSuccessfully(t)
-
-	expected := &os.Service{
-		ID:   "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-		Name: "mywebsite.com",
-		Domains: []os.Domain{
-			os.Domain{
-				Domain:   "www.mywebsite.com",
-				Protocol: "http",
-			},
-		},
-		Origins: []os.Origin{
-			os.Origin{
-				Origin: "mywebsite.com",
-				Port:   80,
-				SSL:    false,
-			},
-		},
-		Caching: []os.CacheRule{
-			os.CacheRule{
-				Name: "default",
-				TTL:  3600,
-			},
-			os.CacheRule{
-				Name: "home",
-				TTL:  17200,
-				Rules: []os.TTLRule{
-					os.TTLRule{
-						Name:       "index",
-						RequestURL: "/index.htm",
-					},
-				},
-			},
-			os.CacheRule{
-				Name: "images",
-				TTL:  12800,
-				Rules: []os.TTLRule{
-					os.TTLRule{
-						Name:       "images",
-						RequestURL: "*.png",
-					},
-				},
-			},
-		},
-		Restrictions: []os.Restriction{
-			os.Restriction{
-				Name: "website only",
-				Rules: []os.RestrictionRule{
-					os.RestrictionRule{
-						Name:     "mywebsite.com",
-						Referrer: "www.mywebsite.com",
-					},
-				},
-			},
-		},
-		FlavorID: "cdn",
-		Status:   "deployed",
-		Errors:   []os.Error{},
-		Links: []gophercloud.Link{
-			gophercloud.Link{
-				Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
-				Rel:  "self",
-			},
-			gophercloud.Link{
-				Href: "blog.mywebsite.com.cdn1.raxcdn.com",
-				Rel:  "access_url",
-			},
-			gophercloud.Link{
-				Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/flavors/cdn",
-				Rel:  "flavor",
-			},
-		},
-	}
-
-	actual, err := Get(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestSuccessfulUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.HandleUpdateCDNServiceSuccessfully(t)
-
-	expected := "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
-	ops := []os.Patch{
-		// Append a single Domain
-		os.Append{Value: os.Domain{Domain: "appended.mocksite4.com"}},
-		// Insert a single Domain
-		os.Insertion{
-			Index: 4,
-			Value: os.Domain{Domain: "inserted.mocksite4.com"},
-		},
-		// Bulk addition
-		os.Append{
-			Value: os.DomainList{
-				os.Domain{Domain: "bulkadded1.mocksite4.com"},
-				os.Domain{Domain: "bulkadded2.mocksite4.com"},
-			},
-		},
-		// Replace a single Origin
-		os.Replacement{
-			Index: 2,
-			Value: os.Origin{Origin: "44.33.22.11", Port: 80, SSL: false},
-		},
-		// Bulk replace Origins
-		os.Replacement{
-			Index: 0, // Ignored
-			Value: os.OriginList{
-				os.Origin{Origin: "44.33.22.11", Port: 80, SSL: false},
-				os.Origin{Origin: "55.44.33.22", Port: 443, SSL: true},
-			},
-		},
-		// Remove a single CacheRule
-		os.Removal{
-			Index: 8,
-			Path:  os.PathCaching,
-		},
-		// Bulk removal
-		os.Removal{
-			All:  true,
-			Path: os.PathCaching,
-		},
-		// Service name replacement
-		os.NameReplacement{
-			NewName: "differentServiceName",
-		},
-	}
-
-	actual, err := Update(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", ops).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.HandleDeleteCDNServiceSuccessfully(t)
-
-	err := Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/cdn/v1/services/doc.go b/rackspace/cdn/v1/services/doc.go
deleted file mode 100644
index ee6e2a5..0000000
--- a/rackspace/cdn/v1/services/doc.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// Package services provides information and interaction with the services API
-// resource in the Rackspace CDN service. This API resource allows for
-// listing, creating, updating, retrieving, and deleting services.
-//
-// A service represents an application that has its content cached to the edge
-// nodes.
-package services
diff --git a/rackspace/client.go b/rackspace/client.go
deleted file mode 100644
index 09e9006..0000000
--- a/rackspace/client.go
+++ /dev/null
@@ -1,234 +0,0 @@
-package rackspace
-
-import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack"
-	"github.com/rackspace/gophercloud/openstack/utils"
-	tokens2 "github.com/rackspace/gophercloud/rackspace/identity/v2/tokens"
-)
-
-const (
-	// RackspaceUSIdentity is an identity endpoint located in the United States.
-	RackspaceUSIdentity = "https://identity.api.rackspacecloud.com/v2.0/"
-
-	// RackspaceUKIdentity is an identity endpoint located in the UK.
-	RackspaceUKIdentity = "https://lon.identity.api.rackspacecloud.com/v2.0/"
-)
-
-const (
-	v20 = "v2.0"
-)
-
-// NewClient creates a client that's prepared to communicate with the Rackspace API, but is not
-// yet authenticated. Most users will probably prefer using the AuthenticatedClient function
-// instead.
-//
-// Provide the base URL of the identity endpoint you wish to authenticate against as "endpoint".
-// Often, this will be either RackspaceUSIdentity or RackspaceUKIdentity.
-func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
-	if endpoint == "" {
-		return os.NewClient(RackspaceUSIdentity)
-	}
-	return os.NewClient(endpoint)
-}
-
-// AuthenticatedClient logs in to Rackspace with the provided credentials and constructs a
-// ProviderClient that's ready to operate.
-//
-// If the provided AuthOptions does not specify an explicit IdentityEndpoint, it will default to
-// the canonical, production Rackspace US identity endpoint.
-func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
-	client, err := NewClient(options.IdentityEndpoint)
-	if err != nil {
-		return nil, err
-	}
-
-	err = Authenticate(client, options)
-	if err != nil {
-		return nil, err
-	}
-	return client, nil
-}
-
-// Authenticate or re-authenticate against the most recent identity service supported at the
-// provided endpoint.
-func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
-	versions := []*utils.Version{
-		&utils.Version{ID: v20, Priority: 20, Suffix: "/v2.0/"},
-	}
-
-	chosen, endpoint, err := utils.ChooseVersion(client, versions)
-	if err != nil {
-		return err
-	}
-
-	switch chosen.ID {
-	case v20:
-		return v2auth(client, endpoint, options)
-	default:
-		// The switch statement must be out of date from the versions list.
-		return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
-	}
-}
-
-// AuthenticateV2 explicitly authenticates with v2 of the identity service.
-func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
-	return v2auth(client, "", options)
-}
-
-func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error {
-	v2Client := NewIdentityV2(client)
-	if endpoint != "" {
-		v2Client.Endpoint = endpoint
-	}
-
-	result := tokens2.Create(v2Client, tokens2.WrapOptions(options))
-
-	token, err := result.ExtractToken()
-	if err != nil {
-		return err
-	}
-
-	catalog, err := result.ExtractServiceCatalog()
-	if err != nil {
-		return err
-	}
-
-	if options.AllowReauth {
-		client.ReauthFunc = func() error {
-			return AuthenticateV2(client, options)
-		}
-	}
-	client.TokenID = token.ID
-	client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
-		return os.V2EndpointURL(catalog, opts)
-	}
-
-	return nil
-}
-
-// NewIdentityV2 creates a ServiceClient that may be used to access the v2 identity service.
-func NewIdentityV2(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
-	v2Endpoint := client.IdentityBase + "v2.0/"
-
-	return &gophercloud.ServiceClient{
-		ProviderClient: client,
-		Endpoint:       v2Endpoint,
-	}
-}
-
-// NewComputeV2 creates a ServiceClient that may be used to access the v2 compute service.
-func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("compute")
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-
-	return &gophercloud.ServiceClient{
-		ProviderClient: client,
-		Endpoint:       url,
-	}, nil
-}
-
-// NewObjectCDNV1 creates a ServiceClient that may be used with the Rackspace v1 CDN.
-func NewObjectCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("rax:object-cdn")
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
-}
-
-// NewObjectStorageV1 creates a ServiceClient that may be used with the Rackspace v1 object storage package.
-func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	return os.NewObjectStorageV1(client, eo)
-}
-
-// NewBlockStorageV1 creates a ServiceClient that can be used to access the
-// Rackspace Cloud Block Storage v1 API.
-func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("volume")
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-
-	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
-}
-
-// NewLBV1 creates a ServiceClient that can be used to access the Rackspace
-// Cloud Load Balancer v1 API.
-func NewLBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("rax:load-balancer")
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
-}
-
-// NewNetworkV2 creates a ServiceClient that can be used to access the Rackspace
-// Networking v2 API.
-func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("network")
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
-}
-
-// NewCDNV1 creates a ServiceClient that may be used to access the Rackspace v1
-// CDN service.
-func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("rax:cdn")
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
-}
-
-// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
-func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("orchestration")
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
-}
-
-// NewRackConnectV3 creates a ServiceClient that may be used to access the v3 RackConnect service.
-func NewRackConnectV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("rax:rackconnect")
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
-}
-
-// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
-func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("rax:database")
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
-}
-
-// NewAutoScaleV1 creates a ServiceClient that may be used to access the v1 Auto Scale service.
-func NewAutoScaleV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
-	eo.ApplyDefaults("rax:autoscale")
-	url, err := client.EndpointLocator(eo)
-	if err != nil {
-		return nil, err
-	}
-	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
-}
diff --git a/rackspace/client_test.go b/rackspace/client_test.go
deleted file mode 100644
index 73b1c88..0000000
--- a/rackspace/client_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package rackspace
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestAuthenticatedClientV2(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/tokens", func(w http.ResponseWriter, r *http.Request) {
-		fmt.Fprintf(w, `
-      {
-        "access": {
-          "token": {
-            "id": "01234567890",
-            "expires": "2014-10-01T10:00:00.000000Z"
-          },
-          "serviceCatalog": []
-        }
-      }
-    `)
-	})
-
-	options := gophercloud.AuthOptions{
-		Username:         "me",
-		APIKey:           "09876543210",
-		IdentityEndpoint: th.Endpoint() + "v2.0/",
-	}
-	client, err := AuthenticatedClient(options)
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, "01234567890", client.TokenID)
-}
diff --git a/rackspace/compute/v2/bootfromvolume/delegate.go b/rackspace/compute/v2/bootfromvolume/delegate.go
deleted file mode 100644
index 2580459..0000000
--- a/rackspace/compute/v2/bootfromvolume/delegate.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package bootfromvolume
-
-import (
-	"github.com/rackspace/gophercloud"
-	osBFV "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
-	osServers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-)
-
-// Create requests the creation of a server from the given block device mapping.
-func Create(client *gophercloud.ServiceClient, opts osServers.CreateOptsBuilder) osServers.CreateResult {
-	return osBFV.Create(client, opts)
-}
diff --git a/rackspace/compute/v2/bootfromvolume/delegate_test.go b/rackspace/compute/v2/bootfromvolume/delegate_test.go
deleted file mode 100644
index 571a1be..0000000
--- a/rackspace/compute/v2/bootfromvolume/delegate_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package bootfromvolume
-
-import (
-	"testing"
-
-	osBFV "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestCreateOpts(t *testing.T) {
-	base := servers.CreateOpts{
-		Name:      "createdserver",
-		ImageRef:  "asdfasdfasdf",
-		FlavorRef: "performance1-1",
-	}
-
-	ext := osBFV.CreateOptsExt{
-		CreateOptsBuilder: base,
-		BlockDevice: []osBFV.BlockDevice{
-			osBFV.BlockDevice{
-				UUID:            "123456",
-				SourceType:      osBFV.Image,
-				DestinationType: "volume",
-				VolumeSize:      10,
-			},
-		},
-	}
-
-	expected := `
-    {
-      "server": {
-        "name": "createdserver",
-        "imageRef": "asdfasdfasdf",
-        "flavorRef": "performance1-1",
-	"flavorName": "",
-	"imageName": "",
-        "block_device_mapping_v2":[
-          {
-            "uuid":"123456",
-            "source_type":"image",
-            "destination_type":"volume",
-            "boot_index": "0",
-            "delete_on_termination": "false",
-            "volume_size": "10"
-          }
-        ]
-      }
-    }
-  `
-	actual, err := ext.ToServerCreateMap()
-	th.AssertNoErr(t, err)
-	th.CheckJSONEquals(t, expected, actual)
-}
diff --git a/rackspace/compute/v2/flavors/delegate.go b/rackspace/compute/v2/flavors/delegate.go
deleted file mode 100644
index 081ea47..0000000
--- a/rackspace/compute/v2/flavors/delegate.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package flavors
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// ListOpts helps control the results returned by the List() function. For example, a flavor with a
-// minDisk field of 10 will not be returned if you specify MinDisk set to 20.
-type ListOpts struct {
-
-	// MinDisk and MinRAM, if provided, elide flavors that do not meet your criteria.
-	MinDisk int `q:"minDisk"`
-	MinRAM  int `q:"minRam"`
-
-	// Marker specifies the ID of the last flavor in the previous page.
-	Marker string `q:"marker"`
-
-	// Limit instructs List to refrain from sending excessively large lists of flavors.
-	Limit int `q:"limit"`
-}
-
-// ToFlavorListQuery formats a ListOpts into a query string.
-func (opts ListOpts) ToFlavorListQuery() (string, error) {
-	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
-}
-
-// ListDetail enumerates the server images available to your account.
-func ListDetail(client *gophercloud.ServiceClient, opts os.ListOptsBuilder) pagination.Pager {
-	return os.ListDetail(client, opts)
-}
-
-// Get returns details about a single flavor, identity by ID.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
-}
diff --git a/rackspace/compute/v2/flavors/delegate_test.go b/rackspace/compute/v2/flavors/delegate_test.go
deleted file mode 100644
index 204081d..0000000
--- a/rackspace/compute/v2/flavors/delegate_test.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package flavors
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListFlavors(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/flavors/detail", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, ListOutput)
-		case "performance1-2":
-			fmt.Fprintf(w, `{ "flavors": [] }`)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-
-	count := 0
-	err := ListDetail(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		actual, err := ExtractFlavors(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, ExpectedFlavorSlice, actual)
-
-		count++
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGetFlavor(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/flavors/performance1-1", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, GetOutput)
-	})
-
-	actual, err := Get(client.ServiceClient(), "performance1-1").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &Performance1Flavor, actual)
-}
diff --git a/rackspace/compute/v2/flavors/doc.go b/rackspace/compute/v2/flavors/doc.go
deleted file mode 100644
index 278229a..0000000
--- a/rackspace/compute/v2/flavors/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package flavors provides information and interaction with the flavor
-// API resource for the Rackspace Cloud Servers service.
-package flavors
diff --git a/rackspace/compute/v2/flavors/fixtures.go b/rackspace/compute/v2/flavors/fixtures.go
deleted file mode 100644
index 957dccf..0000000
--- a/rackspace/compute/v2/flavors/fixtures.go
+++ /dev/null
@@ -1,137 +0,0 @@
-// +build fixtures
-
-package flavors
-
-// ListOutput is a sample response of a flavor List request.
-const ListOutput = `
-{
-  "flavors": [
-    {
-      "OS-FLV-EXT-DATA:ephemeral": 0,
-      "OS-FLV-WITH-EXT-SPECS:extra_specs": {
-        "class": "performance1",
-        "disk_io_index": "40",
-        "number_of_data_disks": "0",
-        "policy_class": "performance_flavor",
-        "resize_policy_class": "performance_flavor"
-      },
-      "disk": 20,
-      "id": "performance1-1",
-      "links": [
-        {
-          "href": "https://iad.servers.api.rackspacecloud.com/v2/864477/flavors/performance1-1",
-          "rel": "self"
-        },
-        {
-          "href": "https://iad.servers.api.rackspacecloud.com/864477/flavors/performance1-1",
-          "rel": "bookmark"
-        }
-      ],
-      "name": "1 GB Performance",
-      "ram": 1024,
-      "rxtx_factor": 200,
-      "swap": "",
-      "vcpus": 1
-    },
-    {
-      "OS-FLV-EXT-DATA:ephemeral": 20,
-      "OS-FLV-WITH-EXT-SPECS:extra_specs": {
-        "class": "performance1",
-        "disk_io_index": "40",
-        "number_of_data_disks": "1",
-        "policy_class": "performance_flavor",
-        "resize_policy_class": "performance_flavor"
-      },
-      "disk": 40,
-      "id": "performance1-2",
-      "links": [
-        {
-          "href": "https://iad.servers.api.rackspacecloud.com/v2/864477/flavors/performance1-2",
-          "rel": "self"
-        },
-        {
-          "href": "https://iad.servers.api.rackspacecloud.com/864477/flavors/performance1-2",
-          "rel": "bookmark"
-        }
-      ],
-      "name": "2 GB Performance",
-      "ram": 2048,
-      "rxtx_factor": 400,
-      "swap": "",
-      "vcpus": 2
-    }
-  ]
-}`
-
-// GetOutput is a sample response from a flavor Get request. Its contents correspond to the
-// Performance1Flavor struct.
-const GetOutput = `
-{
-  "flavor": {
-    "OS-FLV-EXT-DATA:ephemeral": 0,
-    "OS-FLV-WITH-EXT-SPECS:extra_specs": {
-      "class": "performance1",
-      "disk_io_index": "40",
-      "number_of_data_disks": "0",
-      "policy_class": "performance_flavor",
-      "resize_policy_class": "performance_flavor"
-    },
-    "disk": 20,
-    "id": "performance1-1",
-    "links": [
-      {
-        "href": "https://iad.servers.api.rackspacecloud.com/v2/864477/flavors/performance1-1",
-        "rel": "self"
-      },
-      {
-        "href": "https://iad.servers.api.rackspacecloud.com/864477/flavors/performance1-1",
-        "rel": "bookmark"
-      }
-    ],
-    "name": "1 GB Performance",
-    "ram": 1024,
-    "rxtx_factor": 200,
-    "swap": "",
-    "vcpus": 1
-  }
-}
-`
-
-// Performance1Flavor is the expected result of parsing GetOutput, or the first element of
-// ListOutput.
-var Performance1Flavor = Flavor{
-	ID:         "performance1-1",
-	Disk:       20,
-	RAM:        1024,
-	Name:       "1 GB Performance",
-	RxTxFactor: 200.0,
-	Swap:       0,
-	VCPUs:      1,
-	ExtraSpecs: ExtraSpecs{
-		NumDataDisks: 0,
-		Class:        "performance1",
-		DiskIOIndex:  0,
-		PolicyClass:  "performance_flavor",
-	},
-}
-
-// Performance2Flavor is the second result expected from parsing ListOutput.
-var Performance2Flavor = Flavor{
-	ID:         "performance1-2",
-	Disk:       40,
-	RAM:        2048,
-	Name:       "2 GB Performance",
-	RxTxFactor: 400.0,
-	Swap:       0,
-	VCPUs:      2,
-	ExtraSpecs: ExtraSpecs{
-		NumDataDisks: 0,
-		Class:        "performance1",
-		DiskIOIndex:  0,
-		PolicyClass:  "performance_flavor",
-	},
-}
-
-// ExpectedFlavorSlice is the slice of Flavor structs that are expected to be parsed from
-// ListOutput.
-var ExpectedFlavorSlice = []Flavor{Performance1Flavor, Performance2Flavor}
diff --git a/rackspace/compute/v2/flavors/results.go b/rackspace/compute/v2/flavors/results.go
deleted file mode 100644
index af444a7..0000000
--- a/rackspace/compute/v2/flavors/results.go
+++ /dev/null
@@ -1,104 +0,0 @@
-package flavors
-
-import (
-	"reflect"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/mitchellh/mapstructure"
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// ExtraSpecs provide additional information about the flavor.
-type ExtraSpecs struct {
-	// The number of data disks
-	NumDataDisks int `mapstructure:"number_of_data_disks"`
-	// The flavor class
-	Class string `mapstructure:"class"`
-	// Relative measure of disk I/O performance from 0-99, where higher is faster
-	DiskIOIndex int    `mapstructure:"disk_io_index"`
-	PolicyClass string `mapstructure:"policy_class"`
-}
-
-// Flavor records represent (virtual) hardware configurations for server resources in a region.
-type Flavor struct {
-	// The Id field contains the flavor's unique identifier.
-	// For example, this identifier will be useful when specifying which hardware configuration to use for a new server instance.
-	ID string `mapstructure:"id"`
-
-	// The Disk and RA< fields provide a measure of storage space offered by the flavor, in GB and MB, respectively.
-	Disk int `mapstructure:"disk"`
-	RAM  int `mapstructure:"ram"`
-
-	// The Name field provides a human-readable moniker for the flavor.
-	Name string `mapstructure:"name"`
-
-	RxTxFactor float64 `mapstructure:"rxtx_factor"`
-
-	// Swap indicates how much space is reserved for swap.
-	// If not provided, this field will be set to 0.
-	Swap int `mapstructure:"swap"`
-
-	// VCPUs indicates how many (virtual) CPUs are available for this flavor.
-	VCPUs int `mapstructure:"vcpus"`
-
-	// ExtraSpecs provides extra information about the flavor
-	ExtraSpecs ExtraSpecs `mapstructure:"OS-FLV-WITH-EXT-SPECS:extra_specs"`
-}
-
-// GetResult temporarily holds the response from a Get call.
-type GetResult struct {
-	gophercloud.Result
-}
-
-// Extract provides access to the individual Flavor returned by the Get function.
-func (gr GetResult) Extract() (*Flavor, error) {
-	if gr.Err != nil {
-		return nil, gr.Err
-	}
-
-	var result struct {
-		Flavor Flavor `mapstructure:"flavor"`
-	}
-
-	cfg := &mapstructure.DecoderConfig{
-		DecodeHook: defaulter,
-		Result:     &result,
-	}
-	decoder, err := mapstructure.NewDecoder(cfg)
-	if err != nil {
-		return nil, err
-	}
-	err = decoder.Decode(gr.Body)
-	return &result.Flavor, err
-}
-
-func defaulter(from, to reflect.Kind, v interface{}) (interface{}, error) {
-	if (from == reflect.String) && (to == reflect.Int) {
-		return 0, nil
-	}
-	return v, nil
-}
-
-// ExtractFlavors provides access to the list of flavors in a page acquired from the List operation.
-func ExtractFlavors(page pagination.Page) ([]Flavor, error) {
-	casted := page.(os.FlavorPage).Body
-	var container struct {
-		Flavors []Flavor `mapstructure:"flavors"`
-	}
-
-	cfg := &mapstructure.DecoderConfig{
-		DecodeHook: defaulter,
-		Result:     &container,
-	}
-	decoder, err := mapstructure.NewDecoder(cfg)
-	if err != nil {
-		return container.Flavors, err
-	}
-	err = decoder.Decode(casted)
-	if err != nil {
-		return container.Flavors, err
-	}
-
-	return container.Flavors, nil
-}
diff --git a/rackspace/compute/v2/flavors/urls.go b/rackspace/compute/v2/flavors/urls.go
deleted file mode 100644
index f4e2c3d..0000000
--- a/rackspace/compute/v2/flavors/urls.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package flavors
-
-import (
-	"github.com/rackspace/gophercloud"
-)
-
-func getURL(client *gophercloud.ServiceClient, id string) string {
-	return client.ServiceURL("flavors", id)
-}
diff --git a/rackspace/compute/v2/images/delegate.go b/rackspace/compute/v2/images/delegate.go
deleted file mode 100644
index 18e1f31..0000000
--- a/rackspace/compute/v2/images/delegate.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package images
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/images"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// ListDetail enumerates the available server images.
-func ListDetail(client *gophercloud.ServiceClient, opts os.ListOptsBuilder) pagination.Pager {
-	return os.ListDetail(client, opts)
-}
-
-// Get acquires additional detail about a specific image by ID.
-func Get(client *gophercloud.ServiceClient, id string) os.GetResult {
-	return os.Get(client, id)
-}
-
-// ExtractImages interprets a page as a collection of server images.
-func ExtractImages(page pagination.Page) ([]os.Image, error) {
-	return os.ExtractImages(page)
-}
diff --git a/rackspace/compute/v2/images/delegate_test.go b/rackspace/compute/v2/images/delegate_test.go
deleted file mode 100644
index db0a6e3..0000000
--- a/rackspace/compute/v2/images/delegate_test.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package images
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListImageDetails(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/images/detail", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		r.ParseForm()
-		marker := r.Form.Get("marker")
-		switch marker {
-		case "":
-			fmt.Fprintf(w, ListOutput)
-		case "e19a734c-c7e6-443a-830c-242209c4d65d":
-			fmt.Fprintf(w, `{ "images": [] }`)
-		default:
-			t.Fatalf("Unexpected marker: [%s]", marker)
-		}
-	})
-
-	count := 0
-	err := ListDetail(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractImages(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, ExpectedImageSlice, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGetImageDetails(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/images/e19a734c-c7e6-443a-830c-242209c4d65d", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, GetOutput)
-	})
-
-	actual, err := Get(client.ServiceClient(), "e19a734c-c7e6-443a-830c-242209c4d65d").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &UbuntuImage, actual)
-}
diff --git a/rackspace/compute/v2/images/doc.go b/rackspace/compute/v2/images/doc.go
deleted file mode 100644
index cfae806..0000000
--- a/rackspace/compute/v2/images/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package images provides information and interaction with the image
-// API resource for the Rackspace Cloud Servers service.
-package images
diff --git a/rackspace/compute/v2/images/fixtures.go b/rackspace/compute/v2/images/fixtures.go
deleted file mode 100644
index cb8bda3..0000000
--- a/rackspace/compute/v2/images/fixtures.go
+++ /dev/null
@@ -1,246 +0,0 @@
-// +build fixtures
-
-package images
-
-import (
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/images"
-)
-
-// ListOutput is an example response from an /images/detail request.
-const ListOutput = `
-{
-	"images": [
-		{
-			"OS-DCF:diskConfig": "MANUAL",
-			"OS-EXT-IMG-SIZE:size": 1.017415075e+09,
-			"created": "2014-10-01T15:49:02Z",
-			"id": "30aa010e-080e-4d4b-a7f9-09fc55b07d69",
-			"links": [
-				{
-					"href": "https://iad.servers.api.rackspacecloud.com/v2/111222/images/30aa010e-080e-4d4b-a7f9-09fc55b07d69",
-					"rel": "self"
-				},
-				{
-					"href": "https://iad.servers.api.rackspacecloud.com/111222/images/30aa010e-080e-4d4b-a7f9-09fc55b07d69",
-					"rel": "bookmark"
-				},
-				{
-					"href": "https://iad.servers.api.rackspacecloud.com/111222/images/30aa010e-080e-4d4b-a7f9-09fc55b07d69",
-					"rel": "alternate",
-					"type": "application/vnd.openstack.image"
-				}
-			],
-			"metadata": {
-				"auto_disk_config": "disabled",
-				"cache_in_nova": "True",
-				"com.rackspace__1__build_core": "1",
-				"com.rackspace__1__build_managed": "1",
-				"com.rackspace__1__build_rackconnect": "1",
-				"com.rackspace__1__options": "0",
-				"com.rackspace__1__platform_target": "PublicCloud",
-				"com.rackspace__1__release_build_date": "2014-10-01_15-46-08",
-				"com.rackspace__1__release_id": "100",
-				"com.rackspace__1__release_version": "10",
-				"com.rackspace__1__source": "kickstart",
-				"com.rackspace__1__visible_core": "1",
-				"com.rackspace__1__visible_managed": "0",
-				"com.rackspace__1__visible_rackconnect": "0",
-				"image_type": "base",
-				"org.openstack__1__architecture": "x64",
-				"org.openstack__1__os_distro": "org.archlinux",
-				"org.openstack__1__os_version": "2014.8",
-				"os_distro": "arch",
-				"os_type": "linux",
-				"vm_mode": "hvm"
-			},
-			"minDisk": 20,
-			"minRam": 512,
-			"name": "Arch 2014.10 (PVHVM)",
-			"progress": 100,
-			"status": "ACTIVE",
-			"updated": "2014-10-01T19:37:58Z"
-		},
-		{
-			"OS-DCF:diskConfig": "AUTO",
-			"OS-EXT-IMG-SIZE:size": 1.060306463e+09,
-			"created": "2014-10-01T12:58:11Z",
-			"id": "e19a734c-c7e6-443a-830c-242209c4d65d",
-			"links": [
-				{
-					"href": "https://iad.servers.api.rackspacecloud.com/v2/111222/images/e19a734c-c7e6-443a-830c-242209c4d65d",
-					"rel": "self"
-				},
-				{
-					"href": "https://iad.servers.api.rackspacecloud.com/111222/images/e19a734c-c7e6-443a-830c-242209c4d65d",
-					"rel": "bookmark"
-				},
-				{
-					"href": "https://iad.servers.api.rackspacecloud.com/111222/images/e19a734c-c7e6-443a-830c-242209c4d65d",
-					"rel": "alternate",
-					"type": "application/vnd.openstack.image"
-				}
-			],
-			"metadata": {
-				"auto_disk_config": "True",
-				"cache_in_nova": "True",
-				"com.rackspace__1__build_core": "1",
-				"com.rackspace__1__build_managed": "1",
-				"com.rackspace__1__build_rackconnect": "1",
-				"com.rackspace__1__options": "0",
-				"com.rackspace__1__platform_target": "PublicCloud",
-				"com.rackspace__1__release_build_date": "2014-10-01_12-31-03",
-				"com.rackspace__1__release_id": "1007",
-				"com.rackspace__1__release_version": "6",
-				"com.rackspace__1__source": "kickstart",
-				"com.rackspace__1__visible_core": "1",
-				"com.rackspace__1__visible_managed": "1",
-				"com.rackspace__1__visible_rackconnect": "1",
-				"image_type": "base",
-				"org.openstack__1__architecture": "x64",
-				"org.openstack__1__os_distro": "com.ubuntu",
-				"org.openstack__1__os_version": "14.04",
-				"os_distro": "ubuntu",
-				"os_type": "linux",
-				"vm_mode": "xen"
-			},
-			"minDisk": 20,
-			"minRam": 512,
-			"name": "Ubuntu 14.04 LTS (Trusty Tahr)",
-			"progress": 100,
-			"status": "ACTIVE",
-			"updated": "2014-10-01T15:51:44Z"
-		}
-	]
-}
-`
-
-// GetOutput is an example response from an /images request.
-const GetOutput = `
-{
-	"image": {
-		"OS-DCF:diskConfig": "AUTO",
-		"OS-EXT-IMG-SIZE:size": 1060306463,
-		"created": "2014-10-01T12:58:11Z",
-		"id": "e19a734c-c7e6-443a-830c-242209c4d65d",
-		"links": [
-			{
-				"href": "https://iad.servers.api.rackspacecloud.com/v2/111222/images/e19a734c-c7e6-443a-830c-242209c4d65d",
-				"rel": "self"
-			},
-			{
-				"href": "https://iad.servers.api.rackspacecloud.com/111222/images/e19a734c-c7e6-443a-830c-242209c4d65d",
-				"rel": "bookmark"
-			},
-			{
-				"href": "https://iad.servers.api.rackspacecloud.com/111222/images/e19a734c-c7e6-443a-830c-242209c4d65d",
-				"rel": "alternate",
-				"type": "application/vnd.openstack.image"
-			}
-		],
-		"metadata": {
-			"auto_disk_config": "True",
-			"cache_in_nova": "True",
-			"com.rackspace__1__build_core": "1",
-			"com.rackspace__1__build_managed": "1",
-			"com.rackspace__1__build_rackconnect": "1",
-			"com.rackspace__1__options": "0",
-			"com.rackspace__1__platform_target": "PublicCloud",
-			"com.rackspace__1__release_build_date": "2014-10-01_12-31-03",
-			"com.rackspace__1__release_id": "1007",
-			"com.rackspace__1__release_version": "6",
-			"com.rackspace__1__source": "kickstart",
-			"com.rackspace__1__visible_core": "1",
-			"com.rackspace__1__visible_managed": "1",
-			"com.rackspace__1__visible_rackconnect": "1",
-			"image_type": "base",
-			"org.openstack__1__architecture": "x64",
-			"org.openstack__1__os_distro": "com.ubuntu",
-			"org.openstack__1__os_version": "14.04",
-			"os_distro": "ubuntu",
-			"os_type": "linux",
-			"vm_mode": "xen"
-		},
-		"minDisk": 20,
-		"minRam": 512,
-		"name": "Ubuntu 14.04 LTS (Trusty Tahr)",
-		"progress": 100,
-		"status": "ACTIVE",
-		"updated": "2014-10-01T15:51:44Z"
-	}
-}
-`
-
-// ArchImage is the first Image structure that should be parsed from ListOutput.
-var ArchImage = os.Image{
-	ID:       "30aa010e-080e-4d4b-a7f9-09fc55b07d69",
-	Name:     "Arch 2014.10 (PVHVM)",
-	Created:  "2014-10-01T15:49:02Z",
-	Updated:  "2014-10-01T19:37:58Z",
-	MinDisk:  20,
-	MinRAM:   512,
-	Progress: 100,
-	Status:   "ACTIVE",
-	Metadata: map[string]string{
-		"auto_disk_config":                      "True",
-		"cache_in_nova":                         "True",
-		"com.rackspace__1__build_core":          "1",
-		"com.rackspace__1__build_managed":       "1",
-		"com.rackspace__1__build_rackconnect":   "1",
-		"com.rackspace__1__options":             "0",
-		"com.rackspace__1__platform_target":     "PublicCloud",
-		"com.rackspace__1__release_build_date":  "2014-10-01_12-31-03",
-		"com.rackspace__1__release_id":          "1007",
-		"com.rackspace__1__release_version":     "6",
-		"com.rackspace__1__source":              "kickstart",
-		"com.rackspace__1__visible_core":        "1",
-		"com.rackspace__1__visible_managed":     "1",
-		"com.rackspace__1__visible_rackconnect": "1",
-		"image_type":                            "base",
-		"org.openstack__1__architecture":        "x64",
-		"org.openstack__1__os_distro":           "com.ubuntu",
-		"org.openstack__1__os_version":          "14.04",
-		"os_distro":                             "ubuntu",
-		"os_type":                               "linux",
-		"vm_mode":                               "xen",
-	},
-}
-
-// UbuntuImage is the second Image structure that should be parsed from ListOutput and
-// the only image that should be extracted from GetOutput.
-var UbuntuImage = os.Image{
-	ID:       "e19a734c-c7e6-443a-830c-242209c4d65d",
-	Name:     "Ubuntu 14.04 LTS (Trusty Tahr)",
-	Created:  "2014-10-01T12:58:11Z",
-	Updated:  "2014-10-01T15:51:44Z",
-	MinDisk:  20,
-	MinRAM:   512,
-	Progress: 100,
-	Status:   "ACTIVE",
-	Metadata: map[string]string{
-		"auto_disk_config":                      "True",
-		"cache_in_nova":                         "True",
-		"com.rackspace__1__build_core":          "1",
-		"com.rackspace__1__build_managed":       "1",
-		"com.rackspace__1__build_rackconnect":   "1",
-		"com.rackspace__1__options":             "0",
-		"com.rackspace__1__platform_target":     "PublicCloud",
-		"com.rackspace__1__release_build_date":  "2014-10-01_12-31-03",
-		"com.rackspace__1__release_id":          "1007",
-		"com.rackspace__1__release_version":     "6",
-		"com.rackspace__1__source":              "kickstart",
-		"com.rackspace__1__visible_core":        "1",
-		"com.rackspace__1__visible_managed":     "1",
-		"com.rackspace__1__visible_rackconnect": "1",
-		"image_type":                            "base",
-		"org.openstack__1__architecture":        "x64",
-		"org.openstack__1__os_distro":           "com.ubuntu",
-		"org.openstack__1__os_version":          "14.04",
-		"os_distro":                             "ubuntu",
-		"os_type":                               "linux",
-		"vm_mode":                               "xen",
-	},
-}
-
-// ExpectedImageSlice is the collection of images that should be parsed from ListOutput,
-// in order.
-var ExpectedImageSlice = []os.Image{ArchImage, UbuntuImage}
diff --git a/rackspace/compute/v2/keypairs/delegate.go b/rackspace/compute/v2/keypairs/delegate.go
deleted file mode 100644
index 3e53525..0000000
--- a/rackspace/compute/v2/keypairs/delegate.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package keypairs
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a Pager that allows you to iterate over a collection of KeyPairs.
-func List(client *gophercloud.ServiceClient) pagination.Pager {
-	return os.List(client)
-}
-
-// Create requests the creation of a new keypair on the server, or to import a pre-existing
-// keypair.
-func Create(client *gophercloud.ServiceClient, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(client, opts)
-}
-
-// Get returns public data about a previously uploaded KeyPair.
-func Get(client *gophercloud.ServiceClient, name string) os.GetResult {
-	return os.Get(client, name)
-}
-
-// Delete requests the deletion of a previous stored KeyPair from the server.
-func Delete(client *gophercloud.ServiceClient, name string) os.DeleteResult {
-	return os.Delete(client, name)
-}
-
-// ExtractKeyPairs interprets a page of results as a slice of KeyPairs.
-func ExtractKeyPairs(page pagination.Page) ([]os.KeyPair, error) {
-	return os.ExtractKeyPairs(page)
-}
diff --git a/rackspace/compute/v2/keypairs/delegate_test.go b/rackspace/compute/v2/keypairs/delegate_test.go
deleted file mode 100644
index 62e5df9..0000000
--- a/rackspace/compute/v2/keypairs/delegate_test.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package keypairs
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListSuccessfully(t)
-
-	count := 0
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractKeyPairs(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, os.ExpectedKeyPairSlice, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleCreateSuccessfully(t)
-
-	actual, err := Create(client.ServiceClient(), os.CreateOpts{
-		Name: "createdkey",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &os.CreatedKeyPair, actual)
-}
-
-func TestImport(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleImportSuccessfully(t)
-
-	actual, err := Create(client.ServiceClient(), os.CreateOpts{
-		Name:      "importedkey",
-		PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &os.ImportedKeyPair, actual)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGetSuccessfully(t)
-
-	actual, err := Get(client.ServiceClient(), "firstkey").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &os.FirstKeyPair, actual)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleDeleteSuccessfully(t)
-
-	err := Delete(client.ServiceClient(), "deletedkey").ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/compute/v2/keypairs/doc.go b/rackspace/compute/v2/keypairs/doc.go
deleted file mode 100644
index 3171375..0000000
--- a/rackspace/compute/v2/keypairs/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package keypairs provides information and interaction with the keypair
-// API resource for the Rackspace Cloud Servers service.
-package keypairs
diff --git a/rackspace/compute/v2/networks/doc.go b/rackspace/compute/v2/networks/doc.go
deleted file mode 100644
index 8e5c773..0000000
--- a/rackspace/compute/v2/networks/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package networks provides information and interaction with the network
-// API resource for the Rackspace Cloud Servers service.
-package networks
diff --git a/rackspace/compute/v2/networks/requests.go b/rackspace/compute/v2/networks/requests.go
deleted file mode 100644
index cebbffd..0000000
--- a/rackspace/compute/v2/networks/requests.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package networks
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a Pager which allows you to iterate over a collection of
-// networks. It accepts a ListOpts struct, which allows you to filter and sort
-// the returned collection for greater efficiency.
-func List(c *gophercloud.ServiceClient) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return NetworkPage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(c, listURL(c), createPage)
-}
-
-// Get retrieves a specific network based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
-}
-
-// CreateOptsBuilder is the interface options structs have to satisfy in order
-// to be used in the main Create operation in this package. Since many
-// extensions decorate or modify the common logic, it is useful for them to
-// satisfy a basic interface in order for them to be used.
-type CreateOptsBuilder interface {
-	ToNetworkCreateMap() (map[string]interface{}, error)
-}
-
-// CreateOpts is the common options struct used in this package's Create
-// operation.
-type CreateOpts struct {
-	// REQUIRED. See Network object for more info.
-	CIDR string
-	// REQUIRED. See Network object for more info.
-	Label string
-}
-
-// ToNetworkCreateMap casts a CreateOpts struct to a map.
-func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
-	n := make(map[string]interface{})
-
-	if opts.CIDR == "" {
-		return nil, errors.New("Required field CIDR not set.")
-	}
-	if opts.Label == "" {
-		return nil, errors.New("Required field Label not set.")
-	}
-
-	n["label"] = opts.Label
-	n["cidr"] = opts.CIDR
-	return map[string]interface{}{"network": n}, nil
-}
-
-// Create accepts a CreateOpts struct and creates a new network using the values
-// provided. This operation does not actually require a request body, i.e. the
-// CreateOpts struct argument can be empty.
-//
-// The tenant ID that is contained in the URI is the tenant that creates the
-// network. An admin user, however, has the option of specifying another tenant
-// ID in the CreateOpts struct.
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToNetworkCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	// Send request to API
-	_, res.Err = c.Post(createURL(c), reqBody, &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200, 201, 202},
-	})
-	return res
-}
-
-// Delete accepts a unique ID and deletes the network associated with it.
-func Delete(c *gophercloud.ServiceClient, networkID string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(deleteURL(c, networkID), nil)
-	return res
-}
diff --git a/rackspace/compute/v2/networks/requests_test.go b/rackspace/compute/v2/networks/requests_test.go
deleted file mode 100644
index 6f44c1c..0000000
--- a/rackspace/compute/v2/networks/requests_test.go
+++ /dev/null
@@ -1,156 +0,0 @@
-package networks
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/os-networksv2", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "networks": [
-        {
-            "label": "test-network-1",
-            "cidr": "192.168.100.0/24",
-            "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
-        },
-        {
-            "label": "test-network-2",
-            "cidr": "192.30.250.00/18",
-            "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324"
-        }
-    ]
-}
-      `)
-	})
-
-	client := fake.ServiceClient()
-	count := 0
-
-	err := List(client).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNetworks(page)
-		if err != nil {
-			t.Errorf("Failed to extract networks: %v", err)
-			return false, err
-		}
-
-		expected := []Network{
-			Network{
-				Label: "test-network-1",
-				CIDR:  "192.168.100.0/24",
-				ID:    "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-			},
-			Network{
-				Label: "test-network-2",
-				CIDR:  "192.30.250.00/18",
-				ID:    "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/os-networksv2/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "network": {
-        "label": "test-network-1",
-        "cidr": "192.168.100.0/24",
-        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
-    }
-}
-      `)
-	})
-
-	n, err := Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.CIDR, "192.168.100.0/24")
-	th.AssertEquals(t, n.Label, "test-network-1")
-	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/os-networksv2", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "network": {
-        "label": "test-network-1",
-        "cidr": "192.168.100.0/24"
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "network": {
-        "label": "test-network-1",
-        "cidr": "192.168.100.0/24",
-        "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c"
-    }
-}
-    `)
-	})
-
-	options := CreateOpts{Label: "test-network-1", CIDR: "192.168.100.0/24"}
-	n, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Label, "test-network-1")
-	th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/os-networksv2/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/rackspace/compute/v2/networks/results.go b/rackspace/compute/v2/networks/results.go
deleted file mode 100644
index eb6a76c..0000000
--- a/rackspace/compute/v2/networks/results.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package networks
-
-import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-type commonResult struct {
-	gophercloud.Result
-}
-
-// Extract is a function that accepts a result and extracts a network resource.
-func (r commonResult) Extract() (*Network, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
-		Network *Network `json:"network"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return res.Network, err
-}
-
-// CreateResult represents the result of a create operation.
-type CreateResult struct {
-	commonResult
-}
-
-// GetResult represents the result of a get operation.
-type GetResult struct {
-	commonResult
-}
-
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-// Network represents, well, a network.
-type Network struct {
-	// UUID for the network
-	ID string `mapstructure:"id" json:"id"`
-
-	// Human-readable name for the network. Might not be unique.
-	Label string `mapstructure:"label" json:"label"`
-
-	// Classless Inter-Domain Routing
-	CIDR string `mapstructure:"cidr" json:"cidr"`
-}
-
-// NetworkPage is the page returned by a pager when traversing over a
-// collection of networks.
-type NetworkPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty returns true if the NetworkPage contains no Networks.
-func (r NetworkPage) IsEmpty() (bool, error) {
-	networks, err := ExtractNetworks(r)
-	if err != nil {
-		return true, err
-	}
-	return len(networks) == 0, nil
-}
-
-// ExtractNetworks accepts a Page struct, specifically a NetworkPage struct,
-// and extracts the elements into a slice of Network structs. In other words,
-// a generic collection is mapped into a relevant slice.
-func ExtractNetworks(page pagination.Page) ([]Network, error) {
-	var resp struct {
-		Networks []Network `mapstructure:"networks" json:"networks"`
-	}
-
-	err := mapstructure.Decode(page.(NetworkPage).Body, &resp)
-
-	return resp.Networks, err
-}
diff --git a/rackspace/compute/v2/networks/urls.go b/rackspace/compute/v2/networks/urls.go
deleted file mode 100644
index 19a21aa..0000000
--- a/rackspace/compute/v2/networks/urls.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package networks
-
-import "github.com/rackspace/gophercloud"
-
-func resourceURL(c *gophercloud.ServiceClient, id string) string {
-	return c.ServiceURL("os-networksv2", id)
-}
-
-func rootURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL("os-networksv2")
-}
-
-func getURL(c *gophercloud.ServiceClient, id string) string {
-	return resourceURL(c, id)
-}
-
-func listURL(c *gophercloud.ServiceClient) string {
-	return rootURL(c)
-}
-
-func createURL(c *gophercloud.ServiceClient) string {
-	return rootURL(c)
-}
-
-func deleteURL(c *gophercloud.ServiceClient, id string) string {
-	return resourceURL(c, id)
-}
diff --git a/rackspace/compute/v2/networks/urls_test.go b/rackspace/compute/v2/networks/urls_test.go
deleted file mode 100644
index 983992e..0000000
--- a/rackspace/compute/v2/networks/urls_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package networks
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestGetURL(t *testing.T) {
-	actual := getURL(endpointClient(), "foo")
-	expected := endpoint + "os-networksv2/foo"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "os-networksv2"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestListURL(t *testing.T) {
-	actual := createURL(endpointClient())
-	expected := endpoint + "os-networksv2"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "foo")
-	expected := endpoint + "os-networksv2/foo"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/rackspace/compute/v2/servers/delegate.go b/rackspace/compute/v2/servers/delegate.go
deleted file mode 100644
index 7810d15..0000000
--- a/rackspace/compute/v2/servers/delegate.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package servers
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List makes a request against the API to list servers accessible to you.
-func List(client *gophercloud.ServiceClient, opts os.ListOptsBuilder) pagination.Pager {
-	return os.List(client, opts)
-}
-
-// Create requests a server to be provisioned to the user in the current tenant.
-func Create(client *gophercloud.ServiceClient, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(client, opts)
-}
-
-// Update requests an existing server to be updated with the supplied options.
-func Update(client *gophercloud.ServiceClient, id string, opts os.UpdateOptsBuilder) os.UpdateResult {
-	return os.Update(client, id, opts)
-}
-
-// Delete requests that a server previously provisioned be removed from your account.
-func Delete(client *gophercloud.ServiceClient, id string) os.DeleteResult {
-	return os.Delete(client, id)
-}
-
-// Get requests details on a single server, by ID.
-func Get(client *gophercloud.ServiceClient, id string) os.GetResult {
-	return os.Get(client, id)
-}
-
-// ChangeAdminPassword alters the administrator or root password for a specified server.
-func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) os.ActionResult {
-	return os.ChangeAdminPassword(client, id, newPassword)
-}
-
-// Reboot requests that a given server reboot. Two methods exist for rebooting a server:
-//
-// os.HardReboot (aka PowerCycle) restarts the server instance by physically cutting power to the
-// machine, or if a VM, terminating it at the hypervisor level. It's done. Caput. Full stop. Then,
-// after a brief wait, power is restored or the VM instance restarted.
-//
-// os.SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedures. E.g., in
-// Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to restart the machine.
-func Reboot(client *gophercloud.ServiceClient, id string, how os.RebootMethod) os.ActionResult {
-	return os.Reboot(client, id, how)
-}
-
-// Rebuild will reprovision the server according to the configuration options provided in the
-// RebuildOpts struct.
-func Rebuild(client *gophercloud.ServiceClient, id string, opts os.RebuildOptsBuilder) os.RebuildResult {
-	return os.Rebuild(client, id, opts)
-}
-
-// Resize instructs the provider to change the flavor of the server.
-// Note that this implies rebuilding it.
-// Unfortunately, one cannot pass rebuild parameters to the resize function.
-// When the resize completes, the server will be in RESIZE_VERIFY state.
-// While in this state, you can explore the use of the new server's configuration.
-// If you like it, call ConfirmResize() to commit the resize permanently.
-// Otherwise, call RevertResize() to restore the old configuration.
-func Resize(client *gophercloud.ServiceClient, id string, opts os.ResizeOptsBuilder) os.ActionResult {
-	return os.Resize(client, id, opts)
-}
-
-// ConfirmResize confirms a previous resize operation on a server.
-// See Resize() for more details.
-func ConfirmResize(client *gophercloud.ServiceClient, id string) os.ActionResult {
-	return os.ConfirmResize(client, id)
-}
-
-// WaitForStatus will continually poll a server until it successfully transitions to a specified
-// status. It will do this for at most the number of seconds specified.
-func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
-	return os.WaitForStatus(c, id, status, secs)
-}
-
-// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities.
-func ExtractServers(page pagination.Page) ([]os.Server, error) {
-	return os.ExtractServers(page)
-}
-
-// ListAddresses makes a request against the API to list the servers IP addresses.
-func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager {
-	return os.ListAddresses(client, id)
-}
-
-// ExtractAddresses interprets the results of a single page from a ListAddresses() call, producing a map of Address slices.
-func ExtractAddresses(page pagination.Page) (map[string][]os.Address, error) {
-	return os.ExtractAddresses(page)
-}
-
-// ListAddressesByNetwork makes a request against the API to list the servers IP addresses
-// for the given network.
-func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager {
-	return os.ListAddressesByNetwork(client, id, network)
-}
-
-// ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call, producing a map of Address slices.
-func ExtractNetworkAddresses(page pagination.Page) ([]os.Address, error) {
-	return os.ExtractNetworkAddresses(page)
-}
-
-// Metadata requests all the metadata for the given server ID.
-func Metadata(client *gophercloud.ServiceClient, id string) os.GetMetadataResult {
-	return os.Metadata(client, id)
-}
-
-// UpdateMetadata updates (or creates) all the metadata specified by opts for the given server ID.
-// This operation does not affect already-existing metadata that is not specified
-// by opts.
-func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts os.UpdateMetadataOptsBuilder) os.UpdateMetadataResult {
-	return os.UpdateMetadata(client, id, opts)
-}
diff --git a/rackspace/compute/v2/servers/delegate_test.go b/rackspace/compute/v2/servers/delegate_test.go
deleted file mode 100644
index 03e7ace..0000000
--- a/rackspace/compute/v2/servers/delegate_test.go
+++ /dev/null
@@ -1,182 +0,0 @@
-package servers
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListServers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, ListOutput)
-	})
-
-	count := 0
-	err := List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractServers(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, ExpectedServerSlice, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestCreateServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleServerCreationSuccessfully(t, CreateOutput)
-
-	actual, err := Create(client.ServiceClient(), os.CreateOpts{
-		Name:      "derp",
-		ImageRef:  "f90f6034-2570-4974-8351-6b49732ef2eb",
-		FlavorRef: "1",
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	th.CheckDeepEquals(t, &CreatedServer, actual)
-}
-
-func TestDeleteServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleServerDeletionSuccessfully(t)
-
-	res := Delete(client.ServiceClient(), "asdfasdfasdf")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestGetServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/servers/8c65cb68-0681-4c30-bc88-6b83a8a26aee", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		fmt.Fprintf(w, GetOutput)
-	})
-
-	actual, err := Get(client.ServiceClient(), "8c65cb68-0681-4c30-bc88-6b83a8a26aee").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &GophercloudServer, actual)
-}
-
-func TestUpdateServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/servers/8c65cb68-0681-4c30-bc88-6b83a8a26aee", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		th.TestJSONRequest(t, r, `{ "server": { "name": "test-server-updated" } }`)
-
-		w.Header().Add("Content-Type", "application/json")
-
-		fmt.Fprintf(w, UpdateOutput)
-	})
-
-	opts := os.UpdateOpts{
-		Name: "test-server-updated",
-	}
-	actual, err := Update(client.ServiceClient(), "8c65cb68-0681-4c30-bc88-6b83a8a26aee", opts).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &GophercloudUpdatedServer, actual)
-}
-
-func TestChangeAdminPassword(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleAdminPasswordChangeSuccessfully(t)
-
-	res := ChangeAdminPassword(client.ServiceClient(), "1234asdf", "new-password")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestReboot(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleRebootSuccessfully(t)
-
-	res := Reboot(client.ServiceClient(), "1234asdf", os.SoftReboot)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestRebuildServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleRebuildSuccessfully(t, GetOutput)
-
-	opts := os.RebuildOpts{
-		Name:       "new-name",
-		AdminPass:  "swordfish",
-		ImageID:    "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
-		AccessIPv4: "1.2.3.4",
-	}
-	actual, err := Rebuild(client.ServiceClient(), "1234asdf", opts).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &GophercloudServer, actual)
-}
-
-func TestListAddresses(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleAddressListSuccessfully(t)
-
-	expected := os.ListAddressesExpected
-	pages := 0
-	err := ListAddresses(client.ServiceClient(), "asdfasdfasdf").EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractAddresses(page)
-		th.AssertNoErr(t, err)
-
-		if len(actual) != 2 {
-			t.Fatalf("Expected 2 networks, got %d", len(actual))
-		}
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, pages)
-}
-
-func TestListAddressesByNetwork(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleNetworkAddressListSuccessfully(t)
-
-	expected := os.ListNetworkAddressesExpected
-	pages := 0
-	err := ListAddressesByNetwork(client.ServiceClient(), "asdfasdfasdf", "public").EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractNetworkAddresses(page)
-		th.AssertNoErr(t, err)
-
-		if len(actual) != 2 {
-			t.Fatalf("Expected 2 addresses, got %d", len(actual))
-		}
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, pages)
-}
diff --git a/rackspace/compute/v2/servers/doc.go b/rackspace/compute/v2/servers/doc.go
deleted file mode 100644
index c9f77f6..0000000
--- a/rackspace/compute/v2/servers/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package servers provides information and interaction with the server
-// API resource for the Rackspace Cloud Servers service.
-package servers
diff --git a/rackspace/compute/v2/servers/fixtures.go b/rackspace/compute/v2/servers/fixtures.go
deleted file mode 100644
index 75cccd0..0000000
--- a/rackspace/compute/v2/servers/fixtures.go
+++ /dev/null
@@ -1,574 +0,0 @@
-// +build fixtures
-
-package servers
-
-import (
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-)
-
-// ListOutput is the recorded output of a Rackspace servers.List request.
-const ListOutput = `
-{
-	"servers": [
-		{
-			"OS-DCF:diskConfig": "MANUAL",
-			"OS-EXT-STS:power_state": 1,
-			"OS-EXT-STS:task_state": null,
-			"OS-EXT-STS:vm_state": "active",
-			"accessIPv4": "1.2.3.4",
-			"accessIPv6": "1111:4822:7818:121:2000:9b5e:7438:a2d0",
-			"addresses": {
-				"private": [
-					{
-						"addr": "10.208.230.113",
-						"version": 4
-					}
-				],
-				"public": [
-					{
-						"addr": "2001:4800:7818:101:2000:9b5e:7428:a2d0",
-						"version": 6
-					},
-					{
-						"addr": "104.130.131.164",
-						"version": 4
-					}
-				]
-			},
-			"created": "2014-09-23T12:34:58Z",
-			"flavor": {
-				"id": "performance1-8",
-				"links": [
-					{
-						"href": "https://dfw.servers.api.rackspacecloud.com/111111/flavors/performance1-8",
-						"rel": "bookmark"
-					}
-				]
-			},
-			"hostId": "e8951a524bc465b0898aeac7674da6fe1495e253ae1ea17ddb2c2475",
-			"id": "59818cee-bc8c-44eb-8073-673ee65105f7",
-			"image": {
-				"id": "255df5fb-e3d4-45a3-9a07-c976debf7c14",
-				"links": [
-					{
-						"href": "https://dfw.servers.api.rackspacecloud.com/111111/images/255df5fb-e3d4-45a3-9a07-c976debf7c14",
-						"rel": "bookmark"
-					}
-				]
-			},
-			"key_name": "mykey",
-			"links": [
-				{
-					"href": "https://dfw.servers.api.rackspacecloud.com/v2/111111/servers/59818cee-bc8c-44eb-8073-673ee65105f7",
-					"rel": "self"
-				},
-				{
-					"href": "https://dfw.servers.api.rackspacecloud.com/111111/servers/59818cee-bc8c-44eb-8073-673ee65105f7",
-					"rel": "bookmark"
-				}
-			],
-			"metadata": {},
-			"name": "devstack",
-			"progress": 100,
-			"status": "ACTIVE",
-			"tenant_id": "111111",
-			"updated": "2014-09-23T12:38:19Z",
-			"user_id": "14ae7bb21d81422694655f3cc30f2930"
-		},
-		{
-			"OS-DCF:diskConfig": "MANUAL",
-			"OS-EXT-STS:power_state": 1,
-			"OS-EXT-STS:task_state": null,
-			"OS-EXT-STS:vm_state": "active",
-			"accessIPv4": "1.1.2.3",
-			"accessIPv6": "2222:4444:7817:101:be76:4eff:f0e5:9e02",
-			"addresses": {
-				"private": [
-					{
-						"addr": "10.10.20.30",
-						"version": 4
-					}
-				],
-				"public": [
-					{
-						"addr": "1.1.2.3",
-						"version": 4
-					},
-					{
-						"addr": "2222:4444:7817:101:be76:4eff:f0e5:9e02",
-						"version": 6
-					}
-				]
-			},
-			"created": "2014-07-21T19:32:55Z",
-			"flavor": {
-				"id": "performance1-2",
-				"links": [
-					{
-						"href": "https://dfw.servers.api.rackspacecloud.com/111111/flavors/performance1-2",
-						"rel": "bookmark"
-					}
-				]
-			},
-			"hostId": "f859679906d6b1a38c1bd516b78f4dcc7d5fcf012578fa3ce460716c",
-			"id": "25f1c7f5-e00a-4715-b354-16e24b2f4630",
-			"image": {
-				"id": "bb02b1a3-bc77-4d17-ab5b-421d89850fca",
-				"links": [
-					{
-						"href": "https://dfw.servers.api.rackspacecloud.com/111111/images/bb02b1a3-bc77-4d17-ab5b-421d89850fca",
-						"rel": "bookmark"
-					}
-				]
-			},
-			"key_name": "otherkey",
-			"links": [
-				{
-					"href": "https://dfw.servers.api.rackspacecloud.com/v2/111111/servers/25f1c7f5-e00a-4715-b355-16e24b2f4630",
-					"rel": "self"
-				},
-				{
-					"href": "https://dfw.servers.api.rackspacecloud.com/111111/servers/25f1c7f5-e00a-4715-b355-16e24b2f4630",
-					"rel": "bookmark"
-				}
-			],
-			"metadata": {},
-			"name": "peril-dfw",
-			"progress": 100,
-			"status": "ACTIVE",
-			"tenant_id": "111111",
-			"updated": "2014-07-21T19:34:24Z",
-			"user_id": "14ae7bb21d81422694655f3cc30f2930"
-		}
-	]
-}
-`
-
-// GetOutput is the recorded output of a Rackspace servers.Get request.
-const GetOutput = `
-{
-	"server": {
-		"OS-DCF:diskConfig": "AUTO",
-		"OS-EXT-STS:power_state": 1,
-		"OS-EXT-STS:task_state": null,
-		"OS-EXT-STS:vm_state": "active",
-		"accessIPv4": "1.2.4.8",
-		"accessIPv6": "2001:4800:6666:105:2a0f:c056:f594:7777",
-		"addresses": {
-			"private": [
-				{
-					"addr": "10.20.40.80",
-					"version": 4
-				}
-			],
-			"public": [
-				{
-					"addr": "1.2.4.8",
-					"version": 4
-				},
-				{
-					"addr": "2001:4800:6666:105:2a0f:c056:f594:7777",
-					"version": 6
-				}
-			]
-		},
-		"created": "2014-10-21T14:42:16Z",
-		"flavor": {
-			"id": "performance1-1",
-			"links": [
-				{
-					"href": "https://dfw.servers.api.rackspacecloud.com/111111/flavors/performance1-1",
-					"rel": "bookmark"
-				}
-			]
-		},
-		"hostId": "430d2ae02de0a7af77012c94778145eccf67e75b1fac0528aa10d4a7",
-		"id": "8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-		"image": {
-			"id": "e19a734c-c7e6-443a-830c-242209c4d65d",
-			"links": [
-				{
-					"href": "https://dfw.servers.api.rackspacecloud.com/111111/images/e19a734c-c7e6-443a-830c-242209c4d65d",
-					"rel": "bookmark"
-				}
-			]
-		},
-		"key_name": null,
-		"links": [
-			{
-				"href": "https://dfw.servers.api.rackspacecloud.com/v2/111111/servers/8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-				"rel": "self"
-			},
-			{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/servers/8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-				"rel": "bookmark"
-			}
-		],
-		"metadata": {},
-		"name": "Gophercloud-pxpGGuey",
-		"progress": 100,
-		"status": "ACTIVE",
-		"tenant_id": "111111",
-		"updated": "2014-10-21T14:42:57Z",
-		"user_id": "14ae7bb21d81423694655f4dd30f2930"
-	}
-}
-`
-
-// UpdateOutput is the recorded output of a Rackspace servers.Update request.
-const UpdateOutput = `
-{
-	"server": {
-		"OS-DCF:diskConfig": "AUTO",
-		"OS-EXT-STS:power_state": 1,
-		"OS-EXT-STS:task_state": null,
-		"OS-EXT-STS:vm_state": "active",
-		"accessIPv4": "1.2.4.8",
-		"accessIPv6": "2001:4800:6666:105:2a0f:c056:f594:7777",
-		"addresses": {
-			"private": [
-			{
-				"addr": "10.20.40.80",
-				"version": 4
-			}
-			],
-			"public": [
-			{
-				"addr": "1.2.4.8",
-				"version": 4
-				},
-				{
-					"addr": "2001:4800:6666:105:2a0f:c056:f594:7777",
-					"version": 6
-				}
-			]
-		},
-		"created": "2014-10-21T14:42:16Z",
-		"flavor": {
-			"id": "performance1-1",
-			"links": [
-			{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/flavors/performance1-1",
-				"rel": "bookmark"
-			}
-			]
-		},
-		"hostId": "430d2ae02de0a7af77012c94778145eccf67e75b1fac0528aa10d4a7",
-		"id": "8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-		"image": {
-			"id": "e19a734c-c7e6-443a-830c-242209c4d65d",
-			"links": [
-			{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/images/e19a734c-c7e6-443a-830c-242209c4d65d",
-				"rel": "bookmark"
-			}
-			]
-		},
-		"key_name": null,
-		"links": [
-		{
-			"href": "https://dfw.servers.api.rackspacecloud.com/v2/111111/servers/8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-			"rel": "self"
-		},
-		{
-			"href": "https://dfw.servers.api.rackspacecloud.com/111111/servers/8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-			"rel": "bookmark"
-		}
-		],
-		"metadata": {},
-		"name": "test-server-updated",
-		"progress": 100,
-		"status": "ACTIVE",
-		"tenant_id": "111111",
-		"updated": "2014-10-21T14:42:57Z",
-		"user_id": "14ae7bb21d81423694655f4dd30f2930"
-	}
-}
-`
-
-// CreateOutput contains a sample of Rackspace's response to a Create call.
-const CreateOutput = `
-{
-	"server": {
-		"OS-DCF:diskConfig": "AUTO",
-		"adminPass": "v7tADqbE5pr9",
-		"id": "bb63327b-6a2f-34bc-b0ef-4b6d97ea637e",
-		"links": [
-			{
-				"href": "https://dfw.servers.api.rackspacecloud.com/v2/111111/servers/bb63327b-6a2f-34bc-b0ef-4b6d97ea637e",
-				"rel": "self"
-			},
-			{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/servers/bb63327b-6a2f-34bc-b0ef-4b6d97ea637e",
-				"rel": "bookmark"
-			}
-		]
-	}
-}
-`
-
-// DevstackServer is the expected first result from parsing ListOutput.
-var DevstackServer = os.Server{
-	ID:         "59818cee-bc8c-44eb-8073-673ee65105f7",
-	Name:       "devstack",
-	TenantID:   "111111",
-	UserID:     "14ae7bb21d81422694655f3cc30f2930",
-	HostID:     "e8951a524bc465b0898aeac7674da6fe1495e253ae1ea17ddb2c2475",
-	Updated:    "2014-09-23T12:38:19Z",
-	Created:    "2014-09-23T12:34:58Z",
-	AccessIPv4: "1.2.3.4",
-	AccessIPv6: "1111:4822:7818:121:2000:9b5e:7438:a2d0",
-	Progress:   100,
-	Status:     "ACTIVE",
-	Image: map[string]interface{}{
-		"id": "255df5fb-e3d4-45a3-9a07-c976debf7c14",
-		"links": []interface{}{
-			map[string]interface{}{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/images/255df5fb-e3d4-45a3-9a07-c976debf7c14",
-				"rel":  "bookmark",
-			},
-		},
-	},
-	Flavor: map[string]interface{}{
-		"id": "performance1-8",
-		"links": []interface{}{
-			map[string]interface{}{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/flavors/performance1-8",
-				"rel":  "bookmark",
-			},
-		},
-	},
-	Addresses: map[string]interface{}{
-		"private": []interface{}{
-			map[string]interface{}{
-				"addr":    "10.20.30.40",
-				"version": float64(4.0),
-			},
-		},
-		"public": []interface{}{
-			map[string]interface{}{
-				"addr":    "1111:4822:7818:121:2000:9b5e:7438:a2d0",
-				"version": float64(6.0),
-			},
-			map[string]interface{}{
-				"addr":    "1.2.3.4",
-				"version": float64(4.0),
-			},
-		},
-	},
-	Metadata: map[string]interface{}{},
-	Links: []interface{}{
-		map[string]interface{}{
-			"href": "https://dfw.servers.api.rackspacecloud.com/v2/111111/servers/59918cee-bd9d-44eb-8173-673ee75105f7",
-			"rel":  "self",
-		},
-		map[string]interface{}{
-			"href": "https://dfw.servers.api.rackspacecloud.com/v2/111111/servers/59818cee-bc8c-44eb-8073-673ee65105f7",
-			"rel":  "bookmark",
-		},
-	},
-	KeyName:   "mykey",
-	AdminPass: "",
-}
-
-// PerilServer is the expected second result from parsing ListOutput.
-var PerilServer = os.Server{
-	ID:         "25f1c7f5-e00a-4715-b354-16e24b2f4630",
-	Name:       "peril-dfw",
-	TenantID:   "111111",
-	UserID:     "14ae7bb21d81422694655f3cc30f2930",
-	HostID:     "f859679906d6b1a38c1bd516b78f4dcc7d5fcf012578fa3ce460716c",
-	Updated:    "2014-07-21T19:34:24Z",
-	Created:    "2014-07-21T19:32:55Z",
-	AccessIPv4: "1.1.2.3",
-	AccessIPv6: "2222:4444:7817:101:be76:4eff:f0e5:9e02",
-	Progress:   100,
-	Status:     "ACTIVE",
-	Image: map[string]interface{}{
-		"id": "bb02b1a3-bc77-4d17-ab5b-421d89850fca",
-		"links": []interface{}{
-			map[string]interface{}{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/images/bb02b1a3-bc77-4d17-ab5b-421d89850fca",
-				"rel":  "bookmark",
-			},
-		},
-	},
-	Flavor: map[string]interface{}{
-		"id": "performance1-2",
-		"links": []interface{}{
-			map[string]interface{}{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/flavors/performance1-2",
-				"rel":  "bookmark",
-			},
-		},
-	},
-	Addresses: map[string]interface{}{
-		"private": []interface{}{
-			map[string]interface{}{
-				"addr":    "10.10.20.30",
-				"version": float64(4.0),
-			},
-		},
-		"public": []interface{}{
-			map[string]interface{}{
-				"addr":    "2222:4444:7817:101:be76:4eff:f0e5:9e02",
-				"version": float64(6.0),
-			},
-			map[string]interface{}{
-				"addr":    "1.1.2.3",
-				"version": float64(4.0),
-			},
-		},
-	},
-	Metadata: map[string]interface{}{},
-	Links: []interface{}{
-		map[string]interface{}{
-			"href": "https://dfw.servers.api.rackspacecloud.com/v2/111111/servers/25f1c7f5-e00a-4715-b355-16e24b2f4630",
-			"rel":  "self",
-		},
-		map[string]interface{}{
-			"href": "https://dfw.servers.api.rackspacecloud.com/v2/111111/servers/25f1c7f5-e00a-4715-b355-16e24b2f4630",
-			"rel":  "bookmark",
-		},
-	},
-	KeyName:   "otherkey",
-	AdminPass: "",
-}
-
-// GophercloudServer is the expected result from parsing GetOutput.
-var GophercloudServer = os.Server{
-	ID:         "8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-	Name:       "Gophercloud-pxpGGuey",
-	TenantID:   "111111",
-	UserID:     "14ae7bb21d81423694655f4dd30f2930",
-	HostID:     "430d2ae02de0a7af77012c94778145eccf67e75b1fac0528aa10d4a7",
-	Updated:    "2014-10-21T14:42:57Z",
-	Created:    "2014-10-21T14:42:16Z",
-	AccessIPv4: "1.2.4.8",
-	AccessIPv6: "2001:4800:6666:105:2a0f:c056:f594:7777",
-	Progress:   100,
-	Status:     "ACTIVE",
-	Image: map[string]interface{}{
-		"id": "e19a734c-c7e6-443a-830c-242209c4d65d",
-		"links": []interface{}{
-			map[string]interface{}{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/images/e19a734c-c7e6-443a-830c-242209c4d65d",
-				"rel":  "bookmark",
-			},
-		},
-	},
-	Flavor: map[string]interface{}{
-		"id": "performance1-1",
-		"links": []interface{}{
-			map[string]interface{}{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/flavors/performance1-1",
-				"rel":  "bookmark",
-			},
-		},
-	},
-	Addresses: map[string]interface{}{
-		"private": []interface{}{
-			map[string]interface{}{
-				"addr":    "10.20.40.80",
-				"version": float64(4.0),
-			},
-		},
-		"public": []interface{}{
-			map[string]interface{}{
-				"addr":    "2001:4800:6666:105:2a0f:c056:f594:7777",
-				"version": float64(6.0),
-			},
-			map[string]interface{}{
-				"addr":    "1.2.4.8",
-				"version": float64(4.0),
-			},
-		},
-	},
-	Metadata: map[string]interface{}{},
-	Links: []interface{}{
-		map[string]interface{}{
-			"href": "https://dfw.servers.api.rackspacecloud.com/v2/111111/servers/8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-			"rel":  "self",
-		},
-		map[string]interface{}{
-			"href": "https://dfw.servers.api.rackspacecloud.com/111111/servers/8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-			"rel":  "bookmark",
-		},
-	},
-	KeyName:   "",
-	AdminPass: "",
-}
-
-// GophercloudUpdatedServer is the expected result from parsing UpdateOutput.
-var GophercloudUpdatedServer = os.Server{
-	ID:         "8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-	Name:       "test-server-updated",
-	TenantID:   "111111",
-	UserID:     "14ae7bb21d81423694655f4dd30f2930",
-	HostID:     "430d2ae02de0a7af77012c94778145eccf67e75b1fac0528aa10d4a7",
-	Updated:    "2014-10-21T14:42:57Z",
-	Created:    "2014-10-21T14:42:16Z",
-	AccessIPv4: "1.2.4.8",
-	AccessIPv6: "2001:4800:6666:105:2a0f:c056:f594:7777",
-	Progress:   100,
-	Status:     "ACTIVE",
-	Image: map[string]interface{}{
-		"id": "e19a734c-c7e6-443a-830c-242209c4d65d",
-		"links": []interface{}{
-			map[string]interface{}{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/images/e19a734c-c7e6-443a-830c-242209c4d65d",
-				"rel":  "bookmark",
-			},
-		},
-	},
-	Flavor: map[string]interface{}{
-		"id": "performance1-1",
-		"links": []interface{}{
-			map[string]interface{}{
-				"href": "https://dfw.servers.api.rackspacecloud.com/111111/flavors/performance1-1",
-				"rel":  "bookmark",
-			},
-		},
-	},
-	Addresses: map[string]interface{}{
-		"private": []interface{}{
-			map[string]interface{}{
-				"addr":    "10.20.40.80",
-				"version": float64(4.0),
-			},
-		},
-		"public": []interface{}{
-			map[string]interface{}{
-				"addr":    "2001:4800:6666:105:2a0f:c056:f594:7777",
-				"version": float64(6.0),
-			},
-			map[string]interface{}{
-				"addr":    "1.2.4.8",
-				"version": float64(4.0),
-			},
-		},
-	},
-	Metadata: map[string]interface{}{},
-	Links: []interface{}{
-		map[string]interface{}{
-			"href": "https://dfw.servers.api.rackspacecloud.com/v2/111111/servers/8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-			"rel":  "self",
-		},
-		map[string]interface{}{
-			"href": "https://dfw.servers.api.rackspacecloud.com/111111/servers/8c65cb68-0681-4c30-bc88-6b83a8a26aee",
-			"rel":  "bookmark",
-		},
-	},
-	KeyName:   "",
-	AdminPass: "",
-}
-
-// CreatedServer is the partial Server struct that can be parsed from CreateOutput.
-var CreatedServer = os.Server{
-	ID:        "bb63327b-6a2f-34bc-b0ef-4b6d97ea637e",
-	AdminPass: "v7tADqbE5pr9",
-	Links:     []interface{}{},
-}
-
-// ExpectedServerSlice is the collection of servers, in order, that should be parsed from ListOutput.
-var ExpectedServerSlice = []os.Server{DevstackServer, PerilServer}
diff --git a/rackspace/compute/v2/servers/requests.go b/rackspace/compute/v2/servers/requests.go
deleted file mode 100644
index d4472a0..0000000
--- a/rackspace/compute/v2/servers/requests.go
+++ /dev/null
@@ -1,178 +0,0 @@
-package servers
-
-import (
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig"
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
-)
-
-// CreateOpts specifies all of the options that Rackspace accepts in its Create request, including
-// the union of all extensions that Rackspace supports.
-type CreateOpts struct {
-	// Name [required] is the name to assign to the newly launched server.
-	Name string
-
-	// ImageRef [optional; required if ImageName is not provided] is the ID or full
-	// URL to the image that contains the server's OS and initial state.
-	// Also optional if using the boot-from-volume extension.
-	ImageRef string
-
-	// ImageName [optional; required if ImageRef is not provided] is the name of the
-	// image that contains the server's OS and initial state.
-	// Also optional if using the boot-from-volume extension.
-	ImageName string
-
-	// FlavorRef [optional; required if FlavorName is not provided] is the ID or
-	// full URL to the flavor that describes the server's specs.
-	FlavorRef string
-
-	// FlavorName [optional; required if FlavorRef is not provided] is the name of
-	// the flavor that describes the server's specs.
-	FlavorName string
-
-	// SecurityGroups [optional] lists the names of the security groups to which this server should belong.
-	SecurityGroups []string
-
-	// UserData [optional] contains configuration information or scripts to use upon launch.
-	// Create will base64-encode it for you.
-	UserData []byte
-
-	// AvailabilityZone [optional] in which to launch the server.
-	AvailabilityZone string
-
-	// Networks [optional] dictates how this server will be attached to available networks.
-	// By default, the server will be attached to all isolated networks for the tenant.
-	Networks []os.Network
-
-	// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
-	Metadata map[string]string
-
-	// Personality [optional] includes files to inject into the server at launch.
-	// Create will base64-encode file contents for you.
-	Personality os.Personality
-
-	// ConfigDrive [optional] enables metadata injection through a configuration drive.
-	ConfigDrive bool
-
-	// AdminPass [optional] sets the root user password. If not set, a randomly-generated
-	// password will be created and returned in the response.
-	AdminPass string
-
-	// Rackspace-specific extensions begin here.
-
-	// KeyPair [optional] specifies the name of the SSH KeyPair to be injected into the newly launched
-	// server. See the "keypairs" extension in OpenStack compute v2.
-	KeyPair string
-
-	// DiskConfig [optional] controls how the created server's disk is partitioned. See the "diskconfig"
-	// extension in OpenStack compute v2.
-	DiskConfig diskconfig.DiskConfig
-
-	// BlockDevice [optional] will create the server from a volume, which is created from an image,
-	// a snapshot, or another volume.
-	BlockDevice []bootfromvolume.BlockDevice
-}
-
-// ToServerCreateMap constructs a request body using all of the OpenStack extensions that are
-// active on Rackspace.
-func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
-	base := os.CreateOpts{
-		Name:             opts.Name,
-		ImageRef:         opts.ImageRef,
-		ImageName:        opts.ImageName,
-		FlavorRef:        opts.FlavorRef,
-		FlavorName:       opts.FlavorName,
-		SecurityGroups:   opts.SecurityGroups,
-		UserData:         opts.UserData,
-		AvailabilityZone: opts.AvailabilityZone,
-		Networks:         opts.Networks,
-		Metadata:         opts.Metadata,
-		Personality:      opts.Personality,
-		ConfigDrive:      opts.ConfigDrive,
-		AdminPass:        opts.AdminPass,
-	}
-
-	drive := diskconfig.CreateOptsExt{
-		CreateOptsBuilder: base,
-		DiskConfig:        opts.DiskConfig,
-	}
-
-	res, err := drive.ToServerCreateMap()
-	if err != nil {
-		return nil, err
-	}
-
-	if len(opts.BlockDevice) != 0 {
-		bfv := bootfromvolume.CreateOptsExt{
-			CreateOptsBuilder: drive,
-			BlockDevice:       opts.BlockDevice,
-		}
-
-		res, err = bfv.ToServerCreateMap()
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	// key_name doesn't actually come from the extension (or at least isn't documented there) so
-	// we need to add it manually.
-	serverMap := res["server"].(map[string]interface{})
-	if opts.KeyPair != "" {
-		serverMap["key_name"] = opts.KeyPair
-	}
-
-	return res, nil
-}
-
-// RebuildOpts represents all of the configuration options used in a server rebuild operation that
-// are supported by Rackspace.
-type RebuildOpts struct {
-	// Required. The ID of the image you want your server to be provisioned on
-	ImageID string
-
-	// Name to set the server to
-	Name string
-
-	// Required. The server's admin password
-	AdminPass string
-
-	// AccessIPv4 [optional] provides a new IPv4 address for the instance.
-	AccessIPv4 string
-
-	// AccessIPv6 [optional] provides a new IPv6 address for the instance.
-	AccessIPv6 string
-
-	// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
-	Metadata map[string]string
-
-	// Personality [optional] includes files to inject into the server at launch.
-	// Rebuild will base64-encode file contents for you.
-	Personality os.Personality
-
-	// Rackspace-specific stuff begins here.
-
-	// DiskConfig [optional] controls how the created server's disk is partitioned. See the "diskconfig"
-	// extension in OpenStack compute v2.
-	DiskConfig diskconfig.DiskConfig
-}
-
-// ToServerRebuildMap constructs a request body using all of the OpenStack extensions that are
-// active on Rackspace.
-func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) {
-	base := os.RebuildOpts{
-		ImageID:     opts.ImageID,
-		Name:        opts.Name,
-		AdminPass:   opts.AdminPass,
-		AccessIPv4:  opts.AccessIPv4,
-		AccessIPv6:  opts.AccessIPv6,
-		Metadata:    opts.Metadata,
-		Personality: opts.Personality,
-	}
-
-	drive := diskconfig.RebuildOptsExt{
-		RebuildOptsBuilder: base,
-		DiskConfig:         opts.DiskConfig,
-	}
-
-	return drive.ToServerRebuildMap()
-}
diff --git a/rackspace/compute/v2/servers/requests_test.go b/rackspace/compute/v2/servers/requests_test.go
deleted file mode 100644
index 828b5dc..0000000
--- a/rackspace/compute/v2/servers/requests_test.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package servers
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestCreateOpts(t *testing.T) {
-	opts := CreateOpts{
-		Name:       "createdserver",
-		ImageRef:   "image-id",
-		FlavorRef:  "flavor-id",
-		KeyPair:    "mykey",
-		DiskConfig: diskconfig.Manual,
-	}
-
-	expected := `
-	{
-		"server": {
-			"name": "createdserver",
-			"imageRef": "image-id",
-			"flavorRef": "flavor-id",
-			"flavorName": "",
-			"imageName": "",
-			"key_name": "mykey",
-			"OS-DCF:diskConfig": "MANUAL"
-		}
-	}
-	`
-	actual, err := opts.ToServerCreateMap()
-	th.AssertNoErr(t, err)
-	th.CheckJSONEquals(t, expected, actual)
-}
-
-func TestRebuildOpts(t *testing.T) {
-	opts := RebuildOpts{
-		Name:       "rebuiltserver",
-		AdminPass:  "swordfish",
-		ImageID:    "asdfasdfasdf",
-		DiskConfig: diskconfig.Auto,
-	}
-
-	actual, err := opts.ToServerRebuildMap()
-	th.AssertNoErr(t, err)
-
-	expected := `
-	{
-		"rebuild": {
-			"name": "rebuiltserver",
-			"imageRef": "asdfasdfasdf",
-			"adminPass": "swordfish",
-			"OS-DCF:diskConfig": "AUTO"
-		}
-	}
-	`
-	th.CheckJSONEquals(t, expected, actual)
-}
diff --git a/rackspace/compute/v2/virtualinterfaces/requests.go b/rackspace/compute/v2/virtualinterfaces/requests.go
deleted file mode 100644
index 1ff7c5a..0000000
--- a/rackspace/compute/v2/virtualinterfaces/requests.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package virtualinterfaces
-
-import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a Pager which allows you to iterate over a collection of
-// networks. It accepts a ListOpts struct, which allows you to filter and sort
-// the returned collection for greater efficiency.
-func List(c *gophercloud.ServiceClient, instanceID string) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return VirtualInterfacePage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(c, listURL(c, instanceID), createPage)
-}
-
-// Create creates a new virtual interface for a network and attaches the network
-// to the server instance.
-func Create(c *gophercloud.ServiceClient, instanceID, networkID string) CreateResult {
-	var res CreateResult
-
-	reqBody := map[string]map[string]string{
-		"virtual_interface": {
-			"network_id": networkID,
-		},
-	}
-
-	// Send request to API
-	_, res.Err = c.Post(createURL(c, instanceID), reqBody, &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200, 201, 202},
-	})
-	return res
-}
-
-// Delete deletes the interface with interfaceID attached to the instance with
-// instanceID.
-func Delete(c *gophercloud.ServiceClient, instanceID, interfaceID string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(deleteURL(c, instanceID, interfaceID), &gophercloud.RequestOpts{
-		OkCodes: []int{200, 204},
-	})
-	return res
-}
diff --git a/rackspace/compute/v2/virtualinterfaces/requests_test.go b/rackspace/compute/v2/virtualinterfaces/requests_test.go
deleted file mode 100644
index d40af9c..0000000
--- a/rackspace/compute/v2/virtualinterfaces/requests_test.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package virtualinterfaces
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/servers/12345/os-virtual-interfacesv2", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "virtual_interfaces": [
-        {
-            "id": "de7c6d53-b895-4b4a-963c-517ccb0f0775",
-            "ip_addresses": [
-                {
-                    "address": "192.168.0.2",
-                    "network_id": "f212726e-6321-4210-9bae-a13f5a33f83f",
-                    "network_label": "superprivate_xml"
-                }
-            ],
-            "mac_address": "BC:76:4E:04:85:20"
-        },
-        {
-            "id": "e14e789d-3b98-44a6-9c2d-c23eb1d1465c",
-            "ip_addresses": [
-                {
-                    "address": "10.181.1.30",
-                    "network_id": "3b324a1b-31b8-4db5-9fe5-4a2067f60297",
-                    "network_label": "private"
-                }
-            ],
-            "mac_address": "BC:76:4E:04:81:55"
-        }
-    ]
-}
-      `)
-	})
-
-	client := fake.ServiceClient()
-	count := 0
-
-	err := List(client, "12345").EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractVirtualInterfaces(page)
-		if err != nil {
-			t.Errorf("Failed to extract networks: %v", err)
-			return false, err
-		}
-
-		expected := []VirtualInterface{
-			VirtualInterface{
-				MACAddress: "BC:76:4E:04:85:20",
-				IPAddresses: []IPAddress{
-					IPAddress{
-						Address:      "192.168.0.2",
-						NetworkID:    "f212726e-6321-4210-9bae-a13f5a33f83f",
-						NetworkLabel: "superprivate_xml",
-					},
-				},
-				ID: "de7c6d53-b895-4b4a-963c-517ccb0f0775",
-			},
-			VirtualInterface{
-				MACAddress: "BC:76:4E:04:81:55",
-				IPAddresses: []IPAddress{
-					IPAddress{
-						Address:      "10.181.1.30",
-						NetworkID:    "3b324a1b-31b8-4db5-9fe5-4a2067f60297",
-						NetworkLabel: "private",
-					},
-				},
-				ID: "e14e789d-3b98-44a6-9c2d-c23eb1d1465c",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/servers/12345/os-virtual-interfacesv2", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "virtual_interface": {
-        "network_id": "6789"
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `{
-      "virtual_interfaces": [
-        {
-          "id": "de7c6d53-b895-4b4a-963c-517ccb0f0775",
-          "ip_addresses": [
-            {
-              "address": "192.168.0.2",
-              "network_id": "f212726e-6321-4210-9bae-a13f5a33f83f",
-              "network_label": "superprivate_xml"
-            }
-          ],
-          "mac_address": "BC:76:4E:04:85:20"
-        }
-      ]
-    }`)
-	})
-
-	expected := &VirtualInterface{
-		MACAddress: "BC:76:4E:04:85:20",
-		IPAddresses: []IPAddress{
-			IPAddress{
-				Address:      "192.168.0.2",
-				NetworkID:    "f212726e-6321-4210-9bae-a13f5a33f83f",
-				NetworkLabel: "superprivate_xml",
-			},
-		},
-		ID: "de7c6d53-b895-4b4a-963c-517ccb0f0775",
-	}
-
-	actual, err := Create(fake.ServiceClient(), "12345", "6789").Extract()
-	th.AssertNoErr(t, err)
-
-	th.CheckDeepEquals(t, expected, actual)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/servers/12345/os-virtual-interfacesv2/6789", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "12345", "6789")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/rackspace/compute/v2/virtualinterfaces/results.go b/rackspace/compute/v2/virtualinterfaces/results.go
deleted file mode 100644
index 26fa7f3..0000000
--- a/rackspace/compute/v2/virtualinterfaces/results.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package virtualinterfaces
-
-import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-type commonResult struct {
-	gophercloud.Result
-}
-
-// Extract is a function that accepts a result and extracts a network resource.
-func (r commonResult) Extract() (*VirtualInterface, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var res struct {
-		VirtualInterfaces []VirtualInterface `mapstructure:"virtual_interfaces" json:"virtual_interfaces"`
-	}
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	return &res.VirtualInterfaces[0], err
-}
-
-// CreateResult represents the result of a create operation.
-type CreateResult struct {
-	commonResult
-}
-
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-// IPAddress represents a vitual address attached to a VirtualInterface.
-type IPAddress struct {
-	Address      string `mapstructure:"address" json:"address"`
-	NetworkID    string `mapstructure:"network_id" json:"network_id"`
-	NetworkLabel string `mapstructure:"network_label" json:"network_label"`
-}
-
-// VirtualInterface represents a virtual interface.
-type VirtualInterface struct {
-	// UUID for the virtual interface
-	ID string `mapstructure:"id" json:"id"`
-
-	MACAddress string `mapstructure:"mac_address" json:"mac_address"`
-
-	IPAddresses []IPAddress `mapstructure:"ip_addresses" json:"ip_addresses"`
-}
-
-// VirtualInterfacePage is the page returned by a pager when traversing over a
-// collection of virtual interfaces.
-type VirtualInterfacePage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty returns true if the NetworkPage contains no Networks.
-func (r VirtualInterfacePage) IsEmpty() (bool, error) {
-	networks, err := ExtractVirtualInterfaces(r)
-	if err != nil {
-		return true, err
-	}
-	return len(networks) == 0, nil
-}
-
-// ExtractVirtualInterfaces accepts a Page struct, specifically a VirtualInterfacePage struct,
-// and extracts the elements into a slice of VirtualInterface structs. In other words,
-// a generic collection is mapped into a relevant slice.
-func ExtractVirtualInterfaces(page pagination.Page) ([]VirtualInterface, error) {
-	var resp struct {
-		VirtualInterfaces []VirtualInterface `mapstructure:"virtual_interfaces" json:"virtual_interfaces"`
-	}
-
-	err := mapstructure.Decode(page.(VirtualInterfacePage).Body, &resp)
-
-	return resp.VirtualInterfaces, err
-}
diff --git a/rackspace/compute/v2/virtualinterfaces/urls.go b/rackspace/compute/v2/virtualinterfaces/urls.go
deleted file mode 100644
index 9e5693e..0000000
--- a/rackspace/compute/v2/virtualinterfaces/urls.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package virtualinterfaces
-
-import "github.com/rackspace/gophercloud"
-
-func listURL(c *gophercloud.ServiceClient, instanceID string) string {
-	return c.ServiceURL("servers", instanceID, "os-virtual-interfacesv2")
-}
-
-func createURL(c *gophercloud.ServiceClient, instanceID string) string {
-	return c.ServiceURL("servers", instanceID, "os-virtual-interfacesv2")
-}
-
-func deleteURL(c *gophercloud.ServiceClient, instanceID, interfaceID string) string {
-	return c.ServiceURL("servers", instanceID, "os-virtual-interfacesv2", interfaceID)
-}
diff --git a/rackspace/compute/v2/virtualinterfaces/urls_test.go b/rackspace/compute/v2/virtualinterfaces/urls_test.go
deleted file mode 100644
index 6732e4e..0000000
--- a/rackspace/compute/v2/virtualinterfaces/urls_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package virtualinterfaces
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestCreateURL(t *testing.T) {
-	actual := createURL(endpointClient(), "12345")
-	expected := endpoint + "servers/12345/os-virtual-interfacesv2"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestListURL(t *testing.T) {
-	actual := createURL(endpointClient(), "12345")
-	expected := endpoint + "servers/12345/os-virtual-interfacesv2"
-	th.AssertEquals(t, expected, actual)
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient(), "12345", "6789")
-	expected := endpoint + "servers/12345/os-virtual-interfacesv2/6789"
-	th.AssertEquals(t, expected, actual)
-}
diff --git a/rackspace/compute/v2/volumeattach/delegate.go b/rackspace/compute/v2/volumeattach/delegate.go
deleted file mode 100644
index c6003e0..0000000
--- a/rackspace/compute/v2/volumeattach/delegate.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package volumeattach
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a Pager that allows you to iterate over a collection of VolumeAttachments.
-func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
-	return os.List(client, serverID)
-}
-
-// Create requests the creation of a new volume attachment on the server
-func Create(client *gophercloud.ServiceClient, serverID string, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(client, serverID, opts)
-}
-
-// Get returns public data about a previously created VolumeAttachment.
-func Get(client *gophercloud.ServiceClient, serverID, aID string) os.GetResult {
-	return os.Get(client, serverID, aID)
-}
-
-// Delete requests the deletion of a previous stored VolumeAttachment from the server.
-func Delete(client *gophercloud.ServiceClient, serverID, aID string) os.DeleteResult {
-	return os.Delete(client, serverID, aID)
-}
diff --git a/rackspace/compute/v2/volumeattach/delegate_test.go b/rackspace/compute/v2/volumeattach/delegate_test.go
deleted file mode 100644
index f7ef45e..0000000
--- a/rackspace/compute/v2/volumeattach/delegate_test.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package volumeattach
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
-	fixtures "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/testing"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-// FirstVolumeAttachment is the first result in ListOutput.
-var FirstVolumeAttachment = volumeattach.VolumeAttachment{
-	Device:   "/dev/vdd",
-	ID:       "a26887c6-c47b-4654-abb5-dfadf7d3f803",
-	ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-	VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f803",
-}
-
-// SecondVolumeAttachment is the first result in ListOutput.
-var SecondVolumeAttachment = volumeattach.VolumeAttachment{
-	Device:   "/dev/vdc",
-	ID:       "a26887c6-c47b-4654-abb5-dfadf7d3f804",
-	ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-	VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
-}
-
-// ExpectedVolumeAttachmentSlide is the slice of results that should be parsed
-// from ListOutput, in the expected order.
-var ExpectedVolumeAttachmentSlice = []volumeattach.VolumeAttachment{FirstVolumeAttachment, SecondVolumeAttachment}
-
-//CreatedVolumeAttachment is the parsed result from CreatedOutput.
-var CreatedVolumeAttachment = volumeattach.VolumeAttachment{
-	Device:   "/dev/vdc",
-	ID:       "a26887c6-c47b-4654-abb5-dfadf7d3f804",
-	ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
-	VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixtures.HandleListSuccessfully(t)
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	count := 0
-	err := List(client.ServiceClient(), serverId).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := volumeattach.ExtractVolumeAttachments(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, ExpectedVolumeAttachmentSlice, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixtures.HandleCreateSuccessfully(t)
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	actual, err := Create(client.ServiceClient(), serverId, volumeattach.CreateOpts{
-		Device:   "/dev/vdc",
-		VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
-	}).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &CreatedVolumeAttachment, actual)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixtures.HandleGetSuccessfully(t)
-	aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	actual, err := Get(client.ServiceClient(), serverId, aId).Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, &SecondVolumeAttachment, actual)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixtures.HandleDeleteSuccessfully(t)
-	aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-
-	err := Delete(client.ServiceClient(), serverId, aId).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/compute/v2/volumeattach/doc.go b/rackspace/compute/v2/volumeattach/doc.go
deleted file mode 100644
index 2164908..0000000
--- a/rackspace/compute/v2/volumeattach/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package volumeattach provides the ability to attach and detach volume
-// to instances to Rackspace servers
-package volumeattach
diff --git a/rackspace/db/v1/backups/doc.go b/rackspace/db/v1/backups/doc.go
deleted file mode 100644
index 664eead..0000000
--- a/rackspace/db/v1/backups/doc.go
+++ /dev/null
@@ -1,6 +0,0 @@
-// Package backups provides information and interaction with the backup API
-// resource in the Rackspace Database service.
-//
-// A backup is a copy of a database instance that can be used to restore it to
-// some defined point in history.
-package backups
diff --git a/rackspace/db/v1/backups/fixtures.go b/rackspace/db/v1/backups/fixtures.go
deleted file mode 100644
index 6bbe651..0000000
--- a/rackspace/db/v1/backups/fixtures.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// +build fixtures
-
-package backups
-
-import "time"
-
-var (
-	timestamp  = "2015-11-12T14:22:42Z"
-	timeVal, _ = time.Parse(time.RFC3339, timestamp)
-)
-
-var getResp = `
-{
-  "backup": {
-    "created": "` + timestamp + `",
-    "description": "My Backup",
-    "id": "61f12fef-edb1-4561-8122-e7c00ef26a82",
-    "instance_id": "d4603f69-ec7e-4e9b-803f-600b9205576f",
-    "locationRef": null,
-    "name": "snapshot",
-    "parent_id": null,
-    "size": 100,
-    "status": "NEW",
-		"datastore": {
-			"version": "5.1",
-			"type": "MySQL",
-			"version_id": "20000000-0000-0000-0000-000000000002"
-		},
-    "updated": "` + timestamp + `"
-  }
-}
-`
-
-var createReq = `
-{
-  "backup": {
-    "description": "My Backup",
-    "instance": "d4603f69-ec7e-4e9b-803f-600b9205576f",
-    "name": "snapshot"
-  }
-}
-`
-
-var createResp = getResp
-
-var listResp = `
-{
-  "backups": [
-    {
-      "status": "COMPLETED",
-      "updated": "` + timestamp + `",
-      "description": "Backup from Restored Instance",
-      "datastore": {
-        "version": "5.1",
-        "type": "MySQL",
-        "version_id": "20000000-0000-0000-0000-000000000002"
-      },
-      "id": "87972694-4be2-40f5-83f8-501656e0032a",
-      "size": 0.141026,
-      "name": "restored_backup",
-      "created": "` + timestamp + `",
-      "instance_id": "29af2cd9-0674-48ab-b87a-b160f00208e6",
-      "parent_id": null,
-      "locationRef": "http://localhost/path/to/backup"
-    }
-  ]
-}
-`
diff --git a/rackspace/db/v1/backups/requests.go b/rackspace/db/v1/backups/requests.go
deleted file mode 100644
index 9170d78..0000000
--- a/rackspace/db/v1/backups/requests.go
+++ /dev/null
@@ -1,138 +0,0 @@
-package backups
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// CreateOptsBuilder is the top-level interface for creating JSON maps.
-type CreateOptsBuilder interface {
-	ToBackupCreateMap() (map[string]interface{}, error)
-}
-
-// CreateOpts is responsible for configuring newly provisioned backups.
-type CreateOpts struct {
-	// [REQUIRED] The name of the backup. The only restriction is the name must
-	// be less than 64 characters long.
-	Name string
-
-	// [REQUIRED] The ID of the instance being backed up.
-	InstanceID string
-
-	// [OPTIONAL] A human-readable explanation of the backup.
-	Description string
-}
-
-// ToBackupCreateMap will create a JSON map for the Create operation.
-func (opts CreateOpts) ToBackupCreateMap() (map[string]interface{}, error) {
-	if opts.Name == "" {
-		return nil, errors.New("Name is a required field")
-	}
-	if opts.InstanceID == "" {
-		return nil, errors.New("InstanceID is a required field")
-	}
-
-	backup := map[string]interface{}{
-		"name":     opts.Name,
-		"instance": opts.InstanceID,
-	}
-
-	if opts.Description != "" {
-		backup["description"] = opts.Description
-	}
-
-	return map[string]interface{}{"backup": backup}, nil
-}
-
-// Create asynchronously creates a new backup for a specified database instance.
-// During the backup process, write access on MyISAM databases will be
-// temporarily disabled; innoDB databases will be unaffected. During this time,
-// you will not be able to add or delete databases or users; nor delete, stop
-// or reboot the instance itself. Only one backup is permitted at once.
-//
-// Backups are not deleted when database instances are deleted; you must
-// manually delete any backups created using Delete(). Backups are saved to your
-// Cloud Files account in a new container called z_CLOUDDB_BACKUPS. It is
-// strongly recommended you do not alter this container or its contents; usual
-// storage costs apply.
-func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToBackupCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = client.Request("POST", baseURL(client), gophercloud.RequestOpts{
-		JSONBody:     &reqBody,
-		JSONResponse: &res.Body,
-		OkCodes:      []int{202},
-	})
-
-	return res
-}
-
-// ListOptsBuilder is the top-level interface for creating query strings.
-type ListOptsBuilder interface {
-	ToBackupListQuery() (string, error)
-}
-
-// ListOpts allows you to refine a list search by certain parameters.
-type ListOpts struct {
-	// The type of datastore by which to filter.
-	Datastore string `q:"datastore"`
-}
-
-// ToBackupListQuery converts a ListOpts struct into a query string.
-func (opts ListOpts) ToBackupListQuery() (string, error) {
-	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
-}
-
-// List will list all the saved backups for all database instances.
-func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
-	url := baseURL(client)
-
-	if opts != nil {
-		query, err := opts.ToBackupListQuery()
-		if err != nil {
-			return pagination.Pager{Err: err}
-		}
-		url += query
-	}
-
-	pageFn := func(r pagination.PageResult) pagination.Page {
-		return BackupPage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, url, pageFn)
-}
-
-// Get will retrieve details for a particular backup based on its unique ID.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-
-	_, res.Err = client.Request("GET", resourceURL(client, id), gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
-		OkCodes:      []int{200},
-	})
-
-	return res
-}
-
-// Delete will permanently delete a backup.
-func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-
-	_, res.Err = client.Request("DELETE", resourceURL(client, id), gophercloud.RequestOpts{
-		OkCodes: []int{202},
-	})
-
-	return res
-}
diff --git a/rackspace/db/v1/backups/requests_test.go b/rackspace/db/v1/backups/requests_test.go
deleted file mode 100644
index d706733..0000000
--- a/rackspace/db/v1/backups/requests_test.go
+++ /dev/null
@@ -1,131 +0,0 @@
-package backups
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/openstack/db/v1/datastores"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-var (
-	backupID = "{backupID}"
-	_rootURL = "/backups"
-	resURL   = _rootURL + "/" + backupID
-)
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _rootURL, "POST", createReq, createResp, 202)
-
-	opts := CreateOpts{
-		Name:        "snapshot",
-		Description: "My Backup",
-		InstanceID:  "d4603f69-ec7e-4e9b-803f-600b9205576f",
-	}
-
-	instance, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Backup{
-		Created:     timeVal,
-		Description: "My Backup",
-		ID:          "61f12fef-edb1-4561-8122-e7c00ef26a82",
-		InstanceID:  "d4603f69-ec7e-4e9b-803f-600b9205576f",
-		LocationRef: "",
-		Name:        "snapshot",
-		ParentID:    "",
-		Size:        100,
-		Status:      "NEW",
-		Updated:     timeVal,
-		Datastore: datastores.DatastorePartial{
-			Version:   "5.1",
-			Type:      "MySQL",
-			VersionID: "20000000-0000-0000-0000-000000000002",
-		},
-	}
-
-	th.AssertDeepEquals(t, expected, instance)
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _rootURL, "GET", "", listResp, 200)
-
-	pages := 0
-
-	err := List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-		actual, err := ExtractBackups(page)
-		th.AssertNoErr(t, err)
-
-		expected := []Backup{
-			Backup{
-				Created:     timeVal,
-				Description: "Backup from Restored Instance",
-				ID:          "87972694-4be2-40f5-83f8-501656e0032a",
-				InstanceID:  "29af2cd9-0674-48ab-b87a-b160f00208e6",
-				LocationRef: "http://localhost/path/to/backup",
-				Name:        "restored_backup",
-				ParentID:    "",
-				Size:        0.141026,
-				Status:      "COMPLETED",
-				Updated:     timeVal,
-				Datastore: datastores.DatastorePartial{
-					Version:   "5.1",
-					Type:      "MySQL",
-					VersionID: "20000000-0000-0000-0000-000000000002",
-				},
-			},
-		}
-
-		th.AssertDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "GET", "", getResp, 200)
-
-	instance, err := Get(fake.ServiceClient(), backupID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Backup{
-		Created:     timeVal,
-		Description: "My Backup",
-		ID:          "61f12fef-edb1-4561-8122-e7c00ef26a82",
-		InstanceID:  "d4603f69-ec7e-4e9b-803f-600b9205576f",
-		LocationRef: "",
-		Name:        "snapshot",
-		ParentID:    "",
-		Size:        100,
-		Status:      "NEW",
-		Updated:     timeVal,
-		Datastore: datastores.DatastorePartial{
-			Version:   "5.1",
-			Type:      "MySQL",
-			VersionID: "20000000-0000-0000-0000-000000000002",
-		},
-	}
-
-	th.AssertDeepEquals(t, expected, instance)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "DELETE", "", "", 202)
-
-	err := Delete(fake.ServiceClient(), backupID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/db/v1/backups/results.go b/rackspace/db/v1/backups/results.go
deleted file mode 100644
index 04faf32..0000000
--- a/rackspace/db/v1/backups/results.go
+++ /dev/null
@@ -1,149 +0,0 @@
-package backups
-
-import (
-	"fmt"
-	"reflect"
-	"time"
-
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/db/v1/datastores"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Status represents the various states a Backup can be in.
-type Status string
-
-// Enum types for the status.
-const (
-	StatusNew          Status = "NEW"
-	StatusBuilding     Status = "BUILDING"
-	StatusCompleted    Status = "COMPLETED"
-	StatusFailed       Status = "FAILED"
-	StatusDeleteFailed Status = "DELETE_FAILED"
-)
-
-// Backup represents a Backup API resource.
-type Backup struct {
-	Description string
-	ID          string
-	InstanceID  string `json:"instance_id" mapstructure:"instance_id"`
-	LocationRef string
-	Name        string
-	ParentID    string `json:"parent_id" mapstructure:"parent_id"`
-	Size        float64
-	Status      Status
-	Created     time.Time `mapstructure:"-"`
-	Updated     time.Time `mapstructure:"-"`
-	Datastore   datastores.DatastorePartial
-}
-
-// CreateResult represents the result of a create operation.
-type CreateResult struct {
-	commonResult
-}
-
-// GetResult represents the result of a get operation.
-type GetResult struct {
-	commonResult
-}
-
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-type commonResult struct {
-	gophercloud.Result
-}
-
-// Extract will retrieve a Backup struct from an operation's result.
-func (r commonResult) Extract() (*Backup, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		Backup Backup `mapstructure:"backup"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-	val := r.Body.(map[string]interface{})["backup"].(map[string]interface{})
-
-	if t, ok := val["created"].(string); ok && t != "" {
-		creationTime, err := time.Parse(time.RFC3339, t)
-		if err != nil {
-			return &response.Backup, err
-		}
-		response.Backup.Created = creationTime
-	}
-
-	if t, ok := val["updated"].(string); ok && t != "" {
-		updatedTime, err := time.Parse(time.RFC3339, t)
-		if err != nil {
-			return &response.Backup, err
-		}
-		response.Backup.Updated = updatedTime
-	}
-
-	return &response.Backup, err
-}
-
-// BackupPage represents a page of backups.
-type BackupPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty checks whether an BackupPage struct is empty.
-func (r BackupPage) IsEmpty() (bool, error) {
-	is, err := ExtractBackups(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
-}
-
-// ExtractBackups will retrieve a slice of Backup structs from a paginated collection.
-func ExtractBackups(page pagination.Page) ([]Backup, error) {
-	casted := page.(BackupPage).Body
-
-	var resp struct {
-		Backups []Backup `mapstructure:"backups" json:"backups"`
-	}
-
-	if err := mapstructure.Decode(casted, &resp); err != nil {
-		return nil, err
-	}
-
-	var vals []interface{}
-	switch casted.(type) {
-	case map[string]interface{}:
-		vals = casted.(map[string]interface{})["backups"].([]interface{})
-	case map[string][]interface{}:
-		vals = casted.(map[string][]interface{})["backups"]
-	default:
-		return resp.Backups, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i, v := range vals {
-		val := v.(map[string]interface{})
-
-		if t, ok := val["created"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return resp.Backups, err
-			}
-			resp.Backups[i].Created = creationTime
-		}
-
-		if t, ok := val["updated"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return resp.Backups, err
-			}
-			resp.Backups[i].Updated = updatedTime
-		}
-	}
-
-	return resp.Backups, nil
-}
diff --git a/rackspace/db/v1/backups/urls.go b/rackspace/db/v1/backups/urls.go
deleted file mode 100644
index 553444e..0000000
--- a/rackspace/db/v1/backups/urls.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package backups
-
-import "github.com/rackspace/gophercloud"
-
-func baseURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL("backups")
-}
-
-func resourceURL(c *gophercloud.ServiceClient, backupID string) string {
-	return c.ServiceURL("backups", backupID)
-}
diff --git a/rackspace/db/v1/configurations/delegate.go b/rackspace/db/v1/configurations/delegate.go
deleted file mode 100644
index d8cb48a..0000000
--- a/rackspace/db/v1/configurations/delegate.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package configurations
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/configurations"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List will list all of the available configurations.
-func List(client *gophercloud.ServiceClient) pagination.Pager {
-	return os.List(client)
-}
-
-// Create will create a new configuration group.
-func Create(client *gophercloud.ServiceClient, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(client, opts)
-}
-
-// Get will retrieve the details for a specified configuration group.
-func Get(client *gophercloud.ServiceClient, configID string) os.GetResult {
-	return os.Get(client, configID)
-}
-
-// Update will modify an existing configuration group by performing a merge
-// between new and existing values. If the key already exists, the new value
-// will overwrite. All other keys will remain unaffected.
-func Update(client *gophercloud.ServiceClient, configID string, opts os.UpdateOptsBuilder) os.UpdateResult {
-	return os.Update(client, configID, opts)
-}
-
-// Replace will modify an existing configuration group by overwriting the
-// entire parameter group with the new values provided. Any existing keys not
-// included in UpdateOptsBuilder will be deleted.
-func Replace(client *gophercloud.ServiceClient, configID string, opts os.UpdateOptsBuilder) os.ReplaceResult {
-	return os.Replace(client, configID, opts)
-}
-
-// Delete will permanently delete a configuration group. Please note that
-// config groups cannot be deleted whilst still attached to running instances -
-// you must detach and then delete them.
-func Delete(client *gophercloud.ServiceClient, configID string) os.DeleteResult {
-	return os.Delete(client, configID)
-}
-
-// ListInstances will list all the instances associated with a particular
-// configuration group.
-func ListInstances(client *gophercloud.ServiceClient, configID string) pagination.Pager {
-	return os.ListInstances(client, configID)
-}
-
-// ListDatastoreParams will list all the available and supported parameters
-// that can be used for a particular datastore ID and a particular version.
-// For example, if you are wondering how you can configure a MySQL 5.6 instance,
-// you can use this operation (you will need to retrieve the MySQL datastore ID
-// by using the datastores API).
-func ListDatastoreParams(client *gophercloud.ServiceClient, datastoreID, versionID string) pagination.Pager {
-	return os.ListDatastoreParams(client, datastoreID, versionID)
-}
-
-// GetDatastoreParam will retrieve information about a specific configuration
-// parameter. For example, you can use this operation to understand more about
-// "innodb_file_per_table" configuration param for MySQL datastores. You will
-// need the param's ID first, which can be attained by using the ListDatastoreParams
-// operation.
-func GetDatastoreParam(client *gophercloud.ServiceClient, datastoreID, versionID, paramID string) os.ParamResult {
-	return os.GetDatastoreParam(client, datastoreID, versionID, paramID)
-}
-
-// ListGlobalParams is similar to ListDatastoreParams but does not require a
-// DatastoreID.
-func ListGlobalParams(client *gophercloud.ServiceClient, versionID string) pagination.Pager {
-	return os.ListGlobalParams(client, versionID)
-}
-
-// GetGlobalParam is similar to GetDatastoreParam but does not require a
-// DatastoreID.
-func GetGlobalParam(client *gophercloud.ServiceClient, versionID, paramID string) os.ParamResult {
-	return os.GetGlobalParam(client, versionID, paramID)
-}
diff --git a/rackspace/db/v1/configurations/delegate_test.go b/rackspace/db/v1/configurations/delegate_test.go
deleted file mode 100644
index 580f02a..0000000
--- a/rackspace/db/v1/configurations/delegate_test.go
+++ /dev/null
@@ -1,237 +0,0 @@
-package configurations
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/db/v1/configurations"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/db/v1/instances"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-var (
-	configID = "{configID}"
-	_baseURL = "/configurations"
-	resURL   = _baseURL + "/" + configID
-
-	dsID               = "{datastoreID}"
-	versionID          = "{versionID}"
-	paramID            = "{paramID}"
-	dsParamListURL     = "/datastores/" + dsID + "/versions/" + versionID + "/parameters"
-	dsParamGetURL      = "/datastores/" + dsID + "/versions/" + versionID + "/parameters/" + paramID
-	globalParamListURL = "/datastores/versions/" + versionID + "/parameters"
-	globalParamGetURL  = "/datastores/versions/" + versionID + "/parameters/" + paramID
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _baseURL, "GET", "", listConfigsJSON, 200)
-
-	count := 0
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractConfigs(page)
-		th.AssertNoErr(t, err)
-
-		expected := []os.Config{exampleConfig}
-		th.AssertDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertEquals(t, 1, count)
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "GET", "", getConfigJSON, 200)
-
-	config, err := Get(fake.ServiceClient(), configID).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, &exampleConfig, config)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _baseURL, "POST", createReq, createConfigJSON, 200)
-
-	opts := os.CreateOpts{
-		Datastore: &os.DatastoreOpts{
-			Type:    "a00000a0-00a0-0a00-00a0-000a000000aa",
-			Version: "b00000b0-00b0-0b00-00b0-000b000000bb",
-		},
-		Description: "example description",
-		Name:        "example-configuration-name",
-		Values: map[string]interface{}{
-			"collation_server": "latin1_swedish_ci",
-			"connect_timeout":  120,
-		},
-	}
-
-	config, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, &exampleConfigWithValues, config)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "PATCH", updateReq, "", 200)
-
-	opts := os.UpdateOpts{
-		Values: map[string]interface{}{
-			"connect_timeout": 300,
-		},
-	}
-
-	err := Update(fake.ServiceClient(), configID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestReplace(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "PUT", updateReq, "", 202)
-
-	opts := os.UpdateOpts{
-		Values: map[string]interface{}{
-			"connect_timeout": 300,
-		},
-	}
-
-	err := Replace(fake.ServiceClient(), configID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "DELETE", "", "", 202)
-
-	err := Delete(fake.ServiceClient(), configID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestListInstances(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL+"/instances", "GET", "", listInstancesJSON, 200)
-
-	expectedInstance := instances.Instance{
-		ID:   "d4603f69-ec7e-4e9b-803f-600b9205576f",
-		Name: "json_rack_instance",
-	}
-
-	pages := 0
-	err := ListInstances(fake.ServiceClient(), configID).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := instances.ExtractInstances(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.AssertDeepEquals(t, actual, []instances.Instance{expectedInstance})
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestListDSParams(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, dsParamListURL, "GET", "", listParamsJSON, 200)
-
-	pages := 0
-	err := ListDatastoreParams(fake.ServiceClient(), dsID, versionID).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := os.ExtractParams(page)
-		if err != nil {
-			return false, err
-		}
-
-		expected := []os.Param{
-			os.Param{Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer"},
-			os.Param{Max: 4294967296, Min: 0, Name: "key_buffer_size", RestartRequired: false, Type: "integer"},
-			os.Param{Max: 65535, Min: 2, Name: "connect_timeout", RestartRequired: false, Type: "integer"},
-			os.Param{Max: 4294967296, Min: 0, Name: "join_buffer_size", RestartRequired: false, Type: "integer"},
-		}
-
-		th.AssertDeepEquals(t, actual, expected)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGetDSParam(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, dsParamGetURL, "GET", "", getParamJSON, 200)
-
-	param, err := GetDatastoreParam(fake.ServiceClient(), dsID, versionID, paramID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &os.Param{
-		Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer",
-	}
-
-	th.AssertDeepEquals(t, expected, param)
-}
-
-func TestListGlobalParams(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, globalParamListURL, "GET", "", listParamsJSON, 200)
-
-	pages := 0
-	err := ListGlobalParams(fake.ServiceClient(), versionID).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := os.ExtractParams(page)
-		if err != nil {
-			return false, err
-		}
-
-		expected := []os.Param{
-			os.Param{Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer"},
-			os.Param{Max: 4294967296, Min: 0, Name: "key_buffer_size", RestartRequired: false, Type: "integer"},
-			os.Param{Max: 65535, Min: 2, Name: "connect_timeout", RestartRequired: false, Type: "integer"},
-			os.Param{Max: 4294967296, Min: 0, Name: "join_buffer_size", RestartRequired: false, Type: "integer"},
-		}
-
-		th.AssertDeepEquals(t, actual, expected)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGetGlobalParam(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, globalParamGetURL, "GET", "", getParamJSON, 200)
-
-	param, err := GetGlobalParam(fake.ServiceClient(), versionID, paramID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &os.Param{
-		Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer",
-	}
-
-	th.AssertDeepEquals(t, expected, param)
-}
diff --git a/rackspace/db/v1/configurations/doc.go b/rackspace/db/v1/configurations/doc.go
deleted file mode 100644
index 48c51d6..0000000
--- a/rackspace/db/v1/configurations/doc.go
+++ /dev/null
@@ -1 +0,0 @@
-package configurations
diff --git a/rackspace/db/v1/configurations/fixtures.go b/rackspace/db/v1/configurations/fixtures.go
deleted file mode 100644
index 01a9325..0000000
--- a/rackspace/db/v1/configurations/fixtures.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// +build fixtures
-
-package configurations
-
-import (
-	"fmt"
-	"time"
-
-	os "github.com/rackspace/gophercloud/openstack/db/v1/configurations"
-)
-
-var (
-	timestamp  = "2015-11-12T14:22:42Z"
-	timeVal, _ = time.Parse(time.RFC3339, timestamp)
-)
-
-var singleConfigJSON = `
-{
-  "created": "` + timestamp + `",
-  "datastore_name": "mysql",
-  "datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb",
-  "datastore_version_name": "5.6",
-  "description": "example_description",
-  "id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
-  "name": "example-configuration-name",
-  "updated": "` + timestamp + `"
-}
-`
-
-var singleConfigWithValuesJSON = `
-{
-  "created": "` + timestamp + `",
-  "datastore_name": "mysql",
-  "datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb",
-  "datastore_version_name": "5.6",
-  "description": "example description",
-  "id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
-  "instance_count": 0,
-  "name": "example-configuration-name",
-  "updated": "` + timestamp + `",
-  "values": {
-    "collation_server": "latin1_swedish_ci",
-    "connect_timeout": 120
-  }
-}
-`
-
-var (
-	listConfigsJSON  = fmt.Sprintf(`{"configurations": [%s]}`, singleConfigJSON)
-	getConfigJSON    = fmt.Sprintf(`{"configuration": %s}`, singleConfigJSON)
-	createConfigJSON = fmt.Sprintf(`{"configuration": %s}`, singleConfigWithValuesJSON)
-)
-
-var createReq = `
-{
-  "configuration": {
-    "datastore": {
-      "type": "a00000a0-00a0-0a00-00a0-000a000000aa",
-      "version": "b00000b0-00b0-0b00-00b0-000b000000bb"
-    },
-    "description": "example description",
-    "name": "example-configuration-name",
-    "values": {
-      "collation_server": "latin1_swedish_ci",
-      "connect_timeout": 120
-    }
-  }
-}
-`
-
-var updateReq = `
-{
-  "configuration": {
-    "values": {
-      "connect_timeout": 300
-    }
-  }
-}
-`
-
-var listInstancesJSON = `
-{
-  "instances": [
-    {
-      "id": "d4603f69-ec7e-4e9b-803f-600b9205576f",
-      "name": "json_rack_instance"
-    }
-  ]
-}
-`
-
-var listParamsJSON = `
-{
-  "configuration-parameters": [
-    {
-      "max": 1,
-      "min": 0,
-      "name": "innodb_file_per_table",
-      "restart_required": true,
-      "type": "integer"
-    },
-    {
-      "max": 4294967296,
-      "min": 0,
-      "name": "key_buffer_size",
-      "restart_required": false,
-      "type": "integer"
-    },
-    {
-      "max": 65535,
-      "min": 2,
-      "name": "connect_timeout",
-      "restart_required": false,
-      "type": "integer"
-    },
-    {
-      "max": 4294967296,
-      "min": 0,
-      "name": "join_buffer_size",
-      "restart_required": false,
-      "type": "integer"
-    }
-  ]
-}
-`
-
-var getParamJSON = `
-{
-  "max": 1,
-  "min": 0,
-  "name": "innodb_file_per_table",
-  "restart_required": true,
-  "type": "integer"
-}
-`
-
-var exampleConfig = os.Config{
-	Created:              timeVal,
-	DatastoreName:        "mysql",
-	DatastoreVersionID:   "b00000b0-00b0-0b00-00b0-000b000000bb",
-	DatastoreVersionName: "5.6",
-	Description:          "example_description",
-	ID:                   "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
-	Name:                 "example-configuration-name",
-	Updated:              timeVal,
-}
-
-var exampleConfigWithValues = os.Config{
-	Created:              timeVal,
-	DatastoreName:        "mysql",
-	DatastoreVersionID:   "b00000b0-00b0-0b00-00b0-000b000000bb",
-	DatastoreVersionName: "5.6",
-	Description:          "example description",
-	ID:                   "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
-	Name:                 "example-configuration-name",
-	Updated:              timeVal,
-	Values: map[string]interface{}{
-		"collation_server": "latin1_swedish_ci",
-		"connect_timeout":  120,
-	},
-}
diff --git a/rackspace/db/v1/databases/delegate.go b/rackspace/db/v1/databases/delegate.go
deleted file mode 100644
index 56552d1..0000000
--- a/rackspace/db/v1/databases/delegate.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package databases
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-func Create(client *gophercloud.ServiceClient, instanceID string, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(client, instanceID, opts)
-}
-
-func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager {
-	return os.List(client, instanceID)
-}
-
-func Delete(client *gophercloud.ServiceClient, instanceID, dbName string) os.DeleteResult {
-	return os.Delete(client, instanceID, dbName)
-}
diff --git a/rackspace/db/v1/databases/delegate_test.go b/rackspace/db/v1/databases/delegate_test.go
deleted file mode 100644
index b9e50a5..0000000
--- a/rackspace/db/v1/databases/delegate_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package databases
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-var (
-	instanceID = "{instanceID}"
-	rootURL    = "/instances"
-	resURL     = rootURL + "/" + instanceID
-	uRootURL   = resURL + "/root"
-	aURL       = resURL + "/action"
-)
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleCreate(t)
-
-	opts := os.BatchCreateOpts{
-		os.CreateOpts{Name: "testingdb", CharSet: "utf8", Collate: "utf8_general_ci"},
-		os.CreateOpts{Name: "sampledb"},
-	}
-
-	res := Create(fake.ServiceClient(), instanceID, opts)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleList(t)
-
-	expectedDBs := []os.Database{
-		os.Database{Name: "anotherexampledb"},
-		os.Database{Name: "exampledb"},
-		os.Database{Name: "nextround"},
-		os.Database{Name: "sampledb"},
-		os.Database{Name: "testingdb"},
-	}
-
-	pages := 0
-	err := List(fake.ServiceClient(), instanceID).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := os.ExtractDBs(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, expectedDBs, actual)
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleDelete(t)
-
-	err := os.Delete(fake.ServiceClient(), instanceID, "{dbName}").ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/db/v1/databases/doc.go b/rackspace/db/v1/databases/doc.go
deleted file mode 100644
index 1a178b6..0000000
--- a/rackspace/db/v1/databases/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package databases provides information and interaction with the database API
-// resource in the Rackspace Database service.
-package databases
diff --git a/rackspace/db/v1/databases/urls.go b/rackspace/db/v1/databases/urls.go
deleted file mode 100644
index 18cbec7..0000000
--- a/rackspace/db/v1/databases/urls.go
+++ /dev/null
@@ -1 +0,0 @@
-package databases
diff --git a/rackspace/db/v1/datastores/delegate.go b/rackspace/db/v1/datastores/delegate.go
deleted file mode 100644
index 573496d..0000000
--- a/rackspace/db/v1/datastores/delegate.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package datastores
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/datastores"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List will list all available flavors.
-func List(client *gophercloud.ServiceClient) pagination.Pager {
-	return os.List(client)
-}
-
-// Get retrieves the details for a particular flavor.
-func Get(client *gophercloud.ServiceClient, flavorID string) os.GetResult {
-	return os.Get(client, flavorID)
-}
-
-// ListVersions will list all of the available versions for a specified
-// datastore type.
-func ListVersions(client *gophercloud.ServiceClient, datastoreID string) pagination.Pager {
-	return os.ListVersions(client, datastoreID)
-}
-
-// GetVersion will retrieve the details of a specified datastore version.
-func GetVersion(client *gophercloud.ServiceClient, datastoreID, versionID string) os.GetVersionResult {
-	return os.GetVersion(client, datastoreID, versionID)
-}
diff --git a/rackspace/db/v1/datastores/delegate_test.go b/rackspace/db/v1/datastores/delegate_test.go
deleted file mode 100644
index 71111b9..0000000
--- a/rackspace/db/v1/datastores/delegate_test.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package datastores
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/db/v1/datastores"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, "/datastores", "GET", "", os.ListDSResp, 200)
-
-	pages := 0
-
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := os.ExtractDatastores(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, []os.Datastore{os.ExampleDatastore}, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, "/datastores/{dsID}", "GET", "", os.GetDSResp, 200)
-
-	ds, err := Get(fake.ServiceClient(), "{dsID}").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, &os.ExampleDatastore, ds)
-}
-
-func TestListVersions(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, "/datastores/{dsID}/versions", "GET", "", os.ListVersionsResp, 200)
-
-	pages := 0
-
-	err := ListVersions(fake.ServiceClient(), "{dsID}").EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := os.ExtractVersions(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, os.ExampleVersions, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGetVersion(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, "/datastores/{dsID}/versions/{versionID}", "GET", "", os.GetVersionResp, 200)
-
-	ds, err := GetVersion(fake.ServiceClient(), "{dsID}", "{versionID}").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, &os.ExampleVersion1, ds)
-}
diff --git a/rackspace/db/v1/datastores/doc.go b/rackspace/db/v1/datastores/doc.go
deleted file mode 100644
index f36997a..0000000
--- a/rackspace/db/v1/datastores/doc.go
+++ /dev/null
@@ -1 +0,0 @@
-package datastores
diff --git a/rackspace/db/v1/flavors/delegate.go b/rackspace/db/v1/flavors/delegate.go
deleted file mode 100644
index 689b81e..0000000
--- a/rackspace/db/v1/flavors/delegate.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package flavors
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/flavors"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List will list all available flavors.
-func List(client *gophercloud.ServiceClient) pagination.Pager {
-	return os.List(client)
-}
-
-// Get retrieves the details for a particular flavor.
-func Get(client *gophercloud.ServiceClient, flavorID string) os.GetResult {
-	return os.Get(client, flavorID)
-}
diff --git a/rackspace/db/v1/flavors/delegate_test.go b/rackspace/db/v1/flavors/delegate_test.go
deleted file mode 100644
index f5f6442..0000000
--- a/rackspace/db/v1/flavors/delegate_test.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package flavors
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/flavors"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListFlavors(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleList(t)
-
-	pages := 0
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := os.ExtractFlavors(page)
-		if err != nil {
-			return false, err
-		}
-
-		expected := []os.Flavor{
-			os.Flavor{
-				ID:   "1",
-				Name: "m1.tiny",
-				RAM:  512,
-				Links: []gophercloud.Link{
-					gophercloud.Link{Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"},
-					gophercloud.Link{Href: "https://openstack.example.com/flavors/1", Rel: "bookmark"},
-				},
-			},
-			os.Flavor{
-				ID:   "2",
-				Name: "m1.small",
-				RAM:  1024,
-				Links: []gophercloud.Link{
-					gophercloud.Link{Href: "https://openstack.example.com/v1.0/1234/flavors/2", Rel: "self"},
-					gophercloud.Link{Href: "https://openstack.example.com/flavors/2", Rel: "bookmark"},
-				},
-			},
-			os.Flavor{
-				ID:   "3",
-				Name: "m1.medium",
-				RAM:  2048,
-				Links: []gophercloud.Link{
-					gophercloud.Link{Href: "https://openstack.example.com/v1.0/1234/flavors/3", Rel: "self"},
-					gophercloud.Link{Href: "https://openstack.example.com/flavors/3", Rel: "bookmark"},
-				},
-			},
-			os.Flavor{
-				ID:   "4",
-				Name: "m1.large",
-				RAM:  4096,
-				Links: []gophercloud.Link{
-					gophercloud.Link{Href: "https://openstack.example.com/v1.0/1234/flavors/4", Rel: "self"},
-					gophercloud.Link{Href: "https://openstack.example.com/flavors/4", Rel: "bookmark"},
-				},
-			},
-		}
-
-		th.AssertDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	if pages != 1 {
-		t.Errorf("Expected one page, got %d", pages)
-	}
-}
-
-func TestGetFlavor(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGet(t)
-
-	actual, err := Get(fake.ServiceClient(), "{flavorID}").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &os.Flavor{
-		ID:   "1",
-		Name: "m1.tiny",
-		RAM:  512,
-		Links: []gophercloud.Link{
-			gophercloud.Link{Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"},
-		},
-	}
-
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/rackspace/db/v1/flavors/doc.go b/rackspace/db/v1/flavors/doc.go
deleted file mode 100644
index 922a4e6..0000000
--- a/rackspace/db/v1/flavors/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package flavors provides information and interaction with the flavor API
-// resource in the Rackspace Database service.
-package flavors
diff --git a/rackspace/db/v1/instances/delegate.go b/rackspace/db/v1/instances/delegate.go
deleted file mode 100644
index f2656fe..0000000
--- a/rackspace/db/v1/instances/delegate.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package instances
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/instances"
-)
-
-// Get retrieves the status and information for a specified database instance.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	return GetResult{os.Get(client, id)}
-}
-
-// Delete permanently destroys the database instance.
-func Delete(client *gophercloud.ServiceClient, id string) os.DeleteResult {
-	return os.Delete(client, id)
-}
-
-// EnableRootUser enables the login from any host for the root user and
-// provides the user with a generated root password.
-func EnableRootUser(client *gophercloud.ServiceClient, id string) os.UserRootResult {
-	return os.EnableRootUser(client, id)
-}
-
-// IsRootEnabled checks an instance to see if root access is enabled. It returns
-// True if root user is enabled for the specified database instance or False
-// otherwise.
-func IsRootEnabled(client *gophercloud.ServiceClient, id string) (bool, error) {
-	return os.IsRootEnabled(client, id)
-}
-
-// Restart will restart only the MySQL Instance. Restarting MySQL will
-// erase any dynamic configuration settings that you have made within MySQL.
-// The MySQL service will be unavailable until the instance restarts.
-func Restart(client *gophercloud.ServiceClient, id string) os.ActionResult {
-	return os.Restart(client, id)
-}
-
-// Resize changes the memory size of the instance, assuming a valid
-// flavorRef is provided. It will also restart the MySQL service.
-func Resize(client *gophercloud.ServiceClient, id, flavorRef string) os.ActionResult {
-	return os.Resize(client, id, flavorRef)
-}
-
-// ResizeVolume will resize the attached volume for an instance. It supports
-// only increasing the volume size and does not support decreasing the size.
-// The volume size is in gigabytes (GB) and must be an integer.
-func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) os.ActionResult {
-	return os.ResizeVolume(client, id, size)
-}
diff --git a/rackspace/db/v1/instances/delegate_test.go b/rackspace/db/v1/instances/delegate_test.go
deleted file mode 100644
index 716e0a4..0000000
--- a/rackspace/db/v1/instances/delegate_test.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package instances
-
-import (
-	"testing"
-
-	osDBs "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/instances"
-	osUsers "github.com/rackspace/gophercloud/openstack/db/v1/users"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-var (
-	_rootURL = "/instances"
-	resURL   = "/instances/" + instanceID
-)
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _rootURL, "POST", createReq, createResp, 200)
-
-	opts := CreateOpts{
-		Name:      "json_rack_instance",
-		FlavorRef: "1",
-		Databases: osDBs.BatchCreateOpts{
-			osDBs.CreateOpts{CharSet: "utf8", Collate: "utf8_general_ci", Name: "sampledb"},
-			osDBs.CreateOpts{Name: "nextround"},
-		},
-		Users: osUsers.BatchCreateOpts{
-			osUsers.CreateOpts{
-				Name:     "demouser",
-				Password: "demopassword",
-				Databases: osDBs.BatchCreateOpts{
-					osDBs.CreateOpts{Name: "sampledb"},
-				},
-			},
-		},
-		Size:         2,
-		RestorePoint: "1234567890",
-	}
-
-	instance, err := Create(fake.ServiceClient(), opts).Extract()
-
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expectedInstance, instance)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "GET", "", getResp, 200)
-
-	instance, err := Get(fake.ServiceClient(), instanceID).Extract()
-
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expectedInstance, instance)
-}
-
-func TestDeleteInstance(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleDelete(t)
-
-	res := Delete(fake.ServiceClient(), instanceID)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestEnableRootUser(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleEnableRoot(t)
-
-	expected := &osUsers.User{Name: "root", Password: "secretsecret"}
-
-	user, err := EnableRootUser(fake.ServiceClient(), instanceID).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, user)
-}
-
-func TestRestart(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleRestart(t)
-
-	res := Restart(fake.ServiceClient(), instanceID)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestResize(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleResize(t)
-
-	res := Resize(fake.ServiceClient(), instanceID, "2")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestResizeVolume(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleResizeVol(t)
-
-	res := ResizeVolume(fake.ServiceClient(), instanceID, 4)
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/rackspace/db/v1/instances/doc.go b/rackspace/db/v1/instances/doc.go
deleted file mode 100644
index 0c8ad63..0000000
--- a/rackspace/db/v1/instances/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package instances provides information and interaction with the instance API
-// resource in the Rackspace Database service.
-package instances
diff --git a/rackspace/db/v1/instances/fixtures.go b/rackspace/db/v1/instances/fixtures.go
deleted file mode 100644
index 92d7bed..0000000
--- a/rackspace/db/v1/instances/fixtures.go
+++ /dev/null
@@ -1,342 +0,0 @@
-// +build fixtures
-
-package instances
-
-import (
-	"fmt"
-	"time"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/db/v1/datastores"
-	"github.com/rackspace/gophercloud/openstack/db/v1/flavors"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/instances"
-)
-
-var (
-	timestamp  = "2015-11-12T14:22:42Z"
-	timeVal, _ = time.Parse(time.RFC3339, timestamp)
-)
-
-var instance = `
-{
-  "created": "` + timestamp + `",
-  "datastore": {
-    "type": "mysql",
-    "version": "5.6"
-  },
-  "flavor": {
-    "id": "1",
-    "links": [
-      {
-        "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1",
-        "rel": "self"
-      },
-      {
-        "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1",
-        "rel": "bookmark"
-      }
-    ]
-  },
-  "links": [
-    {
-      "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1",
-      "rel": "self"
-    }
-  ],
-  "hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com",
-  "id": "{instanceID}",
-  "name": "json_rack_instance",
-  "status": "BUILD",
-  "updated": "` + timestamp + `",
-  "volume": {
-    "size": 2
-  }
-}
-`
-
-var createReq = `
-{
-  "instance": {
-    "databases": [
-      {
-        "character_set": "utf8",
-        "collate": "utf8_general_ci",
-        "name": "sampledb"
-      },
-      {
-        "name": "nextround"
-      }
-    ],
-    "flavorRef": "1",
-    "name": "json_rack_instance",
-    "users": [
-      {
-        "databases": [
-          {
-            "name": "sampledb"
-          }
-        ],
-        "name": "demouser",
-        "password": "demopassword"
-      }
-    ],
-    "volume": {
-      "size": 2
-    },
-    "restorePoint": {
-      "backupRef": "1234567890"
-    }
-  }
-}
-`
-
-var createReplicaReq = `
-{
-  "instance": {
-    "volume": {
-      "size": 1
-    },
-    "flavorRef": "9",
-    "name": "t2s1_ALT_GUEST",
-    "replica_of": "6bdca2fc-418e-40bd-a595-62abda61862d"
-  }
-}
-`
-
-var createReplicaResp = `
-{
-  "instance": {
-    "status": "BUILD",
-    "updated": "` + timestamp + `",
-    "name": "t2s1_ALT_GUEST",
-    "links": [
-      {
-        "href": "https://ord.databases.api.rackspacecloud.com/v1.0/5919009/instances/8367c312-7c40-4a66-aab1-5767478914fc",
-        "rel": "self"
-      },
-      {
-        "href": "https://ord.databases.api.rackspacecloud.com/instances/8367c312-7c40-4a66-aab1-5767478914fc",
-        "rel": "bookmark"
-      }
-    ],
-    "created": "` + timestamp + `",
-    "id": "8367c312-7c40-4a66-aab1-5767478914fc",
-    "volume": {
-      "size": 1
-    },
-    "flavor": {
-      "id": "9"
-    },
-    "datastore": {
-      "version": "5.6",
-      "type": "mysql"
-    },
-    "replica_of": {
-      "id": "6bdca2fc-418e-40bd-a595-62abda61862d"
-    }
-  }
-}
-`
-
-var listReplicasResp = `
-{
-  "instances": [
-    {
-      "status": "ACTIVE",
-      "name": "t1s1_ALT_GUEST",
-      "links": [
-        {
-          "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/3c691f06-bf9a-4618-b7ec-2817ce0cf254",
-          "rel": "self"
-        },
-        {
-          "href": "https://ord.databases.api.rackspacecloud.com/instances/3c691f06-bf9a-4618-b7ec-2817ce0cf254",
-          "rel": "bookmark"
-        }
-      ],
-      "ip": [
-        "10.0.0.3"
-      ],
-      "id": "3c691f06-bf9a-4618-b7ec-2817ce0cf254",
-      "volume": {
-        "size": 1
-      },
-      "flavor": {
-        "id": "9"
-      },
-      "datastore": {
-        "version": "5.6",
-        "type": "mysql"
-      },
-      "replica_of": {
-        "id": "8b499b45-52d6-402d-b398-f9d8f279c69a"
-      }
-    }
-  ]
-}
-`
-
-var getReplicaResp = `
-{
-  "instance": {
-    "status": "ACTIVE",
-    "updated": "` + timestamp + `",
-    "name": "t1_ALT_GUEST",
-    "created": "` + timestamp + `",
-    "ip": [
-      "10.0.0.2"
-    ],
-    "replicas": [
-      {
-        "id": "3c691f06-bf9a-4618-b7ec-2817ce0cf254"
-      }
-    ],
-    "id": "8b499b45-52d6-402d-b398-f9d8f279c69a",
-    "volume": {
-      "used": 0.54,
-      "size": 1
-    },
-    "flavor": {
-      "id": "9"
-    },
-    "datastore": {
-      "version": "5.6",
-      "type": "mysql"
-    }
-  }
-}
-`
-
-var detachReq = `
-{
-  "instance": {
-    "replica_of": "",
-    "slave_of": ""
-  }
-}
-`
-
-var getConfigResp = `
-{
-  "instance": {
-    "configuration": {
-      "basedir": "/usr",
-      "connect_timeout": "15",
-      "datadir": "/var/lib/mysql",
-      "default_storage_engine": "innodb",
-      "innodb_buffer_pool_instances": "1",
-      "innodb_buffer_pool_size": "175M",
-      "innodb_checksum_algorithm": "crc32",
-      "innodb_data_file_path": "ibdata1:10M:autoextend",
-      "innodb_file_per_table": "1",
-      "innodb_io_capacity": "200",
-      "innodb_log_file_size": "256M",
-      "innodb_log_files_in_group": "2",
-      "innodb_open_files": "8192",
-      "innodb_thread_concurrency": "0",
-      "join_buffer_size": "1M",
-      "key_buffer_size": "50M",
-      "local-infile": "0",
-      "log-error": "/var/log/mysql/mysqld.log",
-      "max_allowed_packet": "16M",
-      "max_connect_errors": "10000",
-      "max_connections": "40",
-      "max_heap_table_size": "16M",
-      "myisam-recover": "BACKUP",
-      "open_files_limit": "8192",
-      "performance_schema": "off",
-      "pid_file": "/var/run/mysqld/mysqld.pid",
-      "port": "3306",
-      "query_cache_limit": "1M",
-      "query_cache_size": "8M",
-      "query_cache_type": "1",
-      "read_buffer_size": "256K",
-      "read_rnd_buffer_size": "1M",
-      "server_id": "1",
-      "skip-external-locking": "1",
-      "skip_name_resolve": "1",
-      "sort_buffer_size": "256K",
-      "table_open_cache": "4096",
-      "thread_stack": "192K",
-      "tmp_table_size": "16M",
-      "tmpdir": "/var/tmp",
-      "user": "mysql",
-      "wait_timeout": "3600"
-    }
-  }
-}
-`
-
-var associateReq = `{"instance": {"configuration": "{configGroupID}"}}`
-
-var listBackupsResp = `
-{
-  "backups": [
-    {
-      "status": "COMPLETED",
-      "updated": "` + timestamp + `",
-      "description": "Backup from Restored Instance",
-      "datastore": {
-        "version": "5.1",
-        "type": "MySQL",
-        "version_id": "20000000-0000-0000-0000-000000000002"
-      },
-      "id": "87972694-4be2-40f5-83f8-501656e0032a",
-      "size": 0.141026,
-      "name": "restored_backup",
-      "created": "` + timestamp + `",
-      "instance_id": "29af2cd9-0674-48ab-b87a-b160f00208e6",
-      "parent_id": null,
-      "locationRef": "http://localhost/path/to/backup"
-    }
-  ]
-}
-`
-
-var (
-	createResp        = fmt.Sprintf(`{"instance":%s}`, instance)
-	getResp           = fmt.Sprintf(`{"instance":%s}`, instance)
-	associateResp     = fmt.Sprintf(`{"instance":%s}`, instance)
-	listInstancesResp = fmt.Sprintf(`{"instances":[%s]}`, instance)
-)
-
-var instanceID = "{instanceID}"
-
-var expectedInstance = &Instance{
-	Created:   timeVal,
-	Updated:   timeVal,
-	Datastore: datastores.DatastorePartial{Type: "mysql", Version: "5.6"},
-	Flavor: flavors.Flavor{
-		ID: "1",
-		Links: []gophercloud.Link{
-			gophercloud.Link{Href: "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", Rel: "self"},
-			gophercloud.Link{Href: "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", Rel: "bookmark"},
-		},
-	},
-	Hostname: "e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com",
-	ID:       instanceID,
-	Links: []gophercloud.Link{
-		gophercloud.Link{Href: "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", Rel: "self"},
-	},
-	Name:   "json_rack_instance",
-	Status: "BUILD",
-	Volume: os.Volume{Size: 2},
-}
-
-var expectedReplica = &Instance{
-	Status:  "BUILD",
-	Updated: timeVal,
-	Name:    "t2s1_ALT_GUEST",
-	Links: []gophercloud.Link{
-		gophercloud.Link{Rel: "self", Href: "https://ord.databases.api.rackspacecloud.com/v1.0/5919009/instances/8367c312-7c40-4a66-aab1-5767478914fc"},
-		gophercloud.Link{Rel: "bookmark", Href: "https://ord.databases.api.rackspacecloud.com/instances/8367c312-7c40-4a66-aab1-5767478914fc"},
-	},
-	Created:   timeVal,
-	ID:        "8367c312-7c40-4a66-aab1-5767478914fc",
-	Volume:    os.Volume{Size: 1},
-	Flavor:    flavors.Flavor{ID: "9"},
-	Datastore: datastores.DatastorePartial{Version: "5.6", Type: "mysql"},
-	ReplicaOf: &Instance{
-		ID: "6bdca2fc-418e-40bd-a595-62abda61862d",
-	},
-}
diff --git a/rackspace/db/v1/instances/requests.go b/rackspace/db/v1/instances/requests.go
deleted file mode 100644
index f4df692..0000000
--- a/rackspace/db/v1/instances/requests.go
+++ /dev/null
@@ -1,199 +0,0 @@
-package instances
-
-import (
-	"github.com/rackspace/gophercloud"
-	osDBs "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/instances"
-	osUsers "github.com/rackspace/gophercloud/openstack/db/v1/users"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/db/v1/backups"
-)
-
-// CreateOpts is the struct responsible for configuring a new database instance.
-type CreateOpts struct {
-	// Either the integer UUID (in string form) of the flavor, or its URI
-	// reference as specified in the response from the List() call. Required.
-	FlavorRef string
-
-	// Specifies the volume size in gigabytes (GB). The value must be between 1
-	// and 300. Required.
-	Size int
-
-	// Name of the instance to create. The length of the name is limited to
-	// 255 characters and any characters are permitted. Optional.
-	Name string
-
-	// A slice of database information options.
-	Databases osDBs.CreateOptsBuilder
-
-	// A slice of user information options.
-	Users osUsers.CreateOptsBuilder
-
-	// ID of the configuration group to associate with the instance. Optional.
-	ConfigID string
-
-	// Options to configure the type of datastore the instance will use. This is
-	// optional, and if excluded will default to MySQL.
-	Datastore *os.DatastoreOpts
-
-	// Specifies the backup ID from which to restore the database instance. There
-	// are some things to be aware of before using this field.  When you execute
-	// the Restore Backup operation, a new database instance is created to store
-	// the backup whose ID is specified by the restorePoint attribute. This will
-	// mean that:
-	// - All users, passwords and access that were on the instance at the time of
-	// the backup will be restored along with the databases.
-	// - You can create new users or databases if you want, but they cannot be
-	// the same as the ones from the instance that was backed up.
-	RestorePoint string
-
-	ReplicaOf string
-}
-
-func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) {
-	instance, err := os.CreateOpts{
-		FlavorRef: opts.FlavorRef,
-		Size:      opts.Size,
-		Name:      opts.Name,
-		Databases: opts.Databases,
-		Users:     opts.Users,
-	}.ToInstanceCreateMap()
-
-	if err != nil {
-		return nil, err
-	}
-
-	instance = instance["instance"].(map[string]interface{})
-
-	if opts.ConfigID != "" {
-		instance["configuration"] = opts.ConfigID
-	}
-
-	if opts.Datastore != nil {
-		ds, err := opts.Datastore.ToMap()
-		if err != nil {
-			return nil, err
-		}
-		instance["datastore"] = ds
-	}
-
-	if opts.RestorePoint != "" {
-		instance["restorePoint"] = map[string]string{"backupRef": opts.RestorePoint}
-	}
-
-	if opts.ReplicaOf != "" {
-		instance["replica_of"] = opts.ReplicaOf
-	}
-
-	return map[string]interface{}{"instance": instance}, nil
-}
-
-// Create asynchronously provisions a new database instance. It requires the
-// user to specify a flavor and a volume size. The API service then provisions
-// the instance with the requested flavor and sets up a volume of the specified
-// size, which is the storage for the database instance.
-//
-// Although this call only allows the creation of 1 instance per request, you
-// can create an instance with multiple databases and users. The default
-// binding for a MySQL instance is port 3306.
-func Create(client *gophercloud.ServiceClient, opts os.CreateOptsBuilder) CreateResult {
-	return CreateResult{os.Create(client, opts)}
-}
-
-// ListOpts specifies all of the query options to be used when returning a list
-// of database instances.
-type ListOpts struct {
-	// IncludeHA includes or excludes High Availability instances from the result set
-	IncludeHA bool `q:"include_ha"`
-
-	// IncludeReplicas includes or excludes Replica instances from the result set
-	IncludeReplicas bool `q:"include_replicas"`
-}
-
-// ToInstanceListQuery formats a ListOpts into a query string.
-func (opts ListOpts) ToInstanceListQuery() (string, error) {
-	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
-}
-
-// List retrieves the status and information for all database instances.
-func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
-	url := baseURL(client)
-
-	if opts != nil {
-		query, err := opts.ToInstanceListQuery()
-		if err != nil {
-			return pagination.Pager{Err: err}
-		}
-		url += query
-	}
-
-	createPageFn := func(r pagination.PageResult) pagination.Page {
-		return os.InstancePage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, url, createPageFn)
-}
-
-// GetDefaultConfig lists the default configuration settings from the template
-// that was applied to the specified instance. In a sense, this is the vanilla
-// configuration setting applied to an instance. Further configuration can be
-// applied by associating an instance with a configuration group.
-func GetDefaultConfig(client *gophercloud.ServiceClient, id string) ConfigResult {
-	var res ConfigResult
-
-	_, res.Err = client.Request("GET", configURL(client, id), gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
-		OkCodes:      []int{200},
-	})
-
-	return res
-}
-
-// AssociateWithConfigGroup associates a specified instance to a specified
-// configuration group. If any of the parameters within a configuration group
-// require a restart, then the instance will transition into a restart.
-func AssociateWithConfigGroup(client *gophercloud.ServiceClient, instanceID, configGroupID string) UpdateResult {
-	reqBody := map[string]string{
-		"configuration": configGroupID,
-	}
-
-	var res UpdateResult
-
-	_, res.Err = client.Request("PUT", resourceURL(client, instanceID), gophercloud.RequestOpts{
-		JSONBody: map[string]map[string]string{"instance": reqBody},
-		OkCodes:  []int{202},
-	})
-
-	return res
-}
-
-// DetachFromConfigGroup will detach an instance from all config groups.
-func DetachFromConfigGroup(client *gophercloud.ServiceClient, instanceID string) UpdateResult {
-	return AssociateWithConfigGroup(client, instanceID, "")
-}
-
-// ListBackups will list all the backups for a specified database instance.
-func ListBackups(client *gophercloud.ServiceClient, instanceID string) pagination.Pager {
-	pageFn := func(r pagination.PageResult) pagination.Page {
-		return backups.BackupPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, backupsURL(client, instanceID), pageFn)
-}
-
-// DetachReplica will detach a specified replica instance from its source
-// instance, effectively allowing it to operate independently. Detaching a
-// replica will restart the MySQL service on the instance.
-func DetachReplica(client *gophercloud.ServiceClient, replicaID string) DetachResult {
-	var res DetachResult
-
-	_, res.Err = client.Request("PATCH", resourceURL(client, replicaID), gophercloud.RequestOpts{
-		JSONBody: map[string]interface{}{"instance": map[string]string{"replica_of": "", "slave_of": ""}},
-		OkCodes:  []int{202},
-	})
-
-	return res
-}
diff --git a/rackspace/db/v1/instances/requests_test.go b/rackspace/db/v1/instances/requests_test.go
deleted file mode 100644
index 7fa4601..0000000
--- a/rackspace/db/v1/instances/requests_test.go
+++ /dev/null
@@ -1,246 +0,0 @@
-package instances
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/db/v1/datastores"
-	"github.com/rackspace/gophercloud/openstack/db/v1/flavors"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/instances"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/db/v1/backups"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-func TestInstanceList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	fixture.SetupHandler(t, "/instances", "GET", "", listInstancesResp, 200)
-
-	opts := &ListOpts{
-		IncludeHA:       false,
-		IncludeReplicas: false,
-	}
-
-	pages := 0
-	err := List(fake.ServiceClient(), opts).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractInstances(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, []Instance{*expectedInstance}, actual)
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGetConfig(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL+"/configuration", "GET", "", getConfigResp, 200)
-
-	config, err := GetDefaultConfig(fake.ServiceClient(), instanceID).Extract()
-
-	expected := map[string]string{
-		"basedir":                      "/usr",
-		"connect_timeout":              "15",
-		"datadir":                      "/var/lib/mysql",
-		"default_storage_engine":       "innodb",
-		"innodb_buffer_pool_instances": "1",
-		"innodb_buffer_pool_size":      "175M",
-		"innodb_checksum_algorithm":    "crc32",
-		"innodb_data_file_path":        "ibdata1:10M:autoextend",
-		"innodb_file_per_table":        "1",
-		"innodb_io_capacity":           "200",
-		"innodb_log_file_size":         "256M",
-		"innodb_log_files_in_group":    "2",
-		"innodb_open_files":            "8192",
-		"innodb_thread_concurrency":    "0",
-		"join_buffer_size":             "1M",
-		"key_buffer_size":              "50M",
-		"local-infile":                 "0",
-		"log-error":                    "/var/log/mysql/mysqld.log",
-		"max_allowed_packet":           "16M",
-		"max_connect_errors":           "10000",
-		"max_connections":              "40",
-		"max_heap_table_size":          "16M",
-		"myisam-recover":               "BACKUP",
-		"open_files_limit":             "8192",
-		"performance_schema":           "off",
-		"pid_file":                     "/var/run/mysqld/mysqld.pid",
-		"port":                         "3306",
-		"query_cache_limit":            "1M",
-		"query_cache_size":             "8M",
-		"query_cache_type":             "1",
-		"read_buffer_size":             "256K",
-		"read_rnd_buffer_size":         "1M",
-		"server_id":                    "1",
-		"skip-external-locking":        "1",
-		"skip_name_resolve":            "1",
-		"sort_buffer_size":             "256K",
-		"table_open_cache":             "4096",
-		"thread_stack":                 "192K",
-		"tmp_table_size":               "16M",
-		"tmpdir":                       "/var/tmp",
-		"user":                         "mysql",
-		"wait_timeout":                 "3600",
-	}
-
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, config)
-}
-
-func TestAssociateWithConfigGroup(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "PUT", associateReq, associateResp, 202)
-
-	res := AssociateWithConfigGroup(fake.ServiceClient(), instanceID, "{configGroupID}")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestListBackups(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL+"/backups", "GET", "", listBackupsResp, 200)
-
-	pages := 0
-
-	err := ListBackups(fake.ServiceClient(), instanceID).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-		actual, err := backups.ExtractBackups(page)
-		th.AssertNoErr(t, err)
-
-		expected := []backups.Backup{
-			backups.Backup{
-				Created:     timeVal,
-				Description: "Backup from Restored Instance",
-				ID:          "87972694-4be2-40f5-83f8-501656e0032a",
-				InstanceID:  "29af2cd9-0674-48ab-b87a-b160f00208e6",
-				LocationRef: "http://localhost/path/to/backup",
-				Name:        "restored_backup",
-				ParentID:    "",
-				Size:        0.141026,
-				Status:      "COMPLETED",
-				Updated:     timeVal,
-				Datastore:   datastores.DatastorePartial{Version: "5.1", Type: "MySQL", VersionID: "20000000-0000-0000-0000-000000000002"},
-			},
-		}
-
-		th.AssertDeepEquals(t, expected, actual)
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestCreateReplica(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _rootURL, "POST", createReplicaReq, createReplicaResp, 200)
-
-	opts := CreateOpts{
-		Name:      "t2s1_ALT_GUEST",
-		FlavorRef: "9",
-		Size:      1,
-		ReplicaOf: "6bdca2fc-418e-40bd-a595-62abda61862d",
-	}
-
-	replica, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expectedReplica, replica)
-}
-
-func TestListReplicas(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _rootURL, "GET", "", listReplicasResp, 200)
-
-	pages := 0
-	err := List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractInstances(page)
-		if err != nil {
-			return false, err
-		}
-
-		expected := []Instance{
-			Instance{
-				Status: "ACTIVE",
-				Name:   "t1s1_ALT_GUEST",
-				Links: []gophercloud.Link{
-					gophercloud.Link{Rel: "self", Href: "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/3c691f06-bf9a-4618-b7ec-2817ce0cf254"},
-					gophercloud.Link{Rel: "bookmark", Href: "https://ord.databases.api.rackspacecloud.com/instances/3c691f06-bf9a-4618-b7ec-2817ce0cf254"},
-				},
-				ID:        "3c691f06-bf9a-4618-b7ec-2817ce0cf254",
-				IP:        []string{"10.0.0.3"},
-				Volume:    os.Volume{Size: 1},
-				Flavor:    flavors.Flavor{ID: "9"},
-				Datastore: datastores.DatastorePartial{Version: "5.6", Type: "mysql"},
-				ReplicaOf: &Instance{
-					ID: "8b499b45-52d6-402d-b398-f9d8f279c69a",
-				},
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGetReplica(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "GET", "", getReplicaResp, 200)
-
-	replica, err := Get(fake.ServiceClient(), instanceID).Extract()
-	th.AssertNoErr(t, err)
-
-	expectedReplica := &Instance{
-		Status:  "ACTIVE",
-		Updated: timeVal,
-		Name:    "t1_ALT_GUEST",
-		Created: timeVal,
-		IP: []string{
-			"10.0.0.2",
-		},
-		Replicas: []Instance{
-			Instance{ID: "3c691f06-bf9a-4618-b7ec-2817ce0cf254"},
-		},
-		ID: "8b499b45-52d6-402d-b398-f9d8f279c69a",
-		Volume: os.Volume{
-			Used: 0.54,
-			Size: 1,
-		},
-		Flavor: flavors.Flavor{ID: "9"},
-		Datastore: datastores.DatastorePartial{
-			Version: "5.6",
-			Type:    "mysql",
-		},
-	}
-
-	th.AssertDeepEquals(t, replica, expectedReplica)
-}
-
-func TestDetachReplica(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, resURL, "PATCH", detachReq, "", 202)
-
-	err := DetachReplica(fake.ServiceClient(), instanceID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/db/v1/instances/results.go b/rackspace/db/v1/instances/results.go
deleted file mode 100644
index cdcc9c7..0000000
--- a/rackspace/db/v1/instances/results.go
+++ /dev/null
@@ -1,191 +0,0 @@
-package instances
-
-import (
-	"fmt"
-	"reflect"
-	"time"
-
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/db/v1/datastores"
-	"github.com/rackspace/gophercloud/openstack/db/v1/flavors"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/instances"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Instance represents a remote MySQL instance.
-type Instance struct {
-	// Indicates the datetime that the instance was created
-	Created time.Time `mapstructure:"-"`
-
-	// Indicates the most recent datetime that the instance was updated.
-	Updated time.Time `mapstructure:"-"`
-
-	// Indicates how the instance stores data.
-	Datastore datastores.DatastorePartial
-
-	// Indicates the hardware flavor the instance uses.
-	Flavor flavors.Flavor
-
-	// A DNS-resolvable hostname associated with the database instance (rather
-	// than an IPv4 address). Since the hostname always resolves to the correct
-	// IP address of the database instance, this relieves the user from the task
-	// of maintaining the mapping. Note that although the IP address may likely
-	// change on resizing, migrating, and so forth, the hostname always resolves
-	// to the correct database instance.
-	Hostname string
-
-	// Indicates the unique identifier for the instance resource.
-	ID string
-
-	// Exposes various links that reference the instance resource.
-	Links []gophercloud.Link
-
-	// The human-readable name of the instance.
-	Name string
-
-	// The build status of the instance.
-	Status string
-
-	// Information about the attached volume of the instance.
-	Volume os.Volume
-
-	// IP indicates the various IP addresses which allow access.
-	IP []string
-
-	// Indicates whether this instance is a replica of another source instance.
-	ReplicaOf *Instance `mapstructure:"replica_of" json:"replica_of"`
-
-	// Indicates whether this instance is the source of other replica instances.
-	Replicas []Instance
-}
-
-func commonExtract(err error, body interface{}) (*Instance, error) {
-	if err != nil {
-		return nil, err
-	}
-
-	var response struct {
-		Instance Instance `mapstructure:"instance"`
-	}
-
-	err = mapstructure.Decode(body, &response)
-
-	val := body.(map[string]interface{})["instance"].(map[string]interface{})
-
-	if t, ok := val["created"].(string); ok && t != "" {
-		creationTime, err := time.Parse(time.RFC3339, t)
-		if err != nil {
-			return &response.Instance, err
-		}
-		response.Instance.Created = creationTime
-	}
-
-	if t, ok := val["updated"].(string); ok && t != "" {
-		updatedTime, err := time.Parse(time.RFC3339, t)
-		if err != nil {
-			return &response.Instance, err
-		}
-		response.Instance.Updated = updatedTime
-	}
-
-	return &response.Instance, err
-}
-
-// CreateResult represents the result of a Create operation.
-type CreateResult struct {
-	os.CreateResult
-}
-
-// Extract will retrieve an instance from a create result.
-func (r CreateResult) Extract() (*Instance, error) {
-	return commonExtract(r.Err, r.Body)
-}
-
-// GetResult represents the result of a Get operation.
-type GetResult struct {
-	os.GetResult
-}
-
-// Extract will extract an Instance from a GetResult.
-func (r GetResult) Extract() (*Instance, error) {
-	return commonExtract(r.Err, r.Body)
-}
-
-// ConfigResult represents the result of getting default configuration for an
-// instance.
-type ConfigResult struct {
-	gophercloud.Result
-}
-
-// DetachResult represents the result of detaching a replica from its source.
-type DetachResult struct {
-	gophercloud.ErrResult
-}
-
-// Extract will extract the configuration information (in the form of a map)
-// about a particular instance.
-func (r ConfigResult) Extract() (map[string]string, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		Instance struct {
-			Config map[string]string `mapstructure:"configuration"`
-		} `mapstructure:"instance"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-	return response.Instance.Config, err
-}
-
-// UpdateResult represents the result of an Update operation.
-type UpdateResult struct {
-	gophercloud.ErrResult
-}
-
-// ExtractInstances retrieves a slice of instances from a paginated collection.
-func ExtractInstances(page pagination.Page) ([]Instance, error) {
-	casted := page.(os.InstancePage).Body
-
-	var resp struct {
-		Instances []Instance `mapstructure:"instances"`
-	}
-
-	if err := mapstructure.Decode(casted, &resp); err != nil {
-		return nil, err
-	}
-
-	var vals []interface{}
-	switch casted.(type) {
-	case map[string]interface{}:
-		vals = casted.(map[string]interface{})["instances"].([]interface{})
-	case map[string][]interface{}:
-		vals = casted.(map[string][]interface{})["instances"]
-	default:
-		return resp.Instances, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i, v := range vals {
-		val := v.(map[string]interface{})
-
-		if t, ok := val["created"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return resp.Instances, err
-			}
-			resp.Instances[i].Created = creationTime
-		}
-
-		if t, ok := val["updated"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return resp.Instances, err
-			}
-			resp.Instances[i].Updated = updatedTime
-		}
-	}
-
-	return resp.Instances, nil
-}
diff --git a/rackspace/db/v1/instances/urls.go b/rackspace/db/v1/instances/urls.go
deleted file mode 100644
index 5955f4c..0000000
--- a/rackspace/db/v1/instances/urls.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package instances
-
-import "github.com/rackspace/gophercloud"
-
-func baseURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL("instances")
-}
-
-func createURL(c *gophercloud.ServiceClient) string {
-	return baseURL(c)
-}
-
-func resourceURL(c *gophercloud.ServiceClient, id string) string {
-	return c.ServiceURL("instances", id)
-}
-
-func configURL(c *gophercloud.ServiceClient, id string) string {
-	return c.ServiceURL("instances", id, "configuration")
-}
-
-func backupsURL(c *gophercloud.ServiceClient, id string) string {
-	return c.ServiceURL("instances", id, "backups")
-}
diff --git a/rackspace/db/v1/users/delegate.go b/rackspace/db/v1/users/delegate.go
deleted file mode 100644
index 8298c46..0000000
--- a/rackspace/db/v1/users/delegate.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package users
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/users"
-)
-
-// Create will create a new database user for the specified database instance.
-func Create(client *gophercloud.ServiceClient, instanceID string, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(client, instanceID, opts)
-}
-
-// Delete will permanently remove a user from a specified database instance.
-func Delete(client *gophercloud.ServiceClient, instanceID, userName string) os.DeleteResult {
-	return os.Delete(client, instanceID, userName)
-}
diff --git a/rackspace/db/v1/users/delegate_test.go b/rackspace/db/v1/users/delegate_test.go
deleted file mode 100644
index 7a1b773..0000000
--- a/rackspace/db/v1/users/delegate_test.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package users
-
-import (
-	"testing"
-
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/users"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const instanceID = "{instanceID}"
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleCreate(t)
-
-	opts := os.BatchCreateOpts{
-		os.CreateOpts{
-			Databases: db.BatchCreateOpts{
-				db.CreateOpts{Name: "databaseA"},
-			},
-			Name:     "dbuser3",
-			Password: "secretsecret",
-		},
-		os.CreateOpts{
-			Databases: db.BatchCreateOpts{
-				db.CreateOpts{Name: "databaseB"},
-				db.CreateOpts{Name: "databaseC"},
-			},
-			Name:     "dbuser4",
-			Password: "secretsecret",
-		},
-	}
-
-	res := Create(fake.ServiceClient(), instanceID, opts)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleDelete(t)
-
-	res := Delete(fake.ServiceClient(), instanceID, "{userName}")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/rackspace/db/v1/users/doc.go b/rackspace/db/v1/users/doc.go
deleted file mode 100644
index 84f2eb3..0000000
--- a/rackspace/db/v1/users/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package users provides information and interaction with the user API
-// resource in the Rackspace Database service.
-package users
diff --git a/rackspace/db/v1/users/fixtures.go b/rackspace/db/v1/users/fixtures.go
deleted file mode 100644
index bc54867..0000000
--- a/rackspace/db/v1/users/fixtures.go
+++ /dev/null
@@ -1,79 +0,0 @@
-// +build fixtures
-
-package users
-
-const singleDB = `{"databases": [{"name": "databaseE"}]}`
-
-var changePwdReq = `
-{
-  "users": [
-    {
-      "name": "dbuser1",
-      "password": "newpassword"
-    },
-    {
-      "name": "dbuser2",
-      "password": "anotherpassword"
-    }
-  ]
-}
-`
-
-var updateReq = `
-{
-	"user": {
-		"name": "new_username",
-		"password": "new_password"
-	}
-}
-`
-
-var getResp = `
-{
-	"user": {
-		"name": "exampleuser",
-		"host": "foo",
-		"databases": [
-			{
-				"name": "databaseA"
-			},
-			{
-				"name": "databaseB"
-			}
-		]
-	}
-}
-`
-
-var listResp = `
-{
-"users": [
-  {
-    "name": "dbuser1",
-    "host": "localhost",
-    "databases": [
-      {
-        "name": "databaseA"
-      }
-    ]
-  },
-  {
-    "name": "dbuser2",
-    "host": "localhost",
-    "databases": [
-      {
-        "name": "databaseB"
-      },
-      {
-        "name": "databaseC"
-      }
-    ]
-  }
-]
-}
-`
-
-var (
-	listUserAccessResp = singleDB
-	grantUserAccessReq = singleDB
-)
diff --git a/rackspace/db/v1/users/requests.go b/rackspace/db/v1/users/requests.go
deleted file mode 100644
index 74e47ab..0000000
--- a/rackspace/db/v1/users/requests.go
+++ /dev/null
@@ -1,176 +0,0 @@
-package users
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/users"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List will list all available users for a specified database instance.
-func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager {
-	createPageFn := func(r pagination.PageResult) pagination.Page {
-		return UserPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, baseURL(client, instanceID), createPageFn)
-}
-
-/*
-ChangePassword changes the password for one or more users. For example, to
-change the respective passwords for two users:
-
-	opts := os.BatchCreateOpts{
-		os.CreateOpts{Name: "db_user_1", Password: "new_password_1"},
-		os.CreateOpts{Name: "db_user_2", Password: "new_password_2"},
-	}
-
-	ChangePassword(client, "instance_id", opts)
-*/
-func ChangePassword(client *gophercloud.ServiceClient, instanceID string, opts os.CreateOptsBuilder) UpdatePasswordsResult {
-	var res UpdatePasswordsResult
-
-	reqBody, err := opts.ToUserCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = client.Request("PUT", baseURL(client, instanceID), gophercloud.RequestOpts{
-		JSONBody: &reqBody,
-		OkCodes:  []int{202},
-	})
-
-	return res
-}
-
-// UpdateOpts is the struct responsible for updating an existing user.
-type UpdateOpts struct {
-	// [OPTIONAL] Specifies a name for the user. Valid names can be composed
-	// of the following characters: letters (either case); numbers; these
-	// characters '@', '?', '#', ' ' but NEVER beginning a name string; '_' is
-	// permitted anywhere. Prohibited characters that are forbidden include:
-	// single quotes, double quotes, back quotes, semicolons, commas, backslashes,
-	// and forward slashes. Spaces at the front or end of a user name are also
-	// not permitted.
-	Name string
-
-	// [OPTIONAL] Specifies a password for the user.
-	Password string
-
-	// [OPTIONAL] Specifies the host from which a user is allowed to connect to
-	// the database. Possible values are a string containing an IPv4 address or
-	// "%" to allow connecting from any host. Optional; the default is "%".
-	Host string
-}
-
-// ToMap is a convenience function for creating sub-maps for individual users.
-func (opts UpdateOpts) ToMap() (map[string]interface{}, error) {
-	if opts.Name == "root" {
-		return nil, errors.New("root is a reserved user name and cannot be used")
-	}
-
-	user := map[string]interface{}{}
-
-	if opts.Name != "" {
-		user["name"] = opts.Name
-	}
-
-	if opts.Password != "" {
-		user["password"] = opts.Password
-	}
-
-	if opts.Host != "" {
-		user["host"] = opts.Host
-	}
-
-	return user, nil
-}
-
-// Update will modify the attributes of a specified user. Attributes that can
-// be updated are: user name, password, and host.
-func Update(client *gophercloud.ServiceClient, instanceID, userName string, opts UpdateOpts) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-	reqBody = map[string]interface{}{"user": reqBody}
-
-	_, res.Err = client.Request("PUT", userURL(client, instanceID, userName), gophercloud.RequestOpts{
-		JSONBody: &reqBody,
-		OkCodes:  []int{202},
-	})
-
-	return res
-}
-
-// Get will retrieve the details for a particular user.
-func Get(client *gophercloud.ServiceClient, instanceID, userName string) GetResult {
-	var res GetResult
-
-	_, res.Err = client.Request("GET", userURL(client, instanceID, userName), gophercloud.RequestOpts{
-		JSONResponse: &res.Body,
-		OkCodes:      []int{200},
-	})
-
-	return res
-}
-
-// ListAccess will list all of the databases a user has access to.
-func ListAccess(client *gophercloud.ServiceClient, instanceID, userName string) pagination.Pager {
-	pageFn := func(r pagination.PageResult) pagination.Page {
-		return AccessPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, dbsURL(client, instanceID, userName), pageFn)
-}
-
-/*
-GrantAccess for the specified user to one or more databases on a specified
-instance. For example, to add a user to multiple databases:
-
-	opts := db.BatchCreateOpts{
-		db.CreateOpts{Name: "database_1"},
-		db.CreateOpts{Name: "database_3"},
-		db.CreateOpts{Name: "database_19"},
-	}
-
-	GrantAccess(client, "instance_id", "user_name", opts)
-*/
-func GrantAccess(client *gophercloud.ServiceClient, instanceID, userName string, opts db.CreateOptsBuilder) GrantAccessResult {
-	var res GrantAccessResult
-
-	reqBody, err := opts.ToDBCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = client.Request("PUT", dbsURL(client, instanceID, userName), gophercloud.RequestOpts{
-		JSONBody: &reqBody,
-		OkCodes:  []int{202},
-	})
-
-	return res
-}
-
-/*
-RevokeAccess will revoke access for the specified user to one or more databases
-on a specified instance. For example:
-
-	RevokeAccess(client, "instance_id", "user_name", "db_name")
-*/
-func RevokeAccess(client *gophercloud.ServiceClient, instanceID, userName, dbName string) RevokeAccessResult {
-	var res RevokeAccessResult
-
-	_, res.Err = client.Request("DELETE", dbURL(client, instanceID, userName, dbName), gophercloud.RequestOpts{
-		OkCodes: []int{202},
-	})
-
-	return res
-}
diff --git a/rackspace/db/v1/users/requests_test.go b/rackspace/db/v1/users/requests_test.go
deleted file mode 100644
index 2f2dca7..0000000
--- a/rackspace/db/v1/users/requests_test.go
+++ /dev/null
@@ -1,156 +0,0 @@
-package users
-
-import (
-	"testing"
-
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	os "github.com/rackspace/gophercloud/openstack/db/v1/users"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-	"github.com/rackspace/gophercloud/testhelper/fixture"
-)
-
-var (
-	userName = "{userName}"
-	_rootURL = "/instances/" + instanceID + "/users"
-	_userURL = _rootURL + "/" + userName
-	_dbURL   = _userURL + "/databases"
-)
-
-func TestChangeUserPassword(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _rootURL, "PUT", changePwdReq, "", 202)
-
-	opts := os.BatchCreateOpts{
-		os.CreateOpts{Name: "dbuser1", Password: "newpassword"},
-		os.CreateOpts{Name: "dbuser2", Password: "anotherpassword"},
-	}
-
-	err := ChangePassword(fake.ServiceClient(), instanceID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestUpdateUser(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _userURL, "PUT", updateReq, "", 202)
-
-	opts := UpdateOpts{
-		Name:     "new_username",
-		Password: "new_password",
-	}
-
-	err := Update(fake.ServiceClient(), instanceID, userName, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestGetUser(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _userURL, "GET", "", getResp, 200)
-
-	user, err := Get(fake.ServiceClient(), instanceID, userName).Extract()
-
-	th.AssertNoErr(t, err)
-
-	expected := &User{
-		Name: "exampleuser",
-		Host: "foo",
-		Databases: []db.Database{
-			db.Database{Name: "databaseA"},
-			db.Database{Name: "databaseB"},
-		},
-	}
-
-	th.AssertDeepEquals(t, expected, user)
-}
-
-func TestUserAccessList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _userURL+"/databases", "GET", "", listUserAccessResp, 200)
-
-	expectedDBs := []db.Database{
-		db.Database{Name: "databaseE"},
-	}
-
-	pages := 0
-	err := ListAccess(fake.ServiceClient(), instanceID, userName).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractDBs(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, expectedDBs, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestUserList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	fixture.SetupHandler(t, "/instances/"+instanceID+"/users", "GET", "", listResp, 200)
-
-	expectedUsers := []User{
-		User{
-			Databases: []db.Database{
-				db.Database{Name: "databaseA"},
-			},
-			Name: "dbuser1",
-			Host: "localhost",
-		},
-		User{
-			Databases: []db.Database{
-				db.Database{Name: "databaseB"},
-				db.Database{Name: "databaseC"},
-			},
-			Name: "dbuser2",
-			Host: "localhost",
-		},
-	}
-
-	pages := 0
-	err := List(fake.ServiceClient(), instanceID).EachPage(func(page pagination.Page) (bool, error) {
-		pages++
-
-		actual, err := ExtractUsers(page)
-		if err != nil {
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, expectedUsers, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, pages)
-}
-
-func TestGrantAccess(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _dbURL, "PUT", grantUserAccessReq, "", 202)
-
-	opts := db.BatchCreateOpts{db.CreateOpts{Name: "databaseE"}}
-	err := GrantAccess(fake.ServiceClient(), instanceID, userName, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestRevokeAccess(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	fixture.SetupHandler(t, _dbURL+"/{dbName}", "DELETE", "", "", 202)
-
-	err := RevokeAccess(fake.ServiceClient(), instanceID, userName, "{dbName}").ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/db/v1/users/results.go b/rackspace/db/v1/users/results.go
deleted file mode 100644
index 85b3a7a..0000000
--- a/rackspace/db/v1/users/results.go
+++ /dev/null
@@ -1,149 +0,0 @@
-package users
-
-import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// User represents a database user
-type User struct {
-	// The user name
-	Name string
-
-	// The user password
-	Password string
-
-	// Specifies the host from which a user is allowed to connect to the database.
-	// Possible values are a string containing an IPv4 address or "%" to allow
-	// connecting from any host.
-	Host string
-
-	// The databases associated with this user
-	Databases []db.Database
-}
-
-// UpdatePasswordsResult represents the result of changing a user password.
-type UpdatePasswordsResult struct {
-	gophercloud.ErrResult
-}
-
-// UpdateResult represents the result of updating a user.
-type UpdateResult struct {
-	gophercloud.ErrResult
-}
-
-// GetResult represents the result of getting a user.
-type GetResult struct {
-	gophercloud.Result
-}
-
-// Extract will retrieve a User struct from a getresult.
-func (r GetResult) Extract() (*User, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		User User `mapstructure:"user"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-	return &response.User, err
-}
-
-// AccessPage represents a single page of a paginated user collection.
-type AccessPage struct {
-	pagination.LinkedPageBase
-}
-
-// IsEmpty checks to see whether the collection is empty.
-func (page AccessPage) IsEmpty() (bool, error) {
-	users, err := ExtractDBs(page)
-	if err != nil {
-		return true, err
-	}
-	return len(users) == 0, nil
-}
-
-// NextPageURL will retrieve the next page URL.
-func (page AccessPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"databases_links"`
-	}
-
-	var r resp
-	err := mapstructure.Decode(page.Body, &r)
-	if err != nil {
-		return "", err
-	}
-
-	return gophercloud.ExtractNextURL(r.Links)
-}
-
-// ExtractDBs will convert a generic pagination struct into a more
-// relevant slice of DB structs.
-func ExtractDBs(page pagination.Page) ([]db.Database, error) {
-	casted := page.(AccessPage).Body
-
-	var response struct {
-		DBs []db.Database `mapstructure:"databases"`
-	}
-
-	err := mapstructure.Decode(casted, &response)
-	return response.DBs, err
-}
-
-// UserPage represents a single page of a paginated user collection.
-type UserPage struct {
-	pagination.LinkedPageBase
-}
-
-// IsEmpty checks to see whether the collection is empty.
-func (page UserPage) IsEmpty() (bool, error) {
-	users, err := ExtractUsers(page)
-	if err != nil {
-		return true, err
-	}
-	return len(users) == 0, nil
-}
-
-// NextPageURL will retrieve the next page URL.
-func (page UserPage) NextPageURL() (string, error) {
-	type resp struct {
-		Links []gophercloud.Link `mapstructure:"users_links"`
-	}
-
-	var r resp
-	err := mapstructure.Decode(page.Body, &r)
-	if err != nil {
-		return "", err
-	}
-
-	return gophercloud.ExtractNextURL(r.Links)
-}
-
-// ExtractUsers will convert a generic pagination struct into a more
-// relevant slice of User structs.
-func ExtractUsers(page pagination.Page) ([]User, error) {
-	casted := page.(UserPage).Body
-
-	var response struct {
-		Users []User `mapstructure:"users"`
-	}
-
-	err := mapstructure.Decode(casted, &response)
-
-	return response.Users, err
-}
-
-// GrantAccessResult represents the result of granting access to a user.
-type GrantAccessResult struct {
-	gophercloud.ErrResult
-}
-
-// RevokeAccessResult represents the result of revoking access to a user.
-type RevokeAccessResult struct {
-	gophercloud.ErrResult
-}
diff --git a/rackspace/db/v1/users/urls.go b/rackspace/db/v1/users/urls.go
deleted file mode 100644
index bac8788..0000000
--- a/rackspace/db/v1/users/urls.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package users
-
-import "github.com/rackspace/gophercloud"
-
-func baseURL(c *gophercloud.ServiceClient, instanceID string) string {
-	return c.ServiceURL("instances", instanceID, "users")
-}
-
-func userURL(c *gophercloud.ServiceClient, instanceID, userName string) string {
-	return c.ServiceURL("instances", instanceID, "users", userName)
-}
-
-func dbsURL(c *gophercloud.ServiceClient, instanceID, userName string) string {
-	return c.ServiceURL("instances", instanceID, "users", userName, "databases")
-}
-
-func dbURL(c *gophercloud.ServiceClient, instanceID, userName, dbName string) string {
-	return c.ServiceURL("instances", instanceID, "users", userName, "databases", dbName)
-}
diff --git a/rackspace/identity/v2/extensions/delegate.go b/rackspace/identity/v2/extensions/delegate.go
deleted file mode 100644
index fc547cd..0000000
--- a/rackspace/identity/v2/extensions/delegate.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package extensions
-
-import (
-	"github.com/rackspace/gophercloud"
-	common "github.com/rackspace/gophercloud/openstack/common/extensions"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// ExtractExtensions accepts a Page struct, specifically an ExtensionPage struct, and extracts the
-// elements into a slice of os.Extension structs.
-func ExtractExtensions(page pagination.Page) ([]common.Extension, error) {
-	return common.ExtractExtensions(page)
-}
-
-// Get retrieves information for a specific extension using its alias.
-func Get(c *gophercloud.ServiceClient, alias string) common.GetResult {
-	return common.Get(c, alias)
-}
-
-// List returns a Pager which allows you to iterate over the full collection of extensions.
-// It does not accept query parameters.
-func List(c *gophercloud.ServiceClient) pagination.Pager {
-	return common.List(c)
-}
diff --git a/rackspace/identity/v2/extensions/delegate_test.go b/rackspace/identity/v2/extensions/delegate_test.go
deleted file mode 100644
index e30f794..0000000
--- a/rackspace/identity/v2/extensions/delegate_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package extensions
-
-import (
-	"testing"
-
-	common "github.com/rackspace/gophercloud/openstack/common/extensions"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	common.HandleListExtensionsSuccessfully(t)
-
-	count := 0
-
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractExtensions(page)
-		th.AssertNoErr(t, err)
-		th.AssertDeepEquals(t, common.ExpectedExtensions, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	common.HandleGetExtensionSuccessfully(t)
-
-	actual, err := Get(fake.ServiceClient(), "agent").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, common.SingleExtension, actual)
-}
diff --git a/rackspace/identity/v2/extensions/doc.go b/rackspace/identity/v2/extensions/doc.go
deleted file mode 100644
index b02a95b..0000000
--- a/rackspace/identity/v2/extensions/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package extensions provides information and interaction with the all the
-// extensions available for the Rackspace Identity service.
-package extensions
diff --git a/rackspace/identity/v2/roles/delegate.go b/rackspace/identity/v2/roles/delegate.go
deleted file mode 100644
index a6ee851..0000000
--- a/rackspace/identity/v2/roles/delegate.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package roles
-
-import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-
-	os "github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles"
-)
-
-// List is the operation responsible for listing all available global roles
-// that a user can adopt.
-func List(client *gophercloud.ServiceClient) pagination.Pager {
-	return os.List(client)
-}
-
-// AddUserRole is the operation responsible for assigning a particular role to
-// a user. This is confined to the scope of the user's tenant - so the tenant
-// ID is a required argument.
-func AddUserRole(client *gophercloud.ServiceClient, userID, roleID string) UserRoleResult {
-	var result UserRoleResult
-
-	_, result.Err = client.Request("PUT", userRoleURL(client, userID, roleID), gophercloud.RequestOpts{
-		OkCodes: []int{200, 201},
-	})
-
-	return result
-}
-
-// DeleteUserRole is the operation responsible for deleting a particular role
-// from a user. This is confined to the scope of the user's tenant - so the
-// tenant ID is a required argument.
-func DeleteUserRole(client *gophercloud.ServiceClient, userID, roleID string) UserRoleResult {
-	var result UserRoleResult
-
-	_, result.Err = client.Request("DELETE", userRoleURL(client, userID, roleID), gophercloud.RequestOpts{
-		OkCodes: []int{204},
-	})
-
-	return result
-}
-
-// UserRoleResult represents the result of either an AddUserRole or
-// a DeleteUserRole operation.
-type UserRoleResult struct {
-	gophercloud.ErrResult
-}
-
-func userRoleURL(c *gophercloud.ServiceClient, userID, roleID string) string {
-	return c.ServiceURL(os.UserPath, userID, os.RolePath, os.ExtPath, roleID)
-}
diff --git a/rackspace/identity/v2/roles/delegate_test.go b/rackspace/identity/v2/roles/delegate_test.go
deleted file mode 100644
index fcee97d..0000000
--- a/rackspace/identity/v2/roles/delegate_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package roles
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestRole(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockListRoleResponse(t)
-
-	count := 0
-
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractRoles(page)
-		if err != nil {
-			t.Errorf("Failed to extract users: %v", err)
-			return false, err
-		}
-
-		expected := []os.Role{
-			os.Role{
-				ID:          "123",
-				Name:        "compute:admin",
-				Description: "Nova Administrator",
-				ServiceID:   "cke5372ebabeeabb70a0e702a4626977x4406e5",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestAddUserRole(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockAddUserRoleResponse(t)
-
-	err := AddUserRole(client.ServiceClient(), "{user_id}", "{role_id}").ExtractErr()
-
-	th.AssertNoErr(t, err)
-}
-
-func TestDeleteUserRole(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	MockDeleteUserRoleResponse(t)
-
-	err := DeleteUserRole(client.ServiceClient(), "{user_id}", "{role_id}").ExtractErr()
-
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/identity/v2/roles/fixtures.go b/rackspace/identity/v2/roles/fixtures.go
deleted file mode 100644
index e194e31..0000000
--- a/rackspace/identity/v2/roles/fixtures.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// +build fixtures
-
-package roles
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func MockListRoleResponse(t *testing.T) {
-	th.Mux.HandleFunc("/OS-KSADM/roles", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "roles": [
-        {
-            "id": "123",
-            "name": "compute:admin",
-            "description": "Nova Administrator",
-            "serviceId": "cke5372ebabeeabb70a0e702a4626977x4406e5"
-        }
-    ]
-}
-  `)
-	})
-}
-
-func MockAddUserRoleResponse(t *testing.T) {
-	th.Mux.HandleFunc("/users/{user_id}/roles/OS-KSADM/{role_id}", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusCreated)
-	})
-}
-
-func MockDeleteUserRoleResponse(t *testing.T) {
-	th.Mux.HandleFunc("/users/{user_id}/roles/OS-KSADM/{role_id}", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
diff --git a/rackspace/identity/v2/tenants/delegate.go b/rackspace/identity/v2/tenants/delegate.go
deleted file mode 100644
index 6cdd0cf..0000000
--- a/rackspace/identity/v2/tenants/delegate.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package tenants
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// ExtractTenants interprets a page of List results as a more usable slice of Tenant structs.
-func ExtractTenants(page pagination.Page) ([]os.Tenant, error) {
-	return os.ExtractTenants(page)
-}
-
-// List enumerates the tenants to which the current token grants access.
-func List(client *gophercloud.ServiceClient, opts *os.ListOpts) pagination.Pager {
-	return os.List(client, opts)
-}
diff --git a/rackspace/identity/v2/tenants/delegate_test.go b/rackspace/identity/v2/tenants/delegate_test.go
deleted file mode 100644
index eccbfe2..0000000
--- a/rackspace/identity/v2/tenants/delegate_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package tenants
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListTenants(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListTenantsSuccessfully(t)
-
-	count := 0
-	err := List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		actual, err := ExtractTenants(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, os.ExpectedTenantSlice, actual)
-
-		count++
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
diff --git a/rackspace/identity/v2/tenants/doc.go b/rackspace/identity/v2/tenants/doc.go
deleted file mode 100644
index c1825c2..0000000
--- a/rackspace/identity/v2/tenants/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package tenants provides information and interaction with the tenant
-// API resource for the Rackspace Identity service.
-package tenants
diff --git a/rackspace/identity/v2/tokens/delegate.go b/rackspace/identity/v2/tokens/delegate.go
deleted file mode 100644
index 4f9885a..0000000
--- a/rackspace/identity/v2/tokens/delegate.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package tokens
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
-)
-
-var (
-	// ErrPasswordProvided is returned if both a password and an API key are provided to Create.
-	ErrPasswordProvided = errors.New("Please provide either a password or an API key.")
-)
-
-// AuthOptions wraps the OpenStack AuthOptions struct to be able to customize the request body
-// when API key authentication is used.
-type AuthOptions struct {
-	os.AuthOptions
-}
-
-// WrapOptions embeds a root AuthOptions struct in a package-specific one.
-func WrapOptions(original gophercloud.AuthOptions) AuthOptions {
-	return AuthOptions{AuthOptions: os.WrapOptions(original)}
-}
-
-// ToTokenCreateMap serializes an AuthOptions into a request body. If an API key is provided, it
-// will be used, otherwise
-func (auth AuthOptions) ToTokenCreateMap() (map[string]interface{}, error) {
-	if auth.APIKey == "" {
-		return auth.AuthOptions.ToTokenCreateMap()
-	}
-
-	// Verify that other required attributes are present.
-	if auth.Username == "" {
-		return nil, os.ErrUsernameRequired
-	}
-
-	authMap := make(map[string]interface{})
-
-	authMap["RAX-KSKEY:apiKeyCredentials"] = map[string]interface{}{
-		"username": auth.Username,
-		"apiKey":   auth.APIKey,
-	}
-
-	if auth.TenantID != "" {
-		authMap["tenantId"] = auth.TenantID
-	}
-	if auth.TenantName != "" {
-		authMap["tenantName"] = auth.TenantName
-	}
-
-	return map[string]interface{}{"auth": authMap}, nil
-}
-
-// Create authenticates to Rackspace's identity service and attempts to acquire a Token. Rather
-// than interact with this service directly, users should generally call
-// rackspace.AuthenticatedClient().
-func Create(client *gophercloud.ServiceClient, auth AuthOptions) os.CreateResult {
-	return os.Create(client, auth)
-}
diff --git a/rackspace/identity/v2/tokens/delegate_test.go b/rackspace/identity/v2/tokens/delegate_test.go
deleted file mode 100644
index 6678ff4..0000000
--- a/rackspace/identity/v2/tokens/delegate_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package tokens
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func tokenPost(t *testing.T, options gophercloud.AuthOptions, requestJSON string) os.CreateResult {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleTokenPost(t, requestJSON)
-
-	return Create(client.ServiceClient(), WrapOptions(options))
-}
-
-func TestCreateTokenWithAPIKey(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-		APIKey:   "1234567890abcdef",
-	}
-
-	os.IsSuccessful(t, tokenPost(t, options, `
-    {
-      "auth": {
-        "RAX-KSKEY:apiKeyCredentials": {
-          "username": "me",
-          "apiKey": "1234567890abcdef"
-        }
-      }
-    }
-  `))
-}
diff --git a/rackspace/identity/v2/tokens/doc.go b/rackspace/identity/v2/tokens/doc.go
deleted file mode 100644
index 44043e5..0000000
--- a/rackspace/identity/v2/tokens/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package tokens provides information and interaction with the token
-// API resource for the Rackspace Identity service.
-package tokens
diff --git a/rackspace/identity/v2/users/delegate.go b/rackspace/identity/v2/users/delegate.go
deleted file mode 100644
index 6135bec..0000000
--- a/rackspace/identity/v2/users/delegate.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package users
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/identity/v2/users"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a pager that allows traversal over a collection of users.
-func List(client *gophercloud.ServiceClient) pagination.Pager {
-	return os.List(client)
-}
-
-// CommonOpts are the options which are shared between CreateOpts and
-// UpdateOpts
-type CommonOpts struct {
-	// Required. The username to assign to the user. When provided, the username
-	// must:
-	// - start with an alphabetical (A-Za-z) character
-	// - have a minimum length of 1 character
-	//
-	// The username may contain upper and lowercase characters, as well as any of
-	// the following special character: . - @ _
-	Username string
-
-	// Required. Email address for the user account.
-	Email string
-
-	// Required. Indicates whether the user can authenticate after the user
-	// account is created. If no value is specified, the default value is true.
-	Enabled os.EnabledState
-
-	// Optional. The password to assign to the user. If provided, the password
-	// must:
-	// - start with an alphabetical (A-Za-z) character
-	// - have a minimum length of 8 characters
-	// - contain at least one uppercase character, one lowercase character, and
-	//   one numeric character.
-	//
-	// The password may contain any of the following special characters: . - @ _
-	Password string
-}
-
-// CreateOpts represents the options needed when creating new users.
-type CreateOpts CommonOpts
-
-// ToUserCreateMap assembles a request body based on the contents of a CreateOpts.
-func (opts CreateOpts) ToUserCreateMap() (map[string]interface{}, error) {
-	m := make(map[string]interface{})
-
-	if opts.Username == "" {
-		return m, errors.New("Username is a required field")
-	}
-	if opts.Enabled == nil {
-		return m, errors.New("Enabled is a required field")
-	}
-	if opts.Email == "" {
-		return m, errors.New("Email is a required field")
-	}
-
-	if opts.Username != "" {
-		m["username"] = opts.Username
-	}
-	if opts.Email != "" {
-		m["email"] = opts.Email
-	}
-	if opts.Enabled != nil {
-		m["enabled"] = opts.Enabled
-	}
-	if opts.Password != "" {
-		m["OS-KSADM:password"] = opts.Password
-	}
-
-	return map[string]interface{}{"user": m}, nil
-}
-
-// Create is the operation responsible for creating new users.
-func Create(client *gophercloud.ServiceClient, opts os.CreateOptsBuilder) CreateResult {
-	return CreateResult{os.Create(client, opts)}
-}
-
-// Get requests details on a single user, either by ID.
-func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	return GetResult{os.Get(client, id)}
-}
-
-// UpdateOptsBuilder allows extensions to add additional attributes to the Update request.
-type UpdateOptsBuilder interface {
-	ToUserUpdateMap() map[string]interface{}
-}
-
-// UpdateOpts specifies the base attributes that may be updated on an existing server.
-type UpdateOpts CommonOpts
-
-// ToUserUpdateMap formats an UpdateOpts structure into a request body.
-func (opts UpdateOpts) ToUserUpdateMap() map[string]interface{} {
-	m := make(map[string]interface{})
-
-	if opts.Username != "" {
-		m["username"] = opts.Username
-	}
-	if opts.Enabled != nil {
-		m["enabled"] = &opts.Enabled
-	}
-	if opts.Email != "" {
-		m["email"] = opts.Email
-	}
-
-	return map[string]interface{}{"user": m}
-}
-
-// Update is the operation responsible for updating exist users by their UUID.
-func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var result UpdateResult
-
-	_, result.Err = client.Request("POST", os.ResourceURL(client, id), gophercloud.RequestOpts{
-		JSONResponse: &result.Body,
-		JSONBody:     opts.ToUserUpdateMap(),
-		OkCodes:      []int{200},
-	})
-
-	return result
-}
-
-// Delete is the operation responsible for permanently deleting an API user.
-func Delete(client *gophercloud.ServiceClient, id string) os.DeleteResult {
-	return os.Delete(client, id)
-}
-
-// ResetAPIKey resets the User's API key.
-func ResetAPIKey(client *gophercloud.ServiceClient, id string) ResetAPIKeyResult {
-	var result ResetAPIKeyResult
-
-	_, result.Err = client.Request("POST", resetAPIKeyURL(client, id), gophercloud.RequestOpts{
-		JSONResponse: &result.Body,
-		OkCodes:      []int{200},
-	})
-
-	return result
-}
diff --git a/rackspace/identity/v2/users/delegate_test.go b/rackspace/identity/v2/users/delegate_test.go
deleted file mode 100644
index 62faf0c..0000000
--- a/rackspace/identity/v2/users/delegate_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package users
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/identity/v2/users"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListResponse(t)
-
-	count := 0
-
-	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		users, err := os.ExtractUsers(page)
-
-		th.AssertNoErr(t, err)
-		th.AssertEquals(t, "u1000", users[0].ID)
-		th.AssertEquals(t, "u1001", users[1].ID)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestCreateUser(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockCreateUser(t)
-
-	opts := CreateOpts{
-		Username: "new_user",
-		Enabled:  os.Disabled,
-		Email:    "new_user@foo.com",
-		Password: "foo",
-	}
-
-	user, err := Create(client.ServiceClient(), opts).Extract()
-
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "123456", user.ID)
-	th.AssertEquals(t, "5830280", user.DomainID)
-	th.AssertEquals(t, "DFW", user.DefaultRegion)
-}
-
-func TestGetUser(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetUser(t)
-
-	user, err := Get(client.ServiceClient(), "new_user").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, true, user.Enabled)
-	th.AssertEquals(t, true, user.MultiFactorEnabled)
-}
-
-func TestUpdateUser(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockUpdateUser(t)
-
-	id := "c39e3de9be2d4c779f1dfd6abacc176d"
-
-	opts := UpdateOpts{
-		Enabled: os.Enabled,
-		Email:   "new_email@foo.com",
-	}
-
-	user, err := Update(client.ServiceClient(), id, opts).Extract()
-
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, true, user.Enabled)
-	th.AssertEquals(t, "new_email@foo.com", user.Email)
-}
-
-func TestDeleteServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteUser(t)
-
-	res := Delete(client.ServiceClient(), "c39e3de9be2d4c779f1dfd6abacc176d")
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestResetAPIKey(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockResetAPIKey(t)
-
-	apiKey, err := ResetAPIKey(client.ServiceClient(), "99").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, "joesmith", apiKey.Username)
-	th.AssertEquals(t, "mooH1eiLahd5ahYood7r", apiKey.APIKey)
-}
diff --git a/rackspace/identity/v2/users/fixtures.go b/rackspace/identity/v2/users/fixtures.go
deleted file mode 100644
index d5091b9..0000000
--- a/rackspace/identity/v2/users/fixtures.go
+++ /dev/null
@@ -1,156 +0,0 @@
-// +build fixtures
-
-package users
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func mockListResponse(t *testing.T) {
-	th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-		"users":[
-				{
-						"id": "u1000",
-						"username": "jqsmith",
-						"email": "john.smith@example.org",
-						"enabled": true
-				},
-				{
-						"id": "u1001",
-						"username": "jqsmith",
-						"email": "jane.smith@example.org",
-						"enabled": true
-				}
-		]
-}
-	`)
-	})
-}
-
-func mockCreateUser(t *testing.T) {
-	th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-    "user": {
-        "username": "new_user",
-        "enabled": false,
-        "email": "new_user@foo.com",
-				"OS-KSADM:password": "foo"
-    }
-}
-  `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "user": {
-    "RAX-AUTH:defaultRegion": "DFW",
-    "RAX-AUTH:domainId": "5830280",
-    "id": "123456",
-    "username": "new_user",
-    "email": "new_user@foo.com",
-    "enabled": false
-  }
-}
-`)
-	})
-}
-
-func mockGetUser(t *testing.T) {
-	th.Mux.HandleFunc("/users/new_user", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-	"user": {
-		"RAX-AUTH:defaultRegion": "DFW",
-		"RAX-AUTH:domainId": "5830280",
-		"RAX-AUTH:multiFactorEnabled": "true",
-		"id": "c39e3de9be2d4c779f1dfd6abacc176d",
-		"username": "jqsmith",
-		"email": "john.smith@example.org",
-		"enabled": true
-	}
-}
-`)
-	})
-}
-
-func mockUpdateUser(t *testing.T) {
-	th.Mux.HandleFunc("/users/c39e3de9be2d4c779f1dfd6abacc176d", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-	"user": {
-		"email": "new_email@foo.com",
-		"enabled": true
-	}
-}
-`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-	"user": {
-		"RAX-AUTH:defaultRegion": "DFW",
-		"RAX-AUTH:domainId": "5830280",
-		"RAX-AUTH:multiFactorEnabled": "true",
-		"id": "123456",
-		"username": "jqsmith",
-		"email": "new_email@foo.com",
-		"enabled": true
-	}
-}
-`)
-	})
-}
-
-func mockDeleteUser(t *testing.T) {
-	th.Mux.HandleFunc("/users/c39e3de9be2d4c779f1dfd6abacc176d", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-}
-
-func mockResetAPIKey(t *testing.T) {
-	th.Mux.HandleFunc("/users/99/OS-KSADM/credentials/RAX-KSKEY:apiKeyCredentials/RAX-AUTH/reset", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-{
-    "RAX-KSKEY:apiKeyCredentials": {
-        "username": "joesmith",
-        "apiKey": "mooH1eiLahd5ahYood7r"
-    }
-}`)
-	})
-}
diff --git a/rackspace/identity/v2/users/results.go b/rackspace/identity/v2/users/results.go
deleted file mode 100644
index 6936ecb..0000000
--- a/rackspace/identity/v2/users/results.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package users
-
-import (
-	"strconv"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/identity/v2/users"
-
-	"github.com/mitchellh/mapstructure"
-)
-
-// User represents a user resource that exists on the API.
-type User struct {
-	// The UUID for this user.
-	ID string
-
-	// The human name for this user.
-	Name string
-
-	// The username for this user.
-	Username string
-
-	// Indicates whether the user is enabled (true) or disabled (false).
-	Enabled bool
-
-	// The email address for this user.
-	Email string
-
-	// The ID of the tenant to which this user belongs.
-	TenantID string `mapstructure:"tenant_id"`
-
-	// Specifies the default region for the user account. This value is inherited
-	// from the user administrator when the account is created.
-	DefaultRegion string `mapstructure:"RAX-AUTH:defaultRegion"`
-
-	// Identifies the domain that contains the user account. This value is
-	// inherited from the user administrator when the account is created.
-	DomainID string `mapstructure:"RAX-AUTH:domainId"`
-
-	// The password value that the user needs for authentication. If the Add user
-	// request included a password value, this attribute is not included in the
-	// response.
-	Password string `mapstructure:"OS-KSADM:password"`
-
-	// Indicates whether the user has enabled multi-factor authentication.
-	MultiFactorEnabled bool `mapstructure:"RAX-AUTH:multiFactorEnabled"`
-}
-
-// CreateResult represents the result of a Create operation
-type CreateResult struct {
-	os.CreateResult
-}
-
-// GetResult represents the result of a Get operation
-type GetResult struct {
-	os.GetResult
-}
-
-// UpdateResult represents the result of an Update operation
-type UpdateResult struct {
-	os.UpdateResult
-}
-
-func commonExtract(resp interface{}, err error) (*User, error) {
-	if err != nil {
-		return nil, err
-	}
-
-	var respStruct struct {
-		User *User `json:"user"`
-	}
-
-	// Since the API returns a string instead of a bool, we need to hack the JSON
-	json := resp.(map[string]interface{})
-	user := json["user"].(map[string]interface{})
-	if s, ok := user["RAX-AUTH:multiFactorEnabled"].(string); ok && s != "" {
-		if b, err := strconv.ParseBool(s); err == nil {
-			user["RAX-AUTH:multiFactorEnabled"] = b
-		}
-	}
-
-	err = mapstructure.Decode(json, &respStruct)
-
-	return respStruct.User, err
-}
-
-// Extract will get the Snapshot object out of the GetResult object.
-func (r GetResult) Extract() (*User, error) {
-	return commonExtract(r.Body, r.Err)
-}
-
-// Extract will get the Snapshot object out of the CreateResult object.
-func (r CreateResult) Extract() (*User, error) {
-	return commonExtract(r.Body, r.Err)
-}
-
-// Extract will get the Snapshot object out of the UpdateResult object.
-func (r UpdateResult) Extract() (*User, error) {
-	return commonExtract(r.Body, r.Err)
-}
-
-// ResetAPIKeyResult represents the server response to the ResetAPIKey method.
-type ResetAPIKeyResult struct {
-	gophercloud.Result
-}
-
-// ResetAPIKeyValue represents an API Key that has been reset.
-type ResetAPIKeyValue struct {
-	// The Username for this API Key reset.
-	Username string `mapstructure:"username"`
-
-	// The new API Key for this user.
-	APIKey string `mapstructure:"apiKey"`
-}
-
-// Extract will get the Error or ResetAPIKeyValue object out of the ResetAPIKeyResult object.
-func (r ResetAPIKeyResult) Extract() (*ResetAPIKeyValue, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		ResetAPIKeyValue ResetAPIKeyValue `mapstructure:"RAX-KSKEY:apiKeyCredentials"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.ResetAPIKeyValue, err
-}
diff --git a/rackspace/identity/v2/users/urls.go b/rackspace/identity/v2/users/urls.go
deleted file mode 100644
index bc1aaef..0000000
--- a/rackspace/identity/v2/users/urls.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package users
-
-import "github.com/rackspace/gophercloud"
-
-func resetAPIKeyURL(client *gophercloud.ServiceClient, id string) string {
-	return client.ServiceURL("users", id, "OS-KSADM", "credentials", "RAX-KSKEY:apiKeyCredentials", "RAX-AUTH", "reset")
-}
diff --git a/rackspace/lb/v1/acl/doc.go b/rackspace/lb/v1/acl/doc.go
deleted file mode 100644
index 42325fe..0000000
--- a/rackspace/lb/v1/acl/doc.go
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
-Package acl provides information and interaction with the access lists feature
-of the Rackspace Cloud Load Balancer service.
-
-The access list management feature allows fine-grained network access controls
-to be applied to the load balancer's virtual IP address. A single IP address,
-multiple IP addresses, or entire network subnets can be added. Items that are
-configured with the ALLOW type always takes precedence over items with the DENY
-type. To reject traffic from all items except for those with the ALLOW type,
-add a networkItem with an address of "0.0.0.0/0" and a DENY type.
-*/
-package acl
diff --git a/rackspace/lb/v1/acl/fixtures.go b/rackspace/lb/v1/acl/fixtures.go
deleted file mode 100644
index 6761b9f..0000000
--- a/rackspace/lb/v1/acl/fixtures.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// +build fixtures
-
-package acl
-
-import (
-	"fmt"
-	"net/http"
-	"strconv"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func _rootURL(lbID int) string {
-	return "/loadbalancers/" + strconv.Itoa(lbID) + "/accesslist"
-}
-
-func mockListResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc(_rootURL(id), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "accessList": [
-    {
-      "address": "206.160.163.21",
-      "id": 21,
-      "type": "DENY"
-    },
-    {
-      "address": "206.160.163.22",
-      "id": 22,
-      "type": "DENY"
-    },
-    {
-      "address": "206.160.163.23",
-      "id": 23,
-      "type": "DENY"
-    },
-    {
-      "address": "206.160.163.24",
-      "id": 24,
-      "type": "DENY"
-    }
-  ]
-}
-  `)
-	})
-}
-
-func mockCreateResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "accessList": [
-    {
-      "address": "206.160.163.21",
-      "type": "DENY"
-    },
-    {
-      "address": "206.160.165.11",
-      "type": "DENY"
-    }
-  ]
-}
-    `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockDeleteAllResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockBatchDeleteResponse(t *testing.T, lbID int, ids []int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		r.ParseForm()
-
-		for k, v := range ids {
-			fids := r.Form["id"]
-			th.AssertEquals(t, strconv.Itoa(v), fids[k])
-		}
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockDeleteResponse(t *testing.T, lbID, networkID int) {
-	th.Mux.HandleFunc(_rootURL(lbID)+"/"+strconv.Itoa(networkID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/rackspace/lb/v1/acl/requests.go b/rackspace/lb/v1/acl/requests.go
deleted file mode 100644
index d4ce7c0..0000000
--- a/rackspace/lb/v1/acl/requests.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package acl
-
-import (
-	"errors"
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List is the operation responsible for returning a paginated collection of
-// network items that define a load balancer's access list.
-func List(client *gophercloud.ServiceClient, lbID int) pagination.Pager {
-	url := rootURL(client, lbID)
-
-	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
-		return AccessListPage{pagination.SinglePageBase(r)}
-	})
-}
-
-// CreateOptsBuilder is the interface responsible for generating the JSON
-// for a Create operation.
-type CreateOptsBuilder interface {
-	ToAccessListCreateMap() (map[string]interface{}, error)
-}
-
-// CreateOpts is a slice of CreateOpt structs, that allow the user to create
-// multiple nodes in a single operation (one node per CreateOpt).
-type CreateOpts []CreateOpt
-
-// CreateOpt represents the options to create a single node.
-type CreateOpt struct {
-	// Required - the IP address or CIDR for item to add to access list.
-	Address string
-
-	// Required - the type of the node. Either ALLOW or DENY.
-	Type Type
-}
-
-// ToAccessListCreateMap converts a slice of options into a map that can be
-// used for the JSON.
-func (opts CreateOpts) ToAccessListCreateMap() (map[string]interface{}, error) {
-	type itemMap map[string]interface{}
-	items := []itemMap{}
-
-	for k, v := range opts {
-		if v.Address == "" {
-			return itemMap{}, fmt.Errorf("Address is a required attribute, none provided for %d CreateOpt element", k)
-		}
-		if v.Type != ALLOW && v.Type != DENY {
-			return itemMap{}, fmt.Errorf("Type must be ALLOW or DENY")
-		}
-
-		item := make(itemMap)
-		item["address"] = v.Address
-		item["type"] = v.Type
-
-		items = append(items, item)
-	}
-
-	return itemMap{"accessList": items}, nil
-}
-
-// Create is the operation responsible for adding network items to the access
-// rules for a particular load balancer. If network items already exist, the
-// new item will be appended. A single IP address or subnet range is considered
-// unique and cannot be duplicated.
-func Create(client *gophercloud.ServiceClient, loadBalancerID int, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToAccessListCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = client.Post(rootURL(client, loadBalancerID), reqBody, nil, nil)
-	return res
-}
-
-// BulkDelete will delete multiple network items from a load balancer's access
-// list in a single operation.
-func BulkDelete(c *gophercloud.ServiceClient, loadBalancerID int, itemIDs []int) DeleteResult {
-	var res DeleteResult
-
-	if len(itemIDs) > 10 || len(itemIDs) == 0 {
-		res.Err = errors.New("You must provide a minimum of 1 and a maximum of 10 item IDs")
-		return res
-	}
-
-	url := rootURL(c, loadBalancerID)
-	url += gophercloud.IDSliceToQueryString("id", itemIDs)
-
-	_, res.Err = c.Delete(url, nil)
-	return res
-}
-
-// Delete will remove a single network item from a load balancer's access list.
-func Delete(c *gophercloud.ServiceClient, lbID, itemID int) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, lbID, itemID), nil)
-	return res
-}
-
-// DeleteAll will delete the entire contents of a load balancer's access list,
-// effectively resetting it and allowing all traffic.
-func DeleteAll(c *gophercloud.ServiceClient, lbID int) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(rootURL(c, lbID), nil)
-	return res
-}
diff --git a/rackspace/lb/v1/acl/requests_test.go b/rackspace/lb/v1/acl/requests_test.go
deleted file mode 100644
index c4961a3..0000000
--- a/rackspace/lb/v1/acl/requests_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package acl
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const (
-	lbID    = 12345
-	itemID1 = 67890
-	itemID2 = 67891
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListResponse(t, lbID)
-
-	count := 0
-
-	err := List(client.ServiceClient(), lbID).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractAccessList(page)
-		th.AssertNoErr(t, err)
-
-		expected := AccessList{
-			NetworkItem{Address: "206.160.163.21", ID: 21, Type: DENY},
-			NetworkItem{Address: "206.160.163.22", ID: 22, Type: DENY},
-			NetworkItem{Address: "206.160.163.23", ID: 23, Type: DENY},
-			NetworkItem{Address: "206.160.163.24", ID: 24, Type: DENY},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockCreateResponse(t, lbID)
-
-	opts := CreateOpts{
-		CreateOpt{Address: "206.160.163.21", Type: DENY},
-		CreateOpt{Address: "206.160.165.11", Type: DENY},
-	}
-
-	err := Create(client.ServiceClient(), lbID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestBulkDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	ids := []int{itemID1, itemID2}
-
-	mockBatchDeleteResponse(t, lbID, ids)
-
-	err := BulkDelete(client.ServiceClient(), lbID, ids).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteResponse(t, lbID, itemID1)
-
-	err := Delete(client.ServiceClient(), lbID, itemID1).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDeleteAll(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteAllResponse(t, lbID)
-
-	err := DeleteAll(client.ServiceClient(), lbID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/lb/v1/acl/results.go b/rackspace/lb/v1/acl/results.go
deleted file mode 100644
index 9ea5ea2..0000000
--- a/rackspace/lb/v1/acl/results.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package acl
-
-import (
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// AccessList represents the rules of network access to a particular load
-// balancer.
-type AccessList []NetworkItem
-
-// NetworkItem describes how an IP address or entire subnet may interact with a
-// load balancer.
-type NetworkItem struct {
-	// The IP address or subnet (CIDR) that defines the network item.
-	Address string
-
-	// The numeric unique ID for this item.
-	ID int
-
-	// Either ALLOW or DENY.
-	Type Type
-}
-
-// Type defines how an item may connect to the load balancer.
-type Type string
-
-// Convenience consts.
-const (
-	ALLOW Type = "ALLOW"
-	DENY  Type = "DENY"
-)
-
-// AccessListPage is the page returned by a pager for traversing over a
-// collection of network items in an access list.
-type AccessListPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty checks whether an AccessListPage struct is empty.
-func (p AccessListPage) IsEmpty() (bool, error) {
-	is, err := ExtractAccessList(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
-}
-
-// ExtractAccessList accepts a Page struct, specifically an AccessListPage
-// struct, and extracts the elements into a slice of NetworkItem structs. In
-// other words, a generic collection is mapped into a relevant slice.
-func ExtractAccessList(page pagination.Page) (AccessList, error) {
-	var resp struct {
-		List AccessList `mapstructure:"accessList" json:"accessList"`
-	}
-
-	err := mapstructure.Decode(page.(AccessListPage).Body, &resp)
-
-	return resp.List, err
-}
-
-// CreateResult represents the result of a create operation.
-type CreateResult struct {
-	gophercloud.ErrResult
-}
-
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
diff --git a/rackspace/lb/v1/acl/urls.go b/rackspace/lb/v1/acl/urls.go
deleted file mode 100644
index e373fa1..0000000
--- a/rackspace/lb/v1/acl/urls.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package acl
-
-import (
-	"strconv"
-
-	"github.com/rackspace/gophercloud"
-)
-
-const (
-	path    = "loadbalancers"
-	aclPath = "accesslist"
-)
-
-func resourceURL(c *gophercloud.ServiceClient, lbID, networkID int) string {
-	return c.ServiceURL(path, strconv.Itoa(lbID), aclPath, strconv.Itoa(networkID))
-}
-
-func rootURL(c *gophercloud.ServiceClient, lbID int) string {
-	return c.ServiceURL(path, strconv.Itoa(lbID), aclPath)
-}
diff --git a/rackspace/lb/v1/lbs/doc.go b/rackspace/lb/v1/lbs/doc.go
deleted file mode 100644
index 05f0032..0000000
--- a/rackspace/lb/v1/lbs/doc.go
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-Package lbs provides information and interaction with the Load Balancer API
-resource for the Rackspace Cloud Load Balancer service.
-
-A load balancer is a logical device which belongs to a cloud account. It is
-used to distribute workloads between multiple back-end systems or services,
-based on the criteria defined as part of its configuration. This configuration
-is defined using the Create operation, and can be updated with Update.
-
-To conserve IPv4 address space, it is highly recommended that you share Virtual
-IPs between load balancers. If you have at least one load balancer, you may
-create subsequent ones that share a single virtual IPv4 and/or a single IPv6 by
-passing in a virtual IP ID to the Update operation (instead of a type). This
-feature is also highly desirable if you wish to load balance both an insecure
-and secure protocol using one IP or DNS name. In order to share a virtual IP,
-each Load Balancer must utilize a unique port.
-
-All load balancers have a Status attribute that shows the current configuration
-status of the device. This status is immutable by the caller and is updated
-automatically based on state changes within the service. When a load balancer
-is first created, it is placed into a BUILD state while the configuration is
-being generated and applied based on the request. Once the configuration is
-applied and finalized, it is in an ACTIVE status. In the event of a
-configuration change or update, the status of the load balancer changes to
-PENDING_UPDATE to signify configuration changes are in progress but have not yet
-been finalized. Load balancers in a SUSPENDED status are configured to reject
-traffic and do not forward requests to back-end nodes.
-
-An HTTP load balancer has the X-Forwarded-For (XFF) HTTP header set by default.
-This header contains the originating IP address of a client connecting to a web
-server through an HTTP proxy or load balancer, which many web applications are
-already designed to use when determining the source address for a request.
-
-It also includes the X-Forwarded-Proto (XFP) HTTP header, which has been added
-for identifying the originating protocol of an HTTP request as "http" or
-"https" depending on which protocol the client requested. This is useful when
-using SSL termination.
-
-Finally, it also includes the X-Forwarded-Port HTTP header, which has been
-added for being able to generate secure URLs containing the specified port.
-This header, along with the X-Forwarded-For header, provides the needed
-information to the underlying application servers.
-*/
-package lbs
diff --git a/rackspace/lb/v1/lbs/fixtures.go b/rackspace/lb/v1/lbs/fixtures.go
deleted file mode 100644
index a002c67..0000000
--- a/rackspace/lb/v1/lbs/fixtures.go
+++ /dev/null
@@ -1,586 +0,0 @@
-// +build fixtures
-
-package lbs
-
-import (
-	"fmt"
-	"net/http"
-	"strconv"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func mockListLBResponse(t *testing.T) {
-	th.Mux.HandleFunc("/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "loadBalancers":[
-      {
-         "name":"lb-site1",
-         "id":71,
-         "protocol":"HTTP",
-         "port":80,
-         "algorithm":"RANDOM",
-         "status":"ACTIVE",
-         "nodeCount":3,
-         "virtualIps":[
-            {
-               "id":403,
-               "address":"206.55.130.1",
-               "type":"PUBLIC",
-               "ipVersion":"IPV4"
-            }
-         ],
-         "created":{
-            "time":"2010-11-30T03:23:42Z"
-         },
-         "updated":{
-            "time":"2010-11-30T03:23:44Z"
-         }
-      },
-      {
-         "name":"lb-site2",
-         "id":72,
-         "created":{
-            "time":"2011-11-30T03:23:42Z"
-         },
-         "updated":{
-            "time":"2011-11-30T03:23:44Z"
-         }
-      },
-      {
-         "name":"lb-site3",
-         "id":73,
-         "created":{
-            "time":"2012-11-30T03:23:42Z"
-         },
-         "updated":{
-            "time":"2012-11-30T03:23:44Z"
-         }
-      }
-   ]
-}
-			`)
-	})
-}
-
-func mockCreateLBResponse(t *testing.T) {
-	th.Mux.HandleFunc("/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "loadBalancer": {
-    "name": "a-new-loadbalancer",
-    "port": 80,
-    "protocol": "HTTP",
-    "virtualIps": [
-      {
-        "id": 2341
-      },
-      {
-        "id": 900001
-      }
-    ],
-    "nodes": [
-      {
-        "address": "10.1.1.1",
-        "port": 80,
-        "condition": "ENABLED"
-      }
-    ]
-  }
-}
-		`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-
-		fmt.Fprintf(w, `
-{
-  "loadBalancer": {
-    "name": "a-new-loadbalancer",
-    "id": 144,
-    "protocol": "HTTP",
-    "halfClosed": false,
-    "port": 83,
-    "algorithm": "RANDOM",
-    "status": "BUILD",
-    "timeout": 30,
-    "cluster": {
-      "name": "ztm-n01.staging1.lbaas.rackspace.net"
-    },
-    "nodes": [
-      {
-        "address": "10.1.1.1",
-        "id": 653,
-        "port": 80,
-        "status": "ONLINE",
-        "condition": "ENABLED",
-        "weight": 1
-      }
-    ],
-    "virtualIps": [
-      {
-        "address": "206.10.10.210",
-        "id": 39,
-        "type": "PUBLIC",
-        "ipVersion": "IPV4"
-      },
-      {
-        "address": "2001:4801:79f1:0002:711b:be4c:0000:0021",
-        "id": 900001,
-        "type": "PUBLIC",
-        "ipVersion": "IPV6"
-      }
-    ],
-    "created": {
-      "time": "2010-11-30T03:23:42Z"
-    },
-    "updated": {
-      "time": "2010-11-30T03:23:44Z"
-    },
-    "connectionLogging": {
-      "enabled": false
-    }
-  }
-}
-	`)
-	})
-}
-
-func mockBatchDeleteLBResponse(t *testing.T, ids []int) {
-	th.Mux.HandleFunc("/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		r.ParseForm()
-
-		for k, v := range ids {
-			fids := r.Form["id"]
-			th.AssertEquals(t, strconv.Itoa(v), fids[k])
-		}
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockDeleteLBResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockGetLBResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "loadBalancer": {
-    "id": 2000,
-    "name": "sample-loadbalancer",
-    "protocol": "HTTP",
-    "port": 80,
-    "algorithm": "RANDOM",
-    "status": "ACTIVE",
-    "timeout": 30,
-    "connectionLogging": {
-      "enabled": true
-    },
-    "virtualIps": [
-      {
-        "id": 1000,
-        "address": "206.10.10.210",
-        "type": "PUBLIC",
-        "ipVersion": "IPV4"
-      }
-    ],
-    "nodes": [
-      {
-        "id": 1041,
-        "address": "10.1.1.1",
-        "port": 80,
-        "condition": "ENABLED",
-        "status": "ONLINE"
-      },
-      {
-        "id": 1411,
-        "address": "10.1.1.2",
-        "port": 80,
-        "condition": "ENABLED",
-        "status": "ONLINE"
-      }
-    ],
-    "sessionPersistence": {
-      "persistenceType": "HTTP_COOKIE"
-    },
-    "connectionThrottle": {
-      "maxConnections": 100
-    },
-    "cluster": {
-      "name": "c1.dfw1"
-    },
-    "created": {
-      "time": "2010-11-30T03:23:42Z"
-    },
-    "updated": {
-      "time": "2010-11-30T03:23:44Z"
-    },
-    "sourceAddresses": {
-      "ipv6Public": "2001:4801:79f1:1::1/64",
-      "ipv4Servicenet": "10.0.0.0",
-      "ipv4Public": "10.12.99.28"
-    }
-  }
-}
-	`)
-	})
-}
-
-func mockUpdateLBResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-	"loadBalancer": {
-		"name": "a-new-loadbalancer",
-		"protocol": "TCP",
-		"halfClosed": true,
-		"algorithm": "RANDOM",
-		"port": 8080,
-		"timeout": 100,
-		"httpsRedirect": false
-	}
-}
-		`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockListProtocolsResponse(t *testing.T) {
-	th.Mux.HandleFunc("/loadbalancers/protocols", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "protocols": [
-    {
-      "name": "DNS_TCP",
-      "port": 53
-    },
-    {
-      "name": "DNS_UDP",
-      "port": 53
-    },
-    {
-      "name": "FTP",
-      "port": 21
-    },
-    {
-      "name": "HTTP",
-      "port": 80
-    },
-    {
-      "name": "HTTPS",
-      "port": 443
-    },
-    {
-      "name": "IMAPS",
-      "port": 993
-    },
-    {
-      "name": "IMAPv4",
-      "port": 143
-    },
-    {
-      "name": "LDAP",
-      "port": 389
-    },
-    {
-      "name": "LDAPS",
-      "port": 636
-    },
-    {
-      "name": "MYSQL",
-      "port": 3306
-    },
-    {
-      "name": "POP3",
-      "port": 110
-    },
-    {
-      "name": "POP3S",
-      "port": 995
-    },
-    {
-      "name": "SMTP",
-      "port": 25
-    },
-    {
-      "name": "TCP",
-      "port": 0
-    },
-    {
-      "name": "TCP_CLIENT_FIRST",
-      "port": 0
-    },
-    {
-      "name": "UDP",
-      "port": 0
-    },
-    {
-      "name": "UDP_STREAM",
-      "port": 0
-    },
-    {
-      "name": "SFTP",
-      "port": 22
-    },
-    {
-      "name": "TCP_STREAM",
-      "port": 0
-    }
-  ]
-}
-	`)
-	})
-}
-
-func mockListAlgorithmsResponse(t *testing.T) {
-	th.Mux.HandleFunc("/loadbalancers/algorithms", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "algorithms": [
-    {
-      "name": "LEAST_CONNECTIONS"
-    },
-    {
-      "name": "RANDOM"
-    },
-    {
-      "name": "ROUND_ROBIN"
-    },
-    {
-      "name": "WEIGHTED_LEAST_CONNECTIONS"
-    },
-    {
-      "name": "WEIGHTED_ROUND_ROBIN"
-    }
-  ]
-}
-			`)
-	})
-}
-
-func mockGetLoggingResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id)+"/connectionlogging", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "connectionLogging": {
-    "enabled": true
-  }
-}
-			`)
-	})
-}
-
-func mockEnableLoggingResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id)+"/connectionlogging", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-   "connectionLogging":{
-      "enabled":true
-   }
-}
-		`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockDisableLoggingResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id)+"/connectionlogging", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-	"connectionLogging":{
-			"enabled":false
-	}
-}
-		`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockGetErrorPageResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id)+"/errorpage", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "errorpage": {
-    "content": "<html>DEFAULT ERROR PAGE</html>"
-  }
-}
-			`)
-	})
-}
-
-func mockSetErrorPageResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id)+"/errorpage", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-	"errorpage": {
-		"content": "<html>New error page</html>"
-	}
-}
-		`)
-
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-	"errorpage": {
-		"content": "<html>New error page</html>"
-	}
-}
-			`)
-	})
-}
-
-func mockDeleteErrorPageResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id)+"/errorpage", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusOK)
-	})
-}
-
-func mockGetStatsResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id)+"/stats", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "connectTimeOut": 10,
-  "connectError": 20,
-  "connectFailure": 30,
-  "dataTimedOut": 40,
-  "keepAliveTimedOut": 50,
-  "maxConn": 60,
-  "currentConn": 40,
-  "connectTimeOutSsl": 10,
-  "connectErrorSsl": 20,
-  "connectFailureSsl": 30,
-  "dataTimedOutSsl": 40,
-  "keepAliveTimedOutSsl": 50,
-  "maxConnSsl": 60,
-  "currentConnSsl": 40
-}
-			`)
-	})
-}
-
-func mockGetCachingResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id)+"/contentcaching", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "contentCaching": {
-      "enabled": true
-   }
-}
-			`)
-	})
-}
-
-func mockEnableCachingResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id)+"/contentcaching", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-   "contentCaching":{
-      "enabled":true
-   }
-}
-		`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockDisableCachingResponse(t *testing.T, id int) {
-	th.Mux.HandleFunc("/loadbalancers/"+strconv.Itoa(id)+"/contentcaching", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-	"contentCaching":{
-			"enabled":false
-	}
-}
-		`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/rackspace/lb/v1/lbs/requests.go b/rackspace/lb/v1/lbs/requests.go
deleted file mode 100644
index 46f5f02..0000000
--- a/rackspace/lb/v1/lbs/requests.go
+++ /dev/null
@@ -1,497 +0,0 @@
-package lbs
-
-import (
-	"errors"
-
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/acl"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/monitors"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/nodes"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/sessions"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/throttle"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/vips"
-)
-
-var (
-	errNameRequired    = errors.New("Name is a required attribute")
-	errTimeoutExceeded = errors.New("Timeout must be less than 120")
-)
-
-// ListOptsBuilder allows extensions to add additional parameters to the
-// List request.
-type ListOptsBuilder interface {
-	ToLBListQuery() (string, error)
-}
-
-// ListOpts allows the filtering and sorting of paginated collections through
-// the API.
-type ListOpts struct {
-	ChangesSince string `q:"changes-since"`
-	Status       Status `q:"status"`
-	NodeAddr     string `q:"nodeaddress"`
-	Marker       string `q:"marker"`
-	Limit        int    `q:"limit"`
-}
-
-// ToLBListQuery formats a ListOpts into a query string.
-func (opts ListOpts) ToLBListQuery() (string, error) {
-	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
-}
-
-// List is the operation responsible for returning a paginated collection of
-// load balancers. You may pass in a ListOpts struct to filter results.
-func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
-	url := rootURL(client)
-	if opts != nil {
-		query, err := opts.ToLBListQuery()
-		if err != nil {
-			return pagination.Pager{Err: err}
-		}
-		url += query
-	}
-
-	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
-		return LBPage{pagination.LinkedPageBase{PageResult: r}}
-	})
-}
-
-// CreateOptsBuilder is the interface options structs have to satisfy in order
-// to be used in the main Create operation in this package. Since many
-// extensions decorate or modify the common logic, it is useful for them to
-// satisfy a basic interface in order for them to be used.
-type CreateOptsBuilder interface {
-	ToLBCreateMap() (map[string]interface{}, error)
-}
-
-// CreateOpts is the common options struct used in this package's Create
-// operation.
-type CreateOpts struct {
-	// Required - name of the load balancer to create. The name must be 128
-	// characters or fewer in length, and all UTF-8 characters are valid.
-	Name string
-
-	// Optional - nodes to be added.
-	Nodes []nodes.Node
-
-	// Required - protocol of the service that is being load balanced.
-	// See http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/protocols.html
-	// for a full list of supported protocols.
-	Protocol string
-
-	// Optional - enables or disables Half-Closed support for the load balancer.
-	// Half-Closed support provides the ability for one end of the connection to
-	// terminate its output, while still receiving data from the other end. Only
-	// available for TCP/TCP_CLIENT_FIRST protocols.
-	HalfClosed gophercloud.EnabledState
-
-	// Optional - the type of virtual IPs you want associated with the load
-	// balancer.
-	VIPs []vips.VIP
-
-	// Optional - the access list management feature allows fine-grained network
-	// access controls to be applied to the load balancer virtual IP address.
-	AccessList *acl.AccessList
-
-	// Optional - algorithm that defines how traffic should be directed between
-	// back-end nodes.
-	Algorithm string
-
-	// Optional - current connection logging configuration.
-	ConnectionLogging *ConnectionLogging
-
-	// Optional - specifies a limit on the number of connections per IP address
-	// to help mitigate malicious or abusive traffic to your applications.
-	ConnThrottle *throttle.ConnectionThrottle
-
-	// Optional - the type of health monitor check to perform to ensure that the
-	// service is performing properly.
-	HealthMonitor *monitors.Monitor
-
-	// Optional - arbitrary information that can be associated with each LB.
-	Metadata map[string]interface{}
-
-	// Optional - port number for the service you are load balancing.
-	Port int
-
-	// Optional - the timeout value for the load balancer and communications with
-	// its nodes. Defaults to 30 seconds with a maximum of 120 seconds.
-	Timeout int
-
-	// Optional - specifies whether multiple requests from clients are directed
-	// to the same node.
-	SessionPersistence *sessions.SessionPersistence
-
-	// Optional - enables or disables HTTP to HTTPS redirection for the load
-	// balancer. When enabled, any HTTP request returns status code 301 (Moved
-	// Permanently), and the requester is redirected to the requested URL via the
-	// HTTPS protocol on port 443. For example, http://example.com/page.html
-	// would be redirected to https://example.com/page.html. Only available for
-	// HTTPS protocol (port=443), or HTTP protocol with a properly configured SSL
-	// termination (secureTrafficOnly=true, securePort=443).
-	HTTPSRedirect gophercloud.EnabledState
-}
-
-// ToLBCreateMap casts a CreateOpts struct to a map.
-func (opts CreateOpts) ToLBCreateMap() (map[string]interface{}, error) {
-	lb := make(map[string]interface{})
-
-	if opts.Name == "" {
-		return lb, errNameRequired
-	}
-	if opts.Timeout > 120 {
-		return lb, errTimeoutExceeded
-	}
-
-	lb["name"] = opts.Name
-
-	if len(opts.Nodes) > 0 {
-		nodes := []map[string]interface{}{}
-		for _, n := range opts.Nodes {
-			nodes = append(nodes, map[string]interface{}{
-				"address":   n.Address,
-				"port":      n.Port,
-				"condition": n.Condition,
-			})
-		}
-		lb["nodes"] = nodes
-	}
-
-	if opts.Protocol != "" {
-		lb["protocol"] = opts.Protocol
-	}
-	if opts.HalfClosed != nil {
-		lb["halfClosed"] = opts.HalfClosed
-	}
-	if len(opts.VIPs) > 0 {
-		lb["virtualIps"] = opts.VIPs
-	}
-	if opts.AccessList != nil {
-		lb["accessList"] = &opts.AccessList
-	}
-	if opts.Algorithm != "" {
-		lb["algorithm"] = opts.Algorithm
-	}
-	if opts.ConnectionLogging != nil {
-		lb["connectionLogging"] = &opts.ConnectionLogging
-	}
-	if opts.ConnThrottle != nil {
-		lb["connectionThrottle"] = &opts.ConnThrottle
-	}
-	if opts.HealthMonitor != nil {
-		lb["healthMonitor"] = &opts.HealthMonitor
-	}
-	if len(opts.Metadata) != 0 {
-		lb["metadata"] = opts.Metadata
-	}
-	if opts.Port > 0 {
-		lb["port"] = opts.Port
-	}
-	if opts.Timeout > 0 {
-		lb["timeout"] = opts.Timeout
-	}
-	if opts.SessionPersistence != nil {
-		lb["sessionPersistence"] = &opts.SessionPersistence
-	}
-	if opts.HTTPSRedirect != nil {
-		lb["httpsRedirect"] = &opts.HTTPSRedirect
-	}
-
-	return map[string]interface{}{"loadBalancer": lb}, nil
-}
-
-// Create is the operation responsible for asynchronously provisioning a new
-// load balancer based on the configuration defined in CreateOpts. Once the
-// request is validated and progress has started on the provisioning process, a
-// response struct is returned. When extracted (with Extract()), you have
-// to the load balancer's unique ID and status.
-//
-// Once an ID is attained, you can check on the progress of the operation by
-// calling Get and passing in the ID. If the corresponding request cannot be
-// fulfilled due to insufficient or invalid data, an HTTP 400 (Bad Request)
-// error response is returned with information regarding the nature of the
-// failure in the body of the response. Failures in the validation process are
-// non-recoverable and require the caller to correct the cause of the failure.
-func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToLBCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
-	return res
-}
-
-// Get is the operation responsible for providing detailed information
-// regarding a specific load balancer which is configured and associated with
-// your account. This operation is not capable of returning details for a load
-// balancer which has been deleted.
-func Get(c *gophercloud.ServiceClient, id int) GetResult {
-	var res GetResult
-
-	_, res.Err = c.Get(resourceURL(c, id), &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-
-	return res
-}
-
-// BulkDelete removes all the load balancers referenced in the slice of IDs.
-// Any and all configuration data associated with these load balancers is
-// immediately purged and is not recoverable.
-//
-// If one of the items in the list cannot be removed due to its current status,
-// a 400 Bad Request error is returned along with the IDs of the ones the
-// system identified as potential failures for this request.
-func BulkDelete(c *gophercloud.ServiceClient, ids []int) DeleteResult {
-	var res DeleteResult
-
-	if len(ids) > 10 || len(ids) == 0 {
-		res.Err = errors.New("You must provide a minimum of 1 and a maximum of 10 LB IDs")
-		return res
-	}
-
-	url := rootURL(c)
-	url += gophercloud.IDSliceToQueryString("id", ids)
-
-	_, res.Err = c.Delete(url, nil)
-	return res
-}
-
-// Delete removes a single load balancer.
-func Delete(c *gophercloud.ServiceClient, id int) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, id), nil)
-	return res
-}
-
-// UpdateOptsBuilder represents a type that can be converted into a JSON-like
-// map structure.
-type UpdateOptsBuilder interface {
-	ToLBUpdateMap() (map[string]interface{}, error)
-}
-
-// UpdateOpts represents the options for updating an existing load balancer.
-type UpdateOpts struct {
-	// Optional - new name of the load balancer.
-	Name string
-
-	// Optional - the new protocol you want your load balancer to have.
-	// See http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/protocols.html
-	// for a full list of supported protocols.
-	Protocol string
-
-	// Optional - see the HalfClosed field in CreateOpts for more information.
-	HalfClosed gophercloud.EnabledState
-
-	// Optional - see the Algorithm field in CreateOpts for more information.
-	Algorithm string
-
-	// Optional - see the Port field in CreateOpts for more information.
-	Port int
-
-	// Optional - see the Timeout field in CreateOpts for more information.
-	Timeout int
-
-	// Optional - see the HTTPSRedirect field in CreateOpts for more information.
-	HTTPSRedirect gophercloud.EnabledState
-}
-
-// ToLBUpdateMap casts an UpdateOpts struct to a map.
-func (opts UpdateOpts) ToLBUpdateMap() (map[string]interface{}, error) {
-	lb := make(map[string]interface{})
-
-	if opts.Name != "" {
-		lb["name"] = opts.Name
-	}
-	if opts.Protocol != "" {
-		lb["protocol"] = opts.Protocol
-	}
-	if opts.HalfClosed != nil {
-		lb["halfClosed"] = opts.HalfClosed
-	}
-	if opts.Algorithm != "" {
-		lb["algorithm"] = opts.Algorithm
-	}
-	if opts.Port > 0 {
-		lb["port"] = opts.Port
-	}
-	if opts.Timeout > 0 {
-		lb["timeout"] = opts.Timeout
-	}
-	if opts.HTTPSRedirect != nil {
-		lb["httpsRedirect"] = &opts.HTTPSRedirect
-	}
-
-	return map[string]interface{}{"loadBalancer": lb}, nil
-}
-
-// Update is the operation responsible for asynchronously updating the
-// attributes of a specific load balancer. Upon successful validation of the
-// request, the service returns a 202 Accepted response, and the load balancer
-// enters a PENDING_UPDATE state. A user can poll the load balancer with Get to
-// wait for the changes to be applied. When this happens, the load balancer will
-// return to an ACTIVE state.
-func Update(c *gophercloud.ServiceClient, id int, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToLBUpdateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Put(resourceURL(c, id), reqBody, nil, nil)
-	return res
-}
-
-// ListProtocols is the operation responsible for returning a paginated
-// collection of load balancer protocols.
-func ListProtocols(client *gophercloud.ServiceClient) pagination.Pager {
-	url := protocolsURL(client)
-	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
-		return ProtocolPage{pagination.SinglePageBase(r)}
-	})
-}
-
-// ListAlgorithms is the operation responsible for returning a paginated
-// collection of load balancer algorithms.
-func ListAlgorithms(client *gophercloud.ServiceClient) pagination.Pager {
-	url := algorithmsURL(client)
-	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
-		return AlgorithmPage{pagination.SinglePageBase(r)}
-	})
-}
-
-// IsLoggingEnabled returns true if the load balancer has connection logging
-// enabled and false if not.
-func IsLoggingEnabled(client *gophercloud.ServiceClient, id int) (bool, error) {
-	var body interface{}
-
-	_, err := client.Get(loggingURL(client, id), &body, nil)
-	if err != nil {
-		return false, err
-	}
-
-	var resp struct {
-		CL struct {
-			Enabled bool `mapstructure:"enabled"`
-		} `mapstructure:"connectionLogging"`
-	}
-
-	err = mapstructure.Decode(body, &resp)
-	return resp.CL.Enabled, err
-}
-
-func toConnLoggingMap(state bool) map[string]map[string]bool {
-	return map[string]map[string]bool{
-		"connectionLogging": map[string]bool{"enabled": state},
-	}
-}
-
-// EnableLogging will enable connection logging for a specified load balancer.
-func EnableLogging(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult {
-	var res gophercloud.ErrResult
-	_, res.Err = client.Put(loggingURL(client, id), toConnLoggingMap(true), nil, nil)
-	return res
-}
-
-// DisableLogging will disable connection logging for a specified load balancer.
-func DisableLogging(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult {
-	var res gophercloud.ErrResult
-	_, res.Err = client.Put(loggingURL(client, id), toConnLoggingMap(false), nil, nil)
-	return res
-}
-
-// GetErrorPage will retrieve the current error page for the load balancer.
-func GetErrorPage(client *gophercloud.ServiceClient, id int) ErrorPageResult {
-	var res ErrorPageResult
-	_, res.Err = client.Get(errorPageURL(client, id), &res.Body, nil)
-	return res
-}
-
-// SetErrorPage will set the HTML of the load balancer's error page to a
-// specific value.
-func SetErrorPage(client *gophercloud.ServiceClient, id int, html string) ErrorPageResult {
-	var res ErrorPageResult
-
-	type stringMap map[string]string
-	reqBody := map[string]stringMap{"errorpage": stringMap{"content": html}}
-
-	_, res.Err = client.Put(errorPageURL(client, id), reqBody, &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-
-	return res
-}
-
-// DeleteErrorPage will delete the current error page for the load balancer.
-func DeleteErrorPage(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult {
-	var res gophercloud.ErrResult
-	_, res.Err = client.Delete(errorPageURL(client, id), &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-	return res
-}
-
-// GetStats will retrieve detailed stats related to the load balancer's usage.
-func GetStats(client *gophercloud.ServiceClient, id int) StatsResult {
-	var res StatsResult
-	_, res.Err = client.Get(statsURL(client, id), &res.Body, nil)
-	return res
-}
-
-// IsContentCached will check to see whether the specified load balancer caches
-// content. When content caching is enabled, recently-accessed files are stored
-// on the load balancer for easy retrieval by web clients. Content caching
-// improves the performance of high traffic web sites by temporarily storing
-// data that was recently accessed. While it's cached, requests for that data
-// are served by the load balancer, which in turn reduces load off the back-end
-// nodes. The result is improved response times for those requests and less
-// load on the web server.
-func IsContentCached(client *gophercloud.ServiceClient, id int) (bool, error) {
-	var body interface{}
-
-	_, err := client.Get(cacheURL(client, id), &body, nil)
-	if err != nil {
-		return false, err
-	}
-
-	var resp struct {
-		CC struct {
-			Enabled bool `mapstructure:"enabled"`
-		} `mapstructure:"contentCaching"`
-	}
-
-	err = mapstructure.Decode(body, &resp)
-	return resp.CC.Enabled, err
-}
-
-func toCachingMap(state bool) map[string]map[string]bool {
-	return map[string]map[string]bool{
-		"contentCaching": map[string]bool{"enabled": state},
-	}
-}
-
-// EnableCaching will enable content-caching for the specified load balancer.
-func EnableCaching(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult {
-	var res gophercloud.ErrResult
-	_, res.Err = client.Put(cacheURL(client, id), toCachingMap(true), nil, nil)
-	return res
-}
-
-// DisableCaching will disable content-caching for the specified load balancer.
-func DisableCaching(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult {
-	var res gophercloud.ErrResult
-	_, res.Err = client.Put(cacheURL(client, id), toCachingMap(false), nil, nil)
-	return res
-}
diff --git a/rackspace/lb/v1/lbs/requests_test.go b/rackspace/lb/v1/lbs/requests_test.go
deleted file mode 100644
index a8ec19e..0000000
--- a/rackspace/lb/v1/lbs/requests_test.go
+++ /dev/null
@@ -1,438 +0,0 @@
-package lbs
-
-import (
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/nodes"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/sessions"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/throttle"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/vips"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const (
-	id1 = 12345
-	id2 = 67890
-	ts1 = "2010-11-30T03:23:42Z"
-	ts2 = "2010-11-30T03:23:44Z"
-)
-
-func toTime(t *testing.T, str string) time.Time {
-	ts, err := time.Parse(time.RFC3339, str)
-	if err != nil {
-		t.Fatalf("Could not parse time: %s", err.Error())
-	}
-	return ts
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListLBResponse(t)
-
-	count := 0
-
-	err := List(client.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractLBs(page)
-		th.AssertNoErr(t, err)
-
-		expected := []LoadBalancer{
-			LoadBalancer{
-				Name:      "lb-site1",
-				ID:        71,
-				Protocol:  "HTTP",
-				Port:      80,
-				Algorithm: "RANDOM",
-				Status:    ACTIVE,
-				NodeCount: 3,
-				VIPs: []vips.VIP{
-					vips.VIP{
-						ID:      403,
-						Address: "206.55.130.1",
-						Type:    "PUBLIC",
-						Version: "IPV4",
-					},
-				},
-				Created: Datetime{Time: toTime(t, ts1)},
-				Updated: Datetime{Time: toTime(t, ts2)},
-			},
-			LoadBalancer{
-				ID:      72,
-				Name:    "lb-site2",
-				Created: Datetime{Time: toTime(t, "2011-11-30T03:23:42Z")},
-				Updated: Datetime{Time: toTime(t, "2011-11-30T03:23:44Z")},
-			},
-			LoadBalancer{
-				ID:      73,
-				Name:    "lb-site3",
-				Created: Datetime{Time: toTime(t, "2012-11-30T03:23:42Z")},
-				Updated: Datetime{Time: toTime(t, "2012-11-30T03:23:44Z")},
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockCreateLBResponse(t)
-
-	opts := CreateOpts{
-		Name:     "a-new-loadbalancer",
-		Port:     80,
-		Protocol: "HTTP",
-		VIPs: []vips.VIP{
-			vips.VIP{ID: 2341},
-			vips.VIP{ID: 900001},
-		},
-		Nodes: []nodes.Node{
-			nodes.Node{Address: "10.1.1.1", Port: 80, Condition: "ENABLED"},
-		},
-	}
-
-	lb, err := Create(client.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &LoadBalancer{
-		Name:       "a-new-loadbalancer",
-		ID:         144,
-		Protocol:   "HTTP",
-		HalfClosed: false,
-		Port:       83,
-		Algorithm:  "RANDOM",
-		Status:     BUILD,
-		Timeout:    30,
-		Cluster:    Cluster{Name: "ztm-n01.staging1.lbaas.rackspace.net"},
-		Nodes: []nodes.Node{
-			nodes.Node{
-				Address:   "10.1.1.1",
-				ID:        653,
-				Port:      80,
-				Status:    "ONLINE",
-				Condition: "ENABLED",
-				Weight:    1,
-			},
-		},
-		VIPs: []vips.VIP{
-			vips.VIP{
-				ID:      39,
-				Address: "206.10.10.210",
-				Type:    vips.PUBLIC,
-				Version: vips.IPV4,
-			},
-			vips.VIP{
-				ID:      900001,
-				Address: "2001:4801:79f1:0002:711b:be4c:0000:0021",
-				Type:    vips.PUBLIC,
-				Version: vips.IPV6,
-			},
-		},
-		Created:           Datetime{Time: toTime(t, ts1)},
-		Updated:           Datetime{Time: toTime(t, ts2)},
-		ConnectionLogging: ConnectionLogging{Enabled: false},
-	}
-
-	th.AssertDeepEquals(t, expected, lb)
-}
-
-func TestBulkDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	ids := []int{id1, id2}
-
-	mockBatchDeleteLBResponse(t, ids)
-
-	err := BulkDelete(client.ServiceClient(), ids).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteLBResponse(t, id1)
-
-	err := Delete(client.ServiceClient(), id1).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetLBResponse(t, id1)
-
-	lb, err := Get(client.ServiceClient(), id1).Extract()
-
-	expected := &LoadBalancer{
-		Name:              "sample-loadbalancer",
-		ID:                2000,
-		Protocol:          "HTTP",
-		Port:              80,
-		Algorithm:         "RANDOM",
-		Status:            ACTIVE,
-		Timeout:           30,
-		ConnectionLogging: ConnectionLogging{Enabled: true},
-		VIPs: []vips.VIP{
-			vips.VIP{
-				ID:      1000,
-				Address: "206.10.10.210",
-				Type:    "PUBLIC",
-				Version: "IPV4",
-			},
-		},
-		Nodes: []nodes.Node{
-			nodes.Node{
-				Address:   "10.1.1.1",
-				ID:        1041,
-				Port:      80,
-				Status:    "ONLINE",
-				Condition: "ENABLED",
-			},
-			nodes.Node{
-				Address:   "10.1.1.2",
-				ID:        1411,
-				Port:      80,
-				Status:    "ONLINE",
-				Condition: "ENABLED",
-			},
-		},
-		SessionPersistence: sessions.SessionPersistence{Type: "HTTP_COOKIE"},
-		ConnectionThrottle: throttle.ConnectionThrottle{MaxConnections: 100},
-		Cluster:            Cluster{Name: "c1.dfw1"},
-		Created:            Datetime{Time: toTime(t, ts1)},
-		Updated:            Datetime{Time: toTime(t, ts2)},
-		SourceAddrs: SourceAddrs{
-			IPv4Public:  "10.12.99.28",
-			IPv4Private: "10.0.0.0",
-			IPv6Public:  "2001:4801:79f1:1::1/64",
-		},
-	}
-
-	th.AssertDeepEquals(t, expected, lb)
-	th.AssertNoErr(t, err)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockUpdateLBResponse(t, id1)
-
-	opts := UpdateOpts{
-		Name:          "a-new-loadbalancer",
-		Protocol:      "TCP",
-		HalfClosed:    gophercloud.Enabled,
-		Algorithm:     "RANDOM",
-		Port:          8080,
-		Timeout:       100,
-		HTTPSRedirect: gophercloud.Disabled,
-	}
-
-	err := Update(client.ServiceClient(), id1, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestListProtocols(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListProtocolsResponse(t)
-
-	count := 0
-
-	err := ListProtocols(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractProtocols(page)
-		th.AssertNoErr(t, err)
-
-		expected := []Protocol{
-			Protocol{Name: "DNS_TCP", Port: 53},
-			Protocol{Name: "DNS_UDP", Port: 53},
-			Protocol{Name: "FTP", Port: 21},
-			Protocol{Name: "HTTP", Port: 80},
-			Protocol{Name: "HTTPS", Port: 443},
-			Protocol{Name: "IMAPS", Port: 993},
-			Protocol{Name: "IMAPv4", Port: 143},
-		}
-
-		th.CheckDeepEquals(t, expected[0:7], actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestListAlgorithms(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListAlgorithmsResponse(t)
-
-	count := 0
-
-	err := ListAlgorithms(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractAlgorithms(page)
-		th.AssertNoErr(t, err)
-
-		expected := []Algorithm{
-			Algorithm{Name: "LEAST_CONNECTIONS"},
-			Algorithm{Name: "RANDOM"},
-			Algorithm{Name: "ROUND_ROBIN"},
-			Algorithm{Name: "WEIGHTED_LEAST_CONNECTIONS"},
-			Algorithm{Name: "WEIGHTED_ROUND_ROBIN"},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestIsLoggingEnabled(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetLoggingResponse(t, id1)
-
-	res, err := IsLoggingEnabled(client.ServiceClient(), id1)
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, true, res)
-}
-
-func TestEnablingLogging(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockEnableLoggingResponse(t, id1)
-
-	err := EnableLogging(client.ServiceClient(), id1).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDisablingLogging(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDisableLoggingResponse(t, id1)
-
-	err := DisableLogging(client.ServiceClient(), id1).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestGetErrorPage(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetErrorPageResponse(t, id1)
-
-	content, err := GetErrorPage(client.ServiceClient(), id1).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &ErrorPage{Content: "<html>DEFAULT ERROR PAGE</html>"}
-	th.AssertDeepEquals(t, expected, content)
-}
-
-func TestSetErrorPage(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockSetErrorPageResponse(t, id1)
-
-	html := "<html>New error page</html>"
-	content, err := SetErrorPage(client.ServiceClient(), id1, html).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &ErrorPage{Content: html}
-	th.AssertDeepEquals(t, expected, content)
-}
-
-func TestDeleteErrorPage(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteErrorPageResponse(t, id1)
-
-	err := DeleteErrorPage(client.ServiceClient(), id1).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestGetStats(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetStatsResponse(t, id1)
-
-	content, err := GetStats(client.ServiceClient(), id1).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Stats{
-		ConnectTimeout:        10,
-		ConnectError:          20,
-		ConnectFailure:        30,
-		DataTimedOut:          40,
-		KeepAliveTimedOut:     50,
-		MaxConnections:        60,
-		CurrentConnections:    40,
-		SSLConnectTimeout:     10,
-		SSLConnectError:       20,
-		SSLConnectFailure:     30,
-		SSLDataTimedOut:       40,
-		SSLKeepAliveTimedOut:  50,
-		SSLMaxConnections:     60,
-		SSLCurrentConnections: 40,
-	}
-	th.AssertDeepEquals(t, expected, content)
-}
-
-func TestIsCached(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetCachingResponse(t, id1)
-
-	res, err := IsContentCached(client.ServiceClient(), id1)
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, true, res)
-}
-
-func TestEnablingCaching(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockEnableCachingResponse(t, id1)
-
-	err := EnableCaching(client.ServiceClient(), id1).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDisablingCaching(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDisableCachingResponse(t, id1)
-
-	err := DisableCaching(client.ServiceClient(), id1).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/lb/v1/lbs/results.go b/rackspace/lb/v1/lbs/results.go
deleted file mode 100644
index 98f3962..0000000
--- a/rackspace/lb/v1/lbs/results.go
+++ /dev/null
@@ -1,420 +0,0 @@
-package lbs
-
-import (
-	"reflect"
-	"time"
-
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/acl"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/nodes"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/sessions"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/throttle"
-	"github.com/rackspace/gophercloud/rackspace/lb/v1/vips"
-)
-
-// Protocol represents the network protocol which the load balancer accepts.
-type Protocol struct {
-	// The name of the protocol, e.g. HTTP, LDAP, FTP, etc.
-	Name string
-
-	// The port number for the protocol.
-	Port int
-}
-
-// Algorithm defines how traffic should be directed between back-end nodes.
-type Algorithm struct {
-	// The name of the algorithm, e.g RANDOM, ROUND_ROBIN, etc.
-	Name string
-}
-
-// Status represents the potential state of a load balancer resource.
-type Status string
-
-const (
-	// ACTIVE indicates that the LB is configured properly and ready to serve
-	// traffic to incoming requests via the configured virtual IPs.
-	ACTIVE Status = "ACTIVE"
-
-	// BUILD indicates that the LB is being provisioned for the first time and
-	// configuration is being applied to bring the service online. The service
-	// cannot yet serve incoming requests.
-	BUILD Status = "BUILD"
-
-	// PENDINGUPDATE indicates that the LB is online but configuration changes
-	// are being applied to update the service based on a previous request.
-	PENDINGUPDATE Status = "PENDING_UPDATE"
-
-	// PENDINGDELETE indicates that the LB is online but configuration changes
-	// are being applied to begin deletion of the service based on a previous
-	// request.
-	PENDINGDELETE Status = "PENDING_DELETE"
-
-	// SUSPENDED indicates that the LB has been taken offline and disabled.
-	SUSPENDED Status = "SUSPENDED"
-
-	// ERROR indicates that the system encountered an error when attempting to
-	// configure the load balancer.
-	ERROR Status = "ERROR"
-
-	// DELETED indicates that the LB has been deleted.
-	DELETED Status = "DELETED"
-)
-
-// Datetime represents the structure of a Created or Updated field.
-type Datetime struct {
-	Time time.Time `mapstructure:"-"`
-}
-
-// LoadBalancer represents a load balancer API resource.
-type LoadBalancer struct {
-	// Human-readable name for the load balancer.
-	Name string
-
-	// The unique ID for the load balancer.
-	ID int
-
-	// Represents the service protocol being load balanced. See Protocol type for
-	// a list of accepted values.
-	// See http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/protocols.html
-	// for a full list of supported protocols.
-	Protocol string
-
-	// Defines how traffic should be directed between back-end nodes. The default
-	// algorithm is RANDOM. See Algorithm type for a list of accepted values.
-	Algorithm string
-
-	// The current status of the load balancer.
-	Status Status
-
-	// The number of load balancer nodes.
-	NodeCount int `mapstructure:"nodeCount"`
-
-	// Slice of virtual IPs associated with this load balancer.
-	VIPs []vips.VIP `mapstructure:"virtualIps"`
-
-	// Datetime when the LB was created.
-	Created Datetime
-
-	// Datetime when the LB was created.
-	Updated Datetime
-
-	// Port number for the service you are load balancing.
-	Port int
-
-	// HalfClosed provides the ability for one end of the connection to
-	// terminate its output while still receiving data from the other end. This
-	// is only available on TCP/TCP_CLIENT_FIRST protocols.
-	HalfClosed bool
-
-	// Timeout represents the timeout value between a load balancer and its
-	// nodes. Defaults to 30 seconds with a maximum of 120 seconds.
-	Timeout int
-
-	// The cluster name.
-	Cluster Cluster
-
-	// Nodes shows all the back-end nodes which are associated with the load
-	// balancer. These are the devices which are delivered traffic.
-	Nodes []nodes.Node
-
-	// Current connection logging configuration.
-	ConnectionLogging ConnectionLogging
-
-	// SessionPersistence specifies whether multiple requests from clients are
-	// directed to the same node.
-	SessionPersistence sessions.SessionPersistence
-
-	// ConnectionThrottle specifies a limit on the number of connections per IP
-	// address to help mitigate malicious or abusive traffic to your applications.
-	ConnectionThrottle throttle.ConnectionThrottle
-
-	// The source public and private IP addresses.
-	SourceAddrs SourceAddrs `mapstructure:"sourceAddresses"`
-
-	// Represents the access rules for this particular load balancer. IP addresses
-	// or subnet ranges, depending on their type (ALLOW or DENY), can be permitted
-	// or blocked.
-	AccessList acl.AccessList
-}
-
-// SourceAddrs represents the source public and private IP addresses.
-type SourceAddrs struct {
-	IPv4Public  string `json:"ipv4Public" mapstructure:"ipv4Public"`
-	IPv4Private string `json:"ipv4Servicenet" mapstructure:"ipv4Servicenet"`
-	IPv6Public  string `json:"ipv6Public" mapstructure:"ipv6Public"`
-	IPv6Private string `json:"ipv6Servicenet" mapstructure:"ipv6Servicenet"`
-}
-
-// ConnectionLogging - temp
-type ConnectionLogging struct {
-	Enabled bool
-}
-
-// Cluster - temp
-type Cluster struct {
-	Name string
-}
-
-// LBPage is the page returned by a pager when traversing over a collection of
-// LBs.
-type LBPage struct {
-	pagination.LinkedPageBase
-}
-
-// IsEmpty checks whether a NetworkPage struct is empty.
-func (p LBPage) IsEmpty() (bool, error) {
-	is, err := ExtractLBs(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
-}
-
-// ExtractLBs accepts a Page struct, specifically a LBPage struct, and extracts
-// the elements into a slice of LoadBalancer structs. In other words, a generic
-// collection is mapped into a relevant slice.
-func ExtractLBs(page pagination.Page) ([]LoadBalancer, error) {
-	var resp struct {
-		LBs []LoadBalancer `mapstructure:"loadBalancers" json:"loadBalancers"`
-	}
-
-	coll := page.(LBPage).Body
-	err := mapstructure.Decode(coll, &resp)
-
-	s := reflect.ValueOf(coll.(map[string]interface{})["loadBalancers"])
-
-	for i := 0; i < s.Len(); i++ {
-		val := (s.Index(i).Interface()).(map[string]interface{})
-
-		ts, err := extractTS(val, "created")
-		if err != nil {
-			return resp.LBs, err
-		}
-		resp.LBs[i].Created.Time = ts
-
-		ts, err = extractTS(val, "updated")
-		if err != nil {
-			return resp.LBs, err
-		}
-		resp.LBs[i].Updated.Time = ts
-	}
-
-	return resp.LBs, err
-}
-
-func extractTS(body map[string]interface{}, key string) (time.Time, error) {
-	val := body[key].(map[string]interface{})
-	return time.Parse(time.RFC3339, val["time"].(string))
-}
-
-type commonResult struct {
-	gophercloud.Result
-}
-
-// Extract interprets any commonResult as a LB, if possible.
-func (r commonResult) Extract() (*LoadBalancer, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		LB LoadBalancer `mapstructure:"loadBalancer"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	json := r.Body.(map[string]interface{})
-	lb := json["loadBalancer"].(map[string]interface{})
-
-	ts, err := extractTS(lb, "created")
-	if err != nil {
-		return nil, err
-	}
-	response.LB.Created.Time = ts
-
-	ts, err = extractTS(lb, "updated")
-	if err != nil {
-		return nil, err
-	}
-	response.LB.Updated.Time = ts
-
-	return &response.LB, err
-}
-
-// CreateResult represents the result of a create operation.
-type CreateResult struct {
-	commonResult
-}
-
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-// UpdateResult represents the result of an update operation.
-type UpdateResult struct {
-	gophercloud.ErrResult
-}
-
-// GetResult represents the result of a get operation.
-type GetResult struct {
-	commonResult
-}
-
-// ProtocolPage is the page returned by a pager when traversing over a
-// collection of LB protocols.
-type ProtocolPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty checks whether a ProtocolPage struct is empty.
-func (p ProtocolPage) IsEmpty() (bool, error) {
-	is, err := ExtractProtocols(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
-}
-
-// ExtractProtocols accepts a Page struct, specifically a ProtocolPage struct,
-// and extracts the elements into a slice of Protocol structs. In other
-// words, a generic collection is mapped into a relevant slice.
-func ExtractProtocols(page pagination.Page) ([]Protocol, error) {
-	var resp struct {
-		Protocols []Protocol `mapstructure:"protocols" json:"protocols"`
-	}
-	err := mapstructure.Decode(page.(ProtocolPage).Body, &resp)
-	return resp.Protocols, err
-}
-
-// AlgorithmPage is the page returned by a pager when traversing over a
-// collection of LB algorithms.
-type AlgorithmPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty checks whether an AlgorithmPage struct is empty.
-func (p AlgorithmPage) IsEmpty() (bool, error) {
-	is, err := ExtractAlgorithms(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
-}
-
-// ExtractAlgorithms accepts a Page struct, specifically an AlgorithmPage struct,
-// and extracts the elements into a slice of Algorithm structs. In other
-// words, a generic collection is mapped into a relevant slice.
-func ExtractAlgorithms(page pagination.Page) ([]Algorithm, error) {
-	var resp struct {
-		Algorithms []Algorithm `mapstructure:"algorithms" json:"algorithms"`
-	}
-	err := mapstructure.Decode(page.(AlgorithmPage).Body, &resp)
-	return resp.Algorithms, err
-}
-
-// ErrorPage represents the HTML file that is shown to an end user who is
-// attempting to access a load balancer node that is offline/unavailable.
-//
-// During provisioning, every load balancer is configured with a default error
-// page that gets displayed when traffic is requested for an offline node.
-//
-// You can add a single custom error page with an HTTP-based protocol to a load
-// balancer. Page updates override existing content. If a custom error page is
-// deleted, or the load balancer is changed to a non-HTTP protocol, the default
-// error page is restored.
-type ErrorPage struct {
-	Content string
-}
-
-// ErrorPageResult represents the result of an error page operation -
-// specifically getting or creating one.
-type ErrorPageResult struct {
-	gophercloud.Result
-}
-
-// Extract interprets any commonResult as an ErrorPage, if possible.
-func (r ErrorPageResult) Extract() (*ErrorPage, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		ErrorPage ErrorPage `mapstructure:"errorpage"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.ErrorPage, err
-}
-
-// Stats represents all the key information about a load balancer's usage.
-type Stats struct {
-	// The number of connections closed by this load balancer because its
-	// ConnectTimeout interval was exceeded.
-	ConnectTimeout int `mapstructure:"connectTimeOut"`
-
-	// The number of transaction or protocol errors for this load balancer.
-	ConnectError int
-
-	// Number of connection failures for this load balancer.
-	ConnectFailure int
-
-	// Number of connections closed by this load balancer because its Timeout
-	// interval was exceeded.
-	DataTimedOut int
-
-	// Number of connections closed by this load balancer because the
-	// 'keepalive_timeout' interval was exceeded.
-	KeepAliveTimedOut int
-
-	// The maximum number of simultaneous TCP connections this load balancer has
-	// processed at any one time.
-	MaxConnections int `mapstructure:"maxConn"`
-
-	// Number of simultaneous connections active at the time of the request.
-	CurrentConnections int `mapstructure:"currentConn"`
-
-	// Number of SSL connections closed by this load balancer because the
-	// ConnectTimeout interval was exceeded.
-	SSLConnectTimeout int `mapstructure:"connectTimeOutSsl"`
-
-	// Number of SSL transaction or protocol erros in this load balancer.
-	SSLConnectError int `mapstructure:"connectErrorSsl"`
-
-	// Number of SSL connection failures in this load balancer.
-	SSLConnectFailure int `mapstructure:"connectFailureSsl"`
-
-	// Number of SSL connections closed by this load balancer because the
-	// Timeout interval was exceeded.
-	SSLDataTimedOut int `mapstructure:"dataTimedOutSsl"`
-
-	// Number of SSL connections closed by this load balancer because the
-	// 'keepalive_timeout' interval was exceeded.
-	SSLKeepAliveTimedOut int `mapstructure:"keepAliveTimedOutSsl"`
-
-	// Maximum number of simultaneous SSL connections this load balancer has
-	// processed at any one time.
-	SSLMaxConnections int `mapstructure:"maxConnSsl"`
-
-	// Number of simultaneous SSL connections active at the time of the request.
-	SSLCurrentConnections int `mapstructure:"currentConnSsl"`
-}
-
-// StatsResult represents the result of a Stats operation.
-type StatsResult struct {
-	gophercloud.Result
-}
-
-// Extract interprets any commonResult as a Stats struct, if possible.
-func (r StatsResult) Extract() (*Stats, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-	res := &Stats{}
-	err := mapstructure.Decode(r.Body, res)
-	return res, err
-}
diff --git a/rackspace/lb/v1/lbs/urls.go b/rackspace/lb/v1/lbs/urls.go
deleted file mode 100644
index 471a86b..0000000
--- a/rackspace/lb/v1/lbs/urls.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package lbs
-
-import (
-	"strconv"
-
-	"github.com/rackspace/gophercloud"
-)
-
-const (
-	path           = "loadbalancers"
-	protocolsPath  = "protocols"
-	algorithmsPath = "algorithms"
-	logPath        = "connectionlogging"
-	epPath         = "errorpage"
-	stPath         = "stats"
-	cachePath      = "contentcaching"
-)
-
-func resourceURL(c *gophercloud.ServiceClient, id int) string {
-	return c.ServiceURL(path, strconv.Itoa(id))
-}
-
-func rootURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL(path)
-}
-
-func protocolsURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL(path, protocolsPath)
-}
-
-func algorithmsURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL(path, algorithmsPath)
-}
-
-func loggingURL(c *gophercloud.ServiceClient, id int) string {
-	return c.ServiceURL(path, strconv.Itoa(id), logPath)
-}
-
-func errorPageURL(c *gophercloud.ServiceClient, id int) string {
-	return c.ServiceURL(path, strconv.Itoa(id), epPath)
-}
-
-func statsURL(c *gophercloud.ServiceClient, id int) string {
-	return c.ServiceURL(path, strconv.Itoa(id), stPath)
-}
-
-func cacheURL(c *gophercloud.ServiceClient, id int) string {
-	return c.ServiceURL(path, strconv.Itoa(id), cachePath)
-}
diff --git a/rackspace/lb/v1/monitors/doc.go b/rackspace/lb/v1/monitors/doc.go
deleted file mode 100644
index 2c5be75..0000000
--- a/rackspace/lb/v1/monitors/doc.go
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
-Package monitors provides information and interaction with the Health Monitor
-API resource for the Rackspace Cloud Load Balancer service.
-
-The load balancing service includes a health monitoring resource that
-periodically checks your back-end nodes to ensure they are responding correctly.
-If a node does not respond, it is removed from rotation until the health monitor
-determines that the node is functional. In addition to being performed
-periodically, a health check also executes against every new node that is
-added, to ensure that the node is operating properly before allowing it to
-service traffic. Only one health monitor is allowed to be enabled on a load
-balancer at a time.
-
-As part of a good strategy for monitoring connections, secondary nodes should
-also be created which provide failover for effectively routing traffic in case
-the primary node fails. This is an additional feature that ensures that you
-remain up in case your primary node fails.
-
-There are three types of health monitor: CONNECT, HTTP and HTTPS.
-*/
-package monitors
diff --git a/rackspace/lb/v1/monitors/fixtures.go b/rackspace/lb/v1/monitors/fixtures.go
deleted file mode 100644
index 72bfdcd..0000000
--- a/rackspace/lb/v1/monitors/fixtures.go
+++ /dev/null
@@ -1,89 +0,0 @@
-// +build fixtures
-
-package monitors
-
-import (
-	"fmt"
-	"net/http"
-	"strconv"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func _rootURL(lbID int) string {
-	return "/loadbalancers/" + strconv.Itoa(lbID) + "/healthmonitor"
-}
-
-func mockGetResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "healthMonitor": {
-    "type": "CONNECT",
-    "delay": 10,
-    "timeout": 10,
-    "attemptsBeforeDeactivation": 3
-  }
-}
-  `)
-	})
-}
-
-func mockUpdateConnectResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "healthMonitor": {
-    "type": "CONNECT",
-    "delay": 10,
-    "timeout": 10,
-    "attemptsBeforeDeactivation": 3
-  }
-}
-    `)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockUpdateHTTPResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "healthMonitor": {
-    "attemptsBeforeDeactivation": 3,
-    "bodyRegex": "{regex}",
-    "delay": 10,
-    "path": "/foo",
-    "statusRegex": "200",
-    "timeout": 10,
-    "type": "HTTPS"
-  }
-}
-		`)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockDeleteResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/rackspace/lb/v1/monitors/requests.go b/rackspace/lb/v1/monitors/requests.go
deleted file mode 100644
index d4ba276..0000000
--- a/rackspace/lb/v1/monitors/requests.go
+++ /dev/null
@@ -1,160 +0,0 @@
-package monitors
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-)
-
-var (
-	errAttemptLimit = errors.New("AttemptLimit field must be an int greater than 1 and less than 10")
-	errDelay        = errors.New("Delay field must be an int greater than 1 and less than 10")
-	errTimeout      = errors.New("Timeout field must be an int greater than 1 and less than 10")
-)
-
-// UpdateOptsBuilder is the interface options structs have to satisfy in order
-// to be used in the main Update operation in this package.
-type UpdateOptsBuilder interface {
-	ToMonitorUpdateMap() (map[string]interface{}, error)
-}
-
-// UpdateConnectMonitorOpts represents the options needed to update a CONNECT
-// monitor.
-type UpdateConnectMonitorOpts struct {
-	// Required - number of permissible monitor failures before removing a node
-	// from rotation. Must be a number between 1 and 10.
-	AttemptLimit int
-
-	// Required - the minimum number of seconds to wait before executing the
-	// health monitor. Must be a number between 1 and 3600.
-	Delay int
-
-	// Required - maximum number of seconds to wait for a connection to be
-	// established before timing out. Must be a number between 1 and 300.
-	Timeout int
-}
-
-// ToMonitorUpdateMap produces a map for updating CONNECT monitors.
-func (opts UpdateConnectMonitorOpts) ToMonitorUpdateMap() (map[string]interface{}, error) {
-	type m map[string]interface{}
-
-	if !gophercloud.IntWithinRange(opts.AttemptLimit, 1, 10) {
-		return m{}, errAttemptLimit
-	}
-	if !gophercloud.IntWithinRange(opts.Delay, 1, 3600) {
-		return m{}, errDelay
-	}
-	if !gophercloud.IntWithinRange(opts.Timeout, 1, 300) {
-		return m{}, errTimeout
-	}
-
-	return m{"healthMonitor": m{
-		"attemptsBeforeDeactivation": opts.AttemptLimit,
-		"delay":   opts.Delay,
-		"timeout": opts.Timeout,
-		"type":    CONNECT,
-	}}, nil
-}
-
-// UpdateHTTPMonitorOpts represents the options needed to update a HTTP monitor.
-type UpdateHTTPMonitorOpts struct {
-	// Required - number of permissible monitor failures before removing a node
-	// from rotation. Must be a number between 1 and 10.
-	AttemptLimit int `mapstructure:"attemptsBeforeDeactivation"`
-
-	// Required - the minimum number of seconds to wait before executing the
-	// health monitor. Must be a number between 1 and 3600.
-	Delay int
-
-	// Required - maximum number of seconds to wait for a connection to be
-	// established before timing out. Must be a number between 1 and 300.
-	Timeout int
-
-	// Required - a regular expression that will be used to evaluate the contents
-	// of the body of the response.
-	BodyRegex string
-
-	// Required - the HTTP path that will be used in the sample request.
-	Path string
-
-	// Required - a regular expression that will be used to evaluate the HTTP
-	// status code returned in the response.
-	StatusRegex string
-
-	// Optional - the name of a host for which the health monitors will check.
-	HostHeader string
-
-	// Required - either HTTP or HTTPS
-	Type Type
-}
-
-// ToMonitorUpdateMap produces a map for updating HTTP(S) monitors.
-func (opts UpdateHTTPMonitorOpts) ToMonitorUpdateMap() (map[string]interface{}, error) {
-	type m map[string]interface{}
-
-	if !gophercloud.IntWithinRange(opts.AttemptLimit, 1, 10) {
-		return m{}, errAttemptLimit
-	}
-	if !gophercloud.IntWithinRange(opts.Delay, 1, 3600) {
-		return m{}, errDelay
-	}
-	if !gophercloud.IntWithinRange(opts.Timeout, 1, 300) {
-		return m{}, errTimeout
-	}
-	if opts.Type != HTTP && opts.Type != HTTPS {
-		return m{}, errors.New("Type must either by HTTP or HTTPS")
-	}
-	if opts.BodyRegex == "" {
-		return m{}, errors.New("BodyRegex is a required field")
-	}
-	if opts.Path == "" {
-		return m{}, errors.New("Path is a required field")
-	}
-	if opts.StatusRegex == "" {
-		return m{}, errors.New("StatusRegex is a required field")
-	}
-
-	json := m{
-		"attemptsBeforeDeactivation": opts.AttemptLimit,
-		"delay":       opts.Delay,
-		"timeout":     opts.Timeout,
-		"type":        opts.Type,
-		"bodyRegex":   opts.BodyRegex,
-		"path":        opts.Path,
-		"statusRegex": opts.StatusRegex,
-	}
-
-	if opts.HostHeader != "" {
-		json["hostHeader"] = opts.HostHeader
-	}
-
-	return m{"healthMonitor": json}, nil
-}
-
-// Update is the operation responsible for updating a health monitor.
-func Update(c *gophercloud.ServiceClient, id int, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToMonitorUpdateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Put(rootURL(c, id), reqBody, nil, nil)
-	return res
-}
-
-// Get is the operation responsible for showing details of a health monitor.
-func Get(c *gophercloud.ServiceClient, id int) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(rootURL(c, id), &res.Body, nil)
-	return res
-}
-
-// Delete is the operation responsible for deleting a health monitor.
-func Delete(c *gophercloud.ServiceClient, id int) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(rootURL(c, id), nil)
-	return res
-}
diff --git a/rackspace/lb/v1/monitors/requests_test.go b/rackspace/lb/v1/monitors/requests_test.go
deleted file mode 100644
index 76a60db..0000000
--- a/rackspace/lb/v1/monitors/requests_test.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package monitors
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const lbID = 12345
-
-func TestUpdateCONNECT(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockUpdateConnectResponse(t, lbID)
-
-	opts := UpdateConnectMonitorOpts{
-		AttemptLimit: 3,
-		Delay:        10,
-		Timeout:      10,
-	}
-
-	err := Update(client.ServiceClient(), lbID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestUpdateHTTP(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockUpdateHTTPResponse(t, lbID)
-
-	opts := UpdateHTTPMonitorOpts{
-		AttemptLimit: 3,
-		Delay:        10,
-		Timeout:      10,
-		BodyRegex:    "{regex}",
-		Path:         "/foo",
-		StatusRegex:  "200",
-		Type:         HTTPS,
-	}
-
-	err := Update(client.ServiceClient(), lbID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetResponse(t, lbID)
-
-	m, err := Get(client.ServiceClient(), lbID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Monitor{
-		Type:         CONNECT,
-		Delay:        10,
-		Timeout:      10,
-		AttemptLimit: 3,
-	}
-
-	th.AssertDeepEquals(t, expected, m)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteResponse(t, lbID)
-
-	err := Delete(client.ServiceClient(), lbID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/lb/v1/monitors/results.go b/rackspace/lb/v1/monitors/results.go
deleted file mode 100644
index eec556f..0000000
--- a/rackspace/lb/v1/monitors/results.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package monitors
-
-import (
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-)
-
-// Type represents the type of Monitor.
-type Type string
-
-// Useful constants.
-const (
-	CONNECT Type = "CONNECT"
-	HTTP    Type = "HTTP"
-	HTTPS   Type = "HTTPS"
-)
-
-// Monitor represents a health monitor API resource. A monitor comes in three
-// forms: CONNECT, HTTP or HTTPS.
-//
-// A CONNECT monitor establishes a basic connection to each node on its defined
-// port to ensure that the service is listening properly. The connect monitor
-// is the most basic type of health check and does no post-processing or
-// protocol-specific health checks.
-//
-// HTTP and HTTPS health monitors are generally considered more intelligent and
-// powerful than CONNECT. It is capable of processing an HTTP or HTTPS response
-// to determine the condition of a node. It supports the same basic properties
-// as CONNECT and includes additional attributes that are used to evaluate the
-// HTTP response.
-type Monitor struct {
-	// Number of permissible monitor failures before removing a node from
-	// rotation.
-	AttemptLimit int `mapstructure:"attemptsBeforeDeactivation"`
-
-	// The minimum number of seconds to wait before executing the health monitor.
-	Delay int
-
-	// Maximum number of seconds to wait for a connection to be established
-	// before timing out.
-	Timeout int
-
-	// Type of the health monitor.
-	Type Type
-
-	// A regular expression that will be used to evaluate the contents of the
-	// body of the response.
-	BodyRegex string
-
-	// The name of a host for which the health monitors will check.
-	HostHeader string
-
-	// The HTTP path that will be used in the sample request.
-	Path string
-
-	// A regular expression that will be used to evaluate the HTTP status code
-	// returned in the response.
-	StatusRegex string
-}
-
-// UpdateResult represents the result of an Update operation.
-type UpdateResult struct {
-	gophercloud.ErrResult
-}
-
-// GetResult represents the result of a Get operation.
-type GetResult struct {
-	gophercloud.Result
-}
-
-// DeleteResult represents the result of an Delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-// Extract interprets any GetResult as a Monitor.
-func (r GetResult) Extract() (*Monitor, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		M Monitor `mapstructure:"healthMonitor"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.M, err
-}
diff --git a/rackspace/lb/v1/monitors/urls.go b/rackspace/lb/v1/monitors/urls.go
deleted file mode 100644
index 0a1e6df..0000000
--- a/rackspace/lb/v1/monitors/urls.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package monitors
-
-import (
-	"strconv"
-
-	"github.com/rackspace/gophercloud"
-)
-
-const (
-	path        = "loadbalancers"
-	monitorPath = "healthmonitor"
-)
-
-func rootURL(c *gophercloud.ServiceClient, lbID int) string {
-	return c.ServiceURL(path, strconv.Itoa(lbID), monitorPath)
-}
diff --git a/rackspace/lb/v1/nodes/doc.go b/rackspace/lb/v1/nodes/doc.go
deleted file mode 100644
index 49c4318..0000000
--- a/rackspace/lb/v1/nodes/doc.go
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-Package nodes provides information and interaction with the Node API resource
-for the Rackspace Cloud Load Balancer service.
-
-Nodes are responsible for servicing the requests received through the load
-balancer's virtual IP. A node is usually a virtual machine. By default, the
-load balancer employs a basic health check that ensures the node is listening
-on its defined port. The node is checked at the time of addition and at regular
-intervals as defined by the load balancer's health check configuration. If a
-back-end node is not listening on its port, or does not meet the conditions of
-the defined check, then connections will not be forwarded to the node, and its
-status is changed to OFFLINE. Only nodes that are in an ONLINE status receive
-and can service traffic from the load balancer.
-
-All nodes have an associated status that indicates whether the node is
-ONLINE, OFFLINE, or DRAINING. Only nodes that are in ONLINE status can receive
-and service traffic from the load balancer. The OFFLINE status represents a
-node that cannot accept or service traffic. A node in DRAINING status
-represents a node that stops the traffic manager from sending any additional
-new connections to the node, but honors established sessions. If the traffic
-manager receives a request and session persistence requires that the node is
-used, the traffic manager uses it. The status is determined by the passive or
-active health monitors.
-
-If the WEIGHTED_ROUND_ROBIN load balancer algorithm mode is selected, then the
-caller should assign the relevant weights to the node as part of the weight
-attribute of the node element. When the algorithm of the load balancer is
-changed to WEIGHTED_ROUND_ROBIN and the nodes do not already have an assigned
-weight, the service automatically sets the weight to 1 for all nodes.
-
-One or more secondary nodes can be added to a specified load balancer so that
-if all the primary nodes fail, traffic can be redirected to secondary nodes.
-The type attribute allows configuring the node as either PRIMARY or SECONDARY.
-*/
-package nodes
diff --git a/rackspace/lb/v1/nodes/fixtures.go b/rackspace/lb/v1/nodes/fixtures.go
deleted file mode 100644
index 350eefa..0000000
--- a/rackspace/lb/v1/nodes/fixtures.go
+++ /dev/null
@@ -1,245 +0,0 @@
-// +build fixtures
-
-package nodes
-
-import (
-	"fmt"
-	"net/http"
-	"strconv"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func _rootURL(lbID int) string {
-	return "/loadbalancers/" + strconv.Itoa(lbID) + "/nodes"
-}
-
-func _nodeURL(lbID, nodeID int) string {
-	return _rootURL(lbID) + "/" + strconv.Itoa(nodeID)
-}
-
-func mockListResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "nodes": [
-    {
-      "id": 410,
-      "address": "10.1.1.1",
-      "port": 80,
-      "condition": "ENABLED",
-      "status": "ONLINE",
-      "weight": 3,
-      "type": "PRIMARY"
-    },
-    {
-      "id": 411,
-      "address": "10.1.1.2",
-      "port": 80,
-      "condition": "ENABLED",
-      "status": "ONLINE",
-      "weight": 8,
-      "type": "SECONDARY"
-    }
-  ]
-}
-  `)
-	})
-}
-
-func mockCreateResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "nodes": [
-    {
-      "address": "10.2.2.3",
-      "port": 80,
-      "condition": "ENABLED",
-      "type": "PRIMARY"
-    },
-    {
-      "address": "10.2.2.4",
-      "port": 81,
-      "condition": "ENABLED",
-      "type": "SECONDARY"
-    }
-  ]
-}
-    `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-
-		fmt.Fprintf(w, `
-{
-  "nodes": [
-    {
-      "address": "10.2.2.3",
-      "id": 185,
-      "port": 80,
-      "status": "ONLINE",
-      "condition": "ENABLED",
-      "weight": 1,
-      "type": "PRIMARY"
-    },
-    {
-      "address": "10.2.2.4",
-      "id": 186,
-      "port": 81,
-      "status": "ONLINE",
-      "condition": "ENABLED",
-      "weight": 1,
-      "type": "SECONDARY"
-    }
-  ]
-}
-  `)
-	})
-}
-
-func mockCreateErrResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "nodes": [
-    {
-      "address": "10.2.2.3",
-      "port": 80,
-      "condition": "ENABLED",
-      "type": "PRIMARY"
-    },
-    {
-      "address": "10.2.2.4",
-      "port": 81,
-      "condition": "ENABLED",
-      "type": "SECONDARY"
-    }
-  ]
-}
-    `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(422) // Unprocessable Entity
-
-		fmt.Fprintf(w, `
-{
-  "code": 422,
-  "message": "Load Balancer '%d' has a status of 'PENDING_UPDATE' and is considered immutable."
-}
-  `, lbID)
-	})
-}
-
-func mockBatchDeleteResponse(t *testing.T, lbID int, ids []int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		r.ParseForm()
-
-		for k, v := range ids {
-			fids := r.Form["id"]
-			th.AssertEquals(t, strconv.Itoa(v), fids[k])
-		}
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockDeleteResponse(t *testing.T, lbID, nodeID int) {
-	th.Mux.HandleFunc(_nodeURL(lbID, nodeID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockGetResponse(t *testing.T, lbID, nodeID int) {
-	th.Mux.HandleFunc(_nodeURL(lbID, nodeID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "node": {
-    "id": 410,
-    "address": "10.1.1.1",
-    "port": 80,
-    "condition": "ENABLED",
-    "status": "ONLINE",
-    "weight": 12,
-    "type": "PRIMARY"
-  }
-}
-  `)
-	})
-}
-
-func mockUpdateResponse(t *testing.T, lbID, nodeID int) {
-	th.Mux.HandleFunc(_nodeURL(lbID, nodeID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "node": {
-    "condition": "DRAINING",
-    "weight": 10,
-		"type": "SECONDARY"
-  }
-}
-    `)
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockListEventsResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID)+"/events", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "nodeServiceEvents": [
-    {
-      "detailedMessage": "Node is ok",
-      "nodeId": 373,
-      "id": 7,
-      "type": "UPDATE_NODE",
-      "description": "Node '373' status changed to 'ONLINE' for load balancer '323'",
-      "category": "UPDATE",
-      "severity": "INFO",
-      "relativeUri": "/406271/loadbalancers/323/nodes/373/events",
-      "accountId": 406271,
-      "loadbalancerId": 323,
-      "title": "Node Status Updated",
-      "author": "Rackspace Cloud",
-      "created": "10-30-2012 10:18:23"
-    }
-  ]
-}
-`)
-	})
-}
diff --git a/rackspace/lb/v1/nodes/requests.go b/rackspace/lb/v1/nodes/requests.go
deleted file mode 100644
index 9da376f..0000000
--- a/rackspace/lb/v1/nodes/requests.go
+++ /dev/null
@@ -1,286 +0,0 @@
-package nodes
-
-import (
-	"errors"
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List is the operation responsible for returning a paginated collection of
-// load balancer nodes. It requires the node ID, its parent load balancer ID,
-// and optional limit integer (passed in either as a pointer or a nil poitner).
-func List(client *gophercloud.ServiceClient, loadBalancerID int, limit *int) pagination.Pager {
-	url := rootURL(client, loadBalancerID)
-	if limit != nil {
-		url += fmt.Sprintf("?limit=%d", limit)
-	}
-
-	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
-		return NodePage{pagination.SinglePageBase(r)}
-	})
-}
-
-// CreateOptsBuilder is the interface responsible for generating the JSON
-// for a Create operation.
-type CreateOptsBuilder interface {
-	ToNodeCreateMap() (map[string]interface{}, error)
-}
-
-// CreateOpts is a slice of CreateOpt structs, that allow the user to create
-// multiple nodes in a single operation (one node per CreateOpt).
-type CreateOpts []CreateOpt
-
-// CreateOpt represents the options to create a single node.
-type CreateOpt struct {
-	// Required - the IP address or CIDR for this back-end node. It can either be
-	// a private IP (ServiceNet) or a public IP.
-	Address string
-
-	// Optional - the port on which traffic is sent and received.
-	Port int
-
-	// Optional - the condition of the node. See the consts in Results.go.
-	Condition Condition
-
-	// Optional - the type of the node. See the consts in Results.go.
-	Type Type
-
-	// Optional - a pointer to an integer between 0 and 100.
-	Weight *int
-}
-
-func validateWeight(weight *int) error {
-	if weight != nil && (*weight > 100 || *weight < 0) {
-		return errors.New("Weight must be a valid int between 0 and 100")
-	}
-	return nil
-}
-
-// ToNodeCreateMap converts a slice of options into a map that can be used for
-// the JSON.
-func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) {
-	type nodeMap map[string]interface{}
-	nodes := []nodeMap{}
-
-	for k, v := range opts {
-		if v.Address == "" {
-			return nodeMap{}, fmt.Errorf("ID is a required attribute, none provided for %d CreateOpt element", k)
-		}
-		if weightErr := validateWeight(v.Weight); weightErr != nil {
-			return nodeMap{}, weightErr
-		}
-
-		node := make(map[string]interface{})
-		node["address"] = v.Address
-
-		if v.Port > 0 {
-			node["port"] = v.Port
-		}
-		if v.Condition != "" {
-			node["condition"] = v.Condition
-		}
-		if v.Type != "" {
-			node["type"] = v.Type
-		}
-		if v.Weight != nil {
-			node["weight"] = &v.Weight
-		}
-
-		nodes = append(nodes, node)
-	}
-
-	return nodeMap{"nodes": nodes}, nil
-}
-
-// Create is the operation responsible for creating a new node on a load
-// balancer. Since every load balancer exists in both ServiceNet and the public
-// Internet, both private and public IP addresses can be used for nodes.
-//
-// If nodes need time to boot up services before they become operational, you
-// can temporarily prevent traffic from being sent to that node by setting the
-// Condition field to DRAINING. Health checks will still be performed; but once
-// your node is ready, you can update its condition to ENABLED and have it
-// handle traffic.
-func Create(client *gophercloud.ServiceClient, loadBalancerID int, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToNodeCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	resp, err := client.Post(rootURL(client, loadBalancerID), reqBody, &res.Body, nil)
-
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	pr := pagination.PageResultFromParsed(resp, res.Body)
-	return CreateResult{pagination.SinglePageBase(pr)}
-}
-
-// BulkDelete is the operation responsible for batch deleting multiple nodes in
-// a single operation. It accepts a slice of integer IDs and will remove them
-// from the load balancer. The maximum limit is 10 node removals at once.
-func BulkDelete(c *gophercloud.ServiceClient, loadBalancerID int, nodeIDs []int) DeleteResult {
-	var res DeleteResult
-
-	if len(nodeIDs) > 10 || len(nodeIDs) == 0 {
-		res.Err = errors.New("You must provide a minimum of 1 and a maximum of 10 node IDs")
-		return res
-	}
-
-	url := rootURL(c, loadBalancerID)
-	url += gophercloud.IDSliceToQueryString("id", nodeIDs)
-
-	_, res.Err = c.Delete(url, nil)
-	return res
-}
-
-// Get is the operation responsible for showing details for a single node.
-func Get(c *gophercloud.ServiceClient, lbID, nodeID int) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(resourceURL(c, lbID, nodeID), &res.Body, nil)
-	return res
-}
-
-// UpdateOptsBuilder represents a type that can be converted into a JSON-like
-// map structure.
-type UpdateOptsBuilder interface {
-	ToNodeUpdateMap() (map[string]interface{}, error)
-}
-
-// UpdateOpts represent the options for updating an existing node.
-type UpdateOpts struct {
-	// Optional - the condition of the node. See the consts in Results.go.
-	Condition Condition
-
-	// Optional - the type of the node. See the consts in Results.go.
-	Type Type
-
-	// Optional - a pointer to an integer between 0 and 100.
-	Weight *int
-}
-
-// ToNodeUpdateMap converts an options struct into a JSON-like map.
-func (opts UpdateOpts) ToNodeUpdateMap() (map[string]interface{}, error) {
-	node := make(map[string]interface{})
-
-	if opts.Condition != "" {
-		node["condition"] = opts.Condition
-	}
-	if opts.Weight != nil {
-		if weightErr := validateWeight(opts.Weight); weightErr != nil {
-			return node, weightErr
-		}
-		node["weight"] = &opts.Weight
-	}
-	if opts.Type != "" {
-		node["type"] = opts.Type
-	}
-
-	return map[string]interface{}{"node": node}, nil
-}
-
-// Update is the operation responsible for updating an existing node. A node's
-// IP, port, and status are immutable attributes and cannot be modified.
-func Update(c *gophercloud.ServiceClient, lbID, nodeID int, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToNodeUpdateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Put(resourceURL(c, lbID, nodeID), reqBody, nil, nil)
-	return res
-}
-
-// Delete is the operation responsible for permanently deleting a node.
-func Delete(c *gophercloud.ServiceClient, lbID, nodeID int) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, lbID, nodeID), nil)
-	return res
-}
-
-// ListEventsOptsBuilder allows extensions to add additional parameters to the
-// List request.
-type ListEventsOptsBuilder interface {
-	ToEventsListQuery() (string, error)
-}
-
-// ListEventsOpts allows the filtering and sorting of paginated collections through
-// the API.
-type ListEventsOpts struct {
-	Marker string `q:"marker"`
-	Limit  int    `q:"limit"`
-}
-
-// ToEventsListQuery formats a ListOpts into a query string.
-func (opts ListEventsOpts) ToEventsListQuery() (string, error) {
-	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
-}
-
-// ListEvents is the operation responsible for listing all the events
-// associated with the activity between the node and the load balancer. The
-// events report errors found with the node. The detailedMessage provides the
-// detailed reason for the error.
-func ListEvents(client *gophercloud.ServiceClient, loadBalancerID int, opts ListEventsOptsBuilder) pagination.Pager {
-	url := eventsURL(client, loadBalancerID)
-
-	if opts != nil {
-		query, err := opts.ToEventsListQuery()
-		if err != nil {
-			return pagination.Pager{Err: err}
-		}
-		url += query
-	}
-
-	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
-		return NodeEventPage{pagination.SinglePageBase(r)}
-	})
-}
-
-// GetByIPPort locates a load balancer node by IP and port.
-func GetByIPPort(
-	client *gophercloud.ServiceClient,
-	loadBalancerID int,
-	address string,
-	port int,
-) (*Node, error) {
-
-	// nil until found
-	var found *Node
-
-	List(client, loadBalancerID, nil).EachPage(func(page pagination.Page) (bool, error) {
-		lbNodes, err := ExtractNodes(page)
-		if err != nil {
-			return false, err
-		}
-
-		for _, trialNode := range lbNodes {
-			if trialNode.Address == address && trialNode.Port == port {
-				found = &trialNode
-				return false, nil
-			}
-
-		}
-
-		return true, nil
-	})
-
-	if found == nil {
-		return nil, errors.New("Unable to get node by IP and Port")
-	}
-
-	return found, nil
-}
diff --git a/rackspace/lb/v1/nodes/requests_test.go b/rackspace/lb/v1/nodes/requests_test.go
deleted file mode 100644
index a964af8..0000000
--- a/rackspace/lb/v1/nodes/requests_test.go
+++ /dev/null
@@ -1,243 +0,0 @@
-package nodes
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const (
-	lbID    = 12345
-	nodeID  = 67890
-	nodeID2 = 67891
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListResponse(t, lbID)
-
-	count := 0
-
-	err := List(client.ServiceClient(), lbID, nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNodes(page)
-		th.AssertNoErr(t, err)
-
-		expected := []Node{
-			Node{
-				ID:        410,
-				Address:   "10.1.1.1",
-				Port:      80,
-				Condition: ENABLED,
-				Status:    ONLINE,
-				Weight:    3,
-				Type:      PRIMARY,
-			},
-			Node{
-				ID:        411,
-				Address:   "10.1.1.2",
-				Port:      80,
-				Condition: ENABLED,
-				Status:    ONLINE,
-				Weight:    8,
-				Type:      SECONDARY,
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockCreateResponse(t, lbID)
-
-	opts := CreateOpts{
-		CreateOpt{
-			Address:   "10.2.2.3",
-			Port:      80,
-			Condition: ENABLED,
-			Type:      PRIMARY,
-		},
-		CreateOpt{
-			Address:   "10.2.2.4",
-			Port:      81,
-			Condition: ENABLED,
-			Type:      SECONDARY,
-		},
-	}
-
-	page := Create(client.ServiceClient(), lbID, opts)
-
-	actual, err := page.ExtractNodes()
-	th.AssertNoErr(t, err)
-
-	expected := []Node{
-		Node{
-			ID:        185,
-			Address:   "10.2.2.3",
-			Port:      80,
-			Condition: ENABLED,
-			Status:    ONLINE,
-			Weight:    1,
-			Type:      PRIMARY,
-		},
-		Node{
-			ID:        186,
-			Address:   "10.2.2.4",
-			Port:      81,
-			Condition: ENABLED,
-			Status:    ONLINE,
-			Weight:    1,
-			Type:      SECONDARY,
-		},
-	}
-
-	th.CheckDeepEquals(t, expected, actual)
-}
-
-func TestCreateErr(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockCreateErrResponse(t, lbID)
-
-	opts := CreateOpts{
-		CreateOpt{
-			Address:   "10.2.2.3",
-			Port:      80,
-			Condition: ENABLED,
-			Type:      PRIMARY,
-		},
-		CreateOpt{
-			Address:   "10.2.2.4",
-			Port:      81,
-			Condition: ENABLED,
-			Type:      SECONDARY,
-		},
-	}
-
-	page := Create(client.ServiceClient(), lbID, opts)
-
-	actual, err := page.ExtractNodes()
-	if err == nil {
-		t.Fatal("Did not receive expected error from ExtractNodes")
-	}
-	if actual != nil {
-		t.Fatalf("Received non-nil result from failed ExtractNodes: %#v", actual)
-	}
-}
-
-func TestBulkDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	ids := []int{nodeID, nodeID2}
-
-	mockBatchDeleteResponse(t, lbID, ids)
-
-	err := BulkDelete(client.ServiceClient(), lbID, ids).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetResponse(t, lbID, nodeID)
-
-	node, err := Get(client.ServiceClient(), lbID, nodeID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Node{
-		ID:        410,
-		Address:   "10.1.1.1",
-		Port:      80,
-		Condition: ENABLED,
-		Status:    ONLINE,
-		Weight:    12,
-		Type:      PRIMARY,
-	}
-
-	th.AssertDeepEquals(t, expected, node)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockUpdateResponse(t, lbID, nodeID)
-
-	opts := UpdateOpts{
-		Weight:    gophercloud.IntToPointer(10),
-		Condition: DRAINING,
-		Type:      SECONDARY,
-	}
-
-	err := Update(client.ServiceClient(), lbID, nodeID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteResponse(t, lbID, nodeID)
-
-	err := Delete(client.ServiceClient(), lbID, nodeID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestListEvents(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListEventsResponse(t, lbID)
-
-	count := 0
-
-	pager := ListEvents(client.ServiceClient(), lbID, ListEventsOpts{})
-
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNodeEvents(page)
-		th.AssertNoErr(t, err)
-
-		expected := []NodeEvent{
-			NodeEvent{
-				DetailedMessage: "Node is ok",
-				NodeID:          373,
-				ID:              7,
-				Type:            "UPDATE_NODE",
-				Description:     "Node '373' status changed to 'ONLINE' for load balancer '323'",
-				Category:        "UPDATE",
-				Severity:        "INFO",
-				RelativeURI:     "/406271/loadbalancers/323/nodes/373/events",
-				AccountID:       406271,
-				LoadBalancerID:  323,
-				Title:           "Node Status Updated",
-				Author:          "Rackspace Cloud",
-				Created:         "10-30-2012 10:18:23",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
diff --git a/rackspace/lb/v1/nodes/results.go b/rackspace/lb/v1/nodes/results.go
deleted file mode 100644
index 57835dc..0000000
--- a/rackspace/lb/v1/nodes/results.go
+++ /dev/null
@@ -1,213 +0,0 @@
-package nodes
-
-import (
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Node represents a back-end device, usually a virtual machine, that can
-// handle traffic. It is assigned traffic based on its parent load balancer.
-type Node struct {
-	// The IP address or CIDR for this back-end node.
-	Address string
-
-	// The unique ID for this node.
-	ID int
-
-	// The port on which traffic is sent and received.
-	Port int
-
-	// The node's status.
-	Status Status
-
-	// The node's condition.
-	Condition Condition
-
-	// The priority at which this node will receive traffic if a weighted
-	// algorithm is used by its parent load balancer. Ranges from 1 to 100.
-	Weight int
-
-	// Type of node.
-	Type Type
-}
-
-// Type indicates whether the node is of a PRIMARY or SECONDARY nature.
-type Type string
-
-const (
-	// PRIMARY nodes are in the normal rotation to receive traffic from the load
-	// balancer.
-	PRIMARY Type = "PRIMARY"
-
-	// SECONDARY nodes are only in the rotation to receive traffic from the load
-	// balancer when all the primary nodes fail. This provides a failover feature
-	// that automatically routes traffic to the secondary node in the event that
-	// the primary node is disabled or in a failing state. Note that active
-	// health monitoring must be enabled on the load balancer to enable the
-	// failover feature to the secondary node.
-	SECONDARY Type = "SECONDARY"
-)
-
-// Condition represents the condition of a node.
-type Condition string
-
-const (
-	// ENABLED indicates that the node is permitted to accept new connections.
-	ENABLED Condition = "ENABLED"
-
-	// DISABLED indicates that the node is not permitted to accept any new
-	// connections regardless of session persistence configuration. Existing
-	// connections are forcibly terminated.
-	DISABLED Condition = "DISABLED"
-
-	// DRAINING indicates that the node is allowed to service existing
-	// established connections and connections that are being directed to it as a
-	// result of the session persistence configuration.
-	DRAINING Condition = "DRAINING"
-)
-
-// Status indicates whether the node can accept service traffic. If a node is
-// not listening on its port or does not meet the conditions of the defined
-// active health check for the load balancer, then the load balancer does not
-// forward connections, and its status is listed as OFFLINE.
-type Status string
-
-const (
-	// ONLINE indicates that the node is healthy and capable of receiving traffic
-	// from the load balancer.
-	ONLINE Status = "ONLINE"
-
-	// OFFLINE indicates that the node is not in a position to receive service
-	// traffic. It is usually switched into this state when a health check is not
-	// satisfied with the node's response time.
-	OFFLINE Status = "OFFLINE"
-)
-
-// NodePage is the page returned by a pager when traversing over a collection
-// of nodes.
-type NodePage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty checks whether a NodePage struct is empty.
-func (p NodePage) IsEmpty() (bool, error) {
-	is, err := ExtractNodes(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
-}
-
-func commonExtractNodes(body interface{}) ([]Node, error) {
-	var resp struct {
-		Nodes []Node `mapstructure:"nodes" json:"nodes"`
-	}
-
-	err := mapstructure.Decode(body, &resp)
-
-	return resp.Nodes, err
-}
-
-// ExtractNodes accepts a Page struct, specifically a NodePage struct, and
-// extracts the elements into a slice of Node structs. In other words, a
-// generic collection is mapped into a relevant slice.
-func ExtractNodes(page pagination.Page) ([]Node, error) {
-	return commonExtractNodes(page.(NodePage).Body)
-}
-
-// CreateResult represents the result of a create operation. Since multiple
-// nodes can be added in one operation, this result represents multiple nodes
-// and should be treated as a typical pagination Page. Use its ExtractNodes
-// method to get out a slice of Node structs.
-type CreateResult struct {
-	pagination.SinglePageBase
-}
-
-// ExtractNodes extracts a slice of Node structs from a CreateResult.
-func (res CreateResult) ExtractNodes() ([]Node, error) {
-	if res.Err != nil {
-		return nil, res.Err
-	}
-	return commonExtractNodes(res.Body)
-}
-
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-type commonResult struct {
-	gophercloud.Result
-}
-
-// GetResult represents the result of a get operation.
-type GetResult struct {
-	commonResult
-}
-
-// UpdateResult represents the result of an update operation.
-type UpdateResult struct {
-	gophercloud.ErrResult
-}
-
-func (r commonResult) Extract() (*Node, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		Node Node `mapstructure:"node"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.Node, err
-}
-
-// NodeEvent represents a service event that occurred between a node and a
-// load balancer.
-type NodeEvent struct {
-	ID              int
-	DetailedMessage string
-	NodeID          int
-	Type            string
-	Description     string
-	Category        string
-	Severity        string
-	RelativeURI     string
-	AccountID       int
-	LoadBalancerID  int
-	Title           string
-	Author          string
-	Created         string
-}
-
-// NodeEventPage is a concrete type which embeds the common SinglePageBase
-// struct, and is used when traversing node event collections.
-type NodeEventPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty is a concrete function which indicates whether an NodeEventPage is
-// empty or not.
-func (r NodeEventPage) IsEmpty() (bool, error) {
-	is, err := ExtractNodeEvents(r)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
-}
-
-// ExtractNodeEvents accepts a Page struct, specifically a NodeEventPage
-// struct, and extracts the elements into a slice of NodeEvent structs. In
-// other words, the collection is mapped into a relevant slice.
-func ExtractNodeEvents(page pagination.Page) ([]NodeEvent, error) {
-	var resp struct {
-		Events []NodeEvent `mapstructure:"nodeServiceEvents" json:"nodeServiceEvents"`
-	}
-
-	err := mapstructure.Decode(page.(NodeEventPage).Body, &resp)
-
-	return resp.Events, err
-}
diff --git a/rackspace/lb/v1/nodes/urls.go b/rackspace/lb/v1/nodes/urls.go
deleted file mode 100644
index 2cefee2..0000000
--- a/rackspace/lb/v1/nodes/urls.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package nodes
-
-import (
-	"strconv"
-
-	"github.com/rackspace/gophercloud"
-)
-
-const (
-	lbPath    = "loadbalancers"
-	nodePath  = "nodes"
-	eventPath = "events"
-)
-
-func resourceURL(c *gophercloud.ServiceClient, lbID, nodeID int) string {
-	return c.ServiceURL(lbPath, strconv.Itoa(lbID), nodePath, strconv.Itoa(nodeID))
-}
-
-func rootURL(c *gophercloud.ServiceClient, lbID int) string {
-	return c.ServiceURL(lbPath, strconv.Itoa(lbID), nodePath)
-}
-
-func eventsURL(c *gophercloud.ServiceClient, lbID int) string {
-	return c.ServiceURL(lbPath, strconv.Itoa(lbID), nodePath, eventPath)
-}
diff --git a/rackspace/lb/v1/sessions/doc.go b/rackspace/lb/v1/sessions/doc.go
deleted file mode 100644
index dcec0a8..0000000
--- a/rackspace/lb/v1/sessions/doc.go
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-Package sessions provides information and interaction with the Session
-Persistence feature of the Rackspace Cloud Load Balancer service.
-
-Session persistence is a feature of the load balancing service that forces
-multiple requests from clients (of the same protocol) to be directed to the
-same node. This is common with many web applications that do not inherently
-share application state between back-end servers.
-
-There are two modes to choose from: HTTP_COOKIE and SOURCE_IP. You can only set
-one of the session persistence modes on a load balancer, and it can only
-support one protocol. If you set HTTP_COOKIE mode for an HTTP load balancer, it
-supports session persistence for HTTP requests only. Likewise, if you set
-SOURCE_IP mode for an HTTPS load balancer, it supports session persistence for
-only HTTPS requests.
-
-To support session persistence for both HTTP and HTTPS requests concurrently,
-choose one of the following options:
-
-- Use two load balancers, one configured for session persistence for HTTP
-requests and the other configured for session persistence for HTTPS requests.
-That way, the load balancers support session persistence for both HTTP and
-HTTPS requests concurrently, with each load balancer supporting one of the
-protocols.
-
-- Use one load balancer, configure it for session persistence for HTTP requests,
-and then enable SSL termination for that load balancer. The load balancer
-supports session persistence for both HTTP and HTTPS requests concurrently.
-*/
-package sessions
diff --git a/rackspace/lb/v1/sessions/fixtures.go b/rackspace/lb/v1/sessions/fixtures.go
deleted file mode 100644
index e59dd6c..0000000
--- a/rackspace/lb/v1/sessions/fixtures.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// +build fixtures
-
-package sessions
-
-import (
-	"fmt"
-	"net/http"
-	"strconv"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func _rootURL(id int) string {
-	return "/loadbalancers/" + strconv.Itoa(id) + "/sessionpersistence"
-}
-
-func mockGetResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "sessionPersistence": {
-    "persistenceType": "HTTP_COOKIE"
-  }
-}
-`)
-	})
-}
-
-func mockEnableResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "sessionPersistence": {
-    "persistenceType": "HTTP_COOKIE"
-  }
-}
-    `)
-
-		w.WriteHeader(http.StatusAccepted)
-		fmt.Fprintf(w, `{}`)
-	})
-}
-
-func mockDisableResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/rackspace/lb/v1/sessions/requests.go b/rackspace/lb/v1/sessions/requests.go
deleted file mode 100644
index a93d766..0000000
--- a/rackspace/lb/v1/sessions/requests.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package sessions
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-)
-
-// CreateOptsBuilder is the interface options structs have to satisfy in order
-// to be used in the main Create operation in this package.
-type CreateOptsBuilder interface {
-	ToSPCreateMap() (map[string]interface{}, error)
-}
-
-// CreateOpts is the common options struct used in this package's Create
-// operation.
-type CreateOpts struct {
-	// Required - can either be HTTPCOOKIE or SOURCEIP
-	Type Type
-}
-
-// ToSPCreateMap casts a CreateOpts struct to a map.
-func (opts CreateOpts) ToSPCreateMap() (map[string]interface{}, error) {
-	sp := make(map[string]interface{})
-
-	if opts.Type == "" {
-		return sp, errors.New("Type is a required field")
-	}
-
-	sp["persistenceType"] = opts.Type
-	return map[string]interface{}{"sessionPersistence": sp}, nil
-}
-
-// Enable is the operation responsible for enabling session persistence for a
-// particular load balancer.
-func Enable(c *gophercloud.ServiceClient, lbID int, opts CreateOptsBuilder) EnableResult {
-	var res EnableResult
-
-	reqBody, err := opts.ToSPCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Put(rootURL(c, lbID), reqBody, &res.Body, nil)
-	return res
-}
-
-// Get is the operation responsible for showing details of the session
-// persistence configuration for a particular load balancer.
-func Get(c *gophercloud.ServiceClient, lbID int) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(rootURL(c, lbID), &res.Body, nil)
-	return res
-}
-
-// Disable is the operation responsible for disabling session persistence for a
-// particular load balancer.
-func Disable(c *gophercloud.ServiceClient, lbID int) DisableResult {
-	var res DisableResult
-	_, res.Err = c.Delete(rootURL(c, lbID), nil)
-	return res
-}
diff --git a/rackspace/lb/v1/sessions/requests_test.go b/rackspace/lb/v1/sessions/requests_test.go
deleted file mode 100644
index f319e54..0000000
--- a/rackspace/lb/v1/sessions/requests_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package sessions
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const lbID = 12345
-
-func TestEnable(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockEnableResponse(t, lbID)
-
-	opts := CreateOpts{Type: HTTPCOOKIE}
-	err := Enable(client.ServiceClient(), lbID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetResponse(t, lbID)
-
-	sp, err := Get(client.ServiceClient(), lbID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &SessionPersistence{Type: HTTPCOOKIE}
-	th.AssertDeepEquals(t, expected, sp)
-}
-
-func TestDisable(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDisableResponse(t, lbID)
-
-	err := Disable(client.ServiceClient(), lbID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/lb/v1/sessions/results.go b/rackspace/lb/v1/sessions/results.go
deleted file mode 100644
index fe90e72..0000000
--- a/rackspace/lb/v1/sessions/results.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package sessions
-
-import (
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-)
-
-// Type represents the type of session persistence being used.
-type Type string
-
-const (
-	// HTTPCOOKIE is a session persistence mechanism that inserts an HTTP cookie
-	// and is used to determine the destination back-end node. This is supported
-	// for HTTP load balancing only.
-	HTTPCOOKIE Type = "HTTP_COOKIE"
-
-	// SOURCEIP is a session persistence mechanism that keeps track of the source
-	// IP address that is mapped and is able to determine the destination
-	// back-end node. This is supported for HTTPS pass-through and non-HTTP load
-	// balancing only.
-	SOURCEIP Type = "SOURCE_IP"
-)
-
-// SessionPersistence indicates how a load balancer is using session persistence
-type SessionPersistence struct {
-	Type Type `mapstructure:"persistenceType"`
-}
-
-// EnableResult represents the result of an enable operation.
-type EnableResult struct {
-	gophercloud.ErrResult
-}
-
-// DisableResult represents the result of a disable operation.
-type DisableResult struct {
-	gophercloud.ErrResult
-}
-
-// GetResult represents the result of a get operation.
-type GetResult struct {
-	gophercloud.Result
-}
-
-// Extract interprets a GetResult as an SP, if possible.
-func (r GetResult) Extract() (*SessionPersistence, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		SP SessionPersistence `mapstructure:"sessionPersistence"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.SP, err
-}
diff --git a/rackspace/lb/v1/sessions/urls.go b/rackspace/lb/v1/sessions/urls.go
deleted file mode 100644
index c4a896d..0000000
--- a/rackspace/lb/v1/sessions/urls.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package sessions
-
-import (
-	"strconv"
-
-	"github.com/rackspace/gophercloud"
-)
-
-const (
-	path   = "loadbalancers"
-	spPath = "sessionpersistence"
-)
-
-func rootURL(c *gophercloud.ServiceClient, id int) string {
-	return c.ServiceURL(path, strconv.Itoa(id), spPath)
-}
diff --git a/rackspace/lb/v1/ssl/doc.go b/rackspace/lb/v1/ssl/doc.go
deleted file mode 100644
index 6a2c174..0000000
--- a/rackspace/lb/v1/ssl/doc.go
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
-Package ssl provides information and interaction with the SSL Termination
-feature of the Rackspace Cloud Load Balancer service.
-
-You may only enable and configure SSL termination on load balancers with
-non-secure protocols, such as HTTP, but not HTTPS.
-
-SSL-terminated load balancers decrypt the traffic at the traffic manager and
-pass unencrypted traffic to the back-end node. Because of this, the customer's
-back-end nodes don't know what protocol the client requested. For this reason,
-the X-Forwarded-Proto (XFP) header has been added for identifying the
-originating protocol of an HTTP request as "http" or "https" depending on what
-protocol the client requested.
-
-Not every service returns certificates in the proper order. Please verify that
-your chain of certificates matches that of walking up the chain from the domain
-to the CA root.
-
-If used for HTTP to HTTPS redirection, the LoadBalancer's securePort attribute
-must be set to 443, and its secureTrafficOnly attribute must be true.
-*/
-package ssl
diff --git a/rackspace/lb/v1/ssl/fixtures.go b/rackspace/lb/v1/ssl/fixtures.go
deleted file mode 100644
index d03d6de..0000000
--- a/rackspace/lb/v1/ssl/fixtures.go
+++ /dev/null
@@ -1,198 +0,0 @@
-// +build fixtures
-
-package ssl
-
-import (
-	"fmt"
-	"net/http"
-	"strconv"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func _rootURL(id int) string {
-	return "/loadbalancers/" + strconv.Itoa(id) + "/ssltermination"
-}
-
-func _certURL(id, certID int) string {
-	url := _rootURL(id) + "/certificatemappings"
-	if certID > 0 {
-		url += "/" + strconv.Itoa(certID)
-	}
-	return url
-}
-
-func mockGetResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "sslTermination": {
-    "certificate": "-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-    "enabled": true,
-    "secureTrafficOnly": false,
-    "intermediateCertificate": "-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n",
-    "securePort": 443
-  }
-}
-`)
-	})
-}
-
-func mockUpdateResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "sslTermination": {
-    "enabled": true,
-    "securePort": 443,
-    "secureTrafficOnly": false,
-		"privateKey": "foo",
-		"certificate": "-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-  	"intermediateCertificate": "-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n"
-	}
-}
-    `)
-
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `{}`)
-	})
-}
-
-func mockDeleteResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusOK)
-	})
-}
-
-func mockListCertsResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_certURL(lbID, 0), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "certificateMappings": [
-    {
-      "certificateMapping": {
-        "id": 123,
-        "hostName": "rackspace.com"
-      }
-    },
-    {
-      "certificateMapping": {
-        "id": 124,
-        "hostName": "*.rackspace.com"
-      }
-    }
-  ]
-}
-`)
-	})
-}
-
-func mockAddCertResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_certURL(lbID, 0), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "certificateMapping": {
-    "hostName": "rackspace.com",
-    "privateKey":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwIudSMpRZx7TS0/AtDVX3DgXwLD9g+XrNaoazlhwhpYALgzJ\nLAbAnOxT6OT0gTpkPus/B7QhW6y6Auf2cdBeW31XoIwPsSoyNhxgErGBxzNARRB9\nlI1HCa1ojFrcULluj4W6rpaOycI5soDBJiJHin/hbZBPZq6vhPCuNP7Ya48Zd/2X\nCQ9ft3XKfmbs1SdrdROIhigse/SGRbMrCorn/vhNIuohr7yOlHG3GcVcUI9k6ZSZ\nBbqF+ZA4ApSF/Q6/cumieEgofhkYbx5fg02s9Jwr4IWnIR2bSHs7UQ6sVgKYzjs7\nPd3Unpa74jFw6/H6shABoO2CIYLotGmQbFgnpwIDAQABAoIBAQCBCQ+PCIclJHNV\ntUzfeCA5ZR4F9JbxHdRTUnxEbOB8UWotckQfTScoAvj4yvdQ42DrCZxj/UOdvFOs\nPufZvlp91bIz1alugWjE+p8n5+2hIaegoTyHoWZKBfxak0myj5KYfHZvKlbmv1ML\nXV4TwEVRfAIG+v87QTY/UUxuF5vR+BpKIbgUJLfPUFFvJUdl84qsJ44pToxaYUd/\nh5YAGC00U4ay1KVSAUnTkkPNZ0lPG/rWU6w6WcTvNRLMd8DzFLTKLOgQfHhbExAF\n+sXPWjWSzbBRP1O7fHqq96QQh4VFiY/7w9W+sDKQyV6Ul17OSXs6aZ4f+lq4rJTI\n1FG96YiBAoGBAO1tiH0h1oWDBYfJB3KJJ6CQQsDGwtHo/DEgznFVP4XwEVbZ98Ha\nBfBCn3sAybbaikyCV1Hwj7kfHMZPDHbrcUSFX7quu/2zPK+wO3lZKXSyu4YsguSa\nRedInN33PpdnlPhLyQdWSuD5sVHJDF6xn22vlyxeILH3ooLg2WOFMPmVAoGBAM+b\nUG/a7iyfpAQKYyuFAsXz6SeFaDY+ZYeX45L112H8Pu+Ie/qzon+bzLB9FIH8GP6+\nQpQgmm/p37U2gD1zChUv7iW6OfQBKk9rWvMpfRF6d7YHquElejhizfTZ+ntBV/VY\ndOYEczxhrdW7keLpatYaaWUy/VboRZmlz/9JGqVLAoGAHfqNmFc0cgk4IowEj7a3\ntTNh6ltub/i+FynwRykfazcDyXaeLPDtfQe8gVh5H8h6W+y9P9BjJVnDVVrX1RAn\nbiJ1EupLPF5sVDapW8ohTOXgfbGTGXBNUUW+4Nv+IDno+mz/RhjkPYHpnM0I7c/5\ntGzOZsC/2hjNgT8I0+MWav0CgYEAuULdJeQVlKalI6HtW2Gn1uRRVJ49H+LQkY6e\nW3+cw2jo9LI0CMWSphNvNrN3wIMp/vHj0fHCP0pSApDvIWbuQXfzKaGko7UCf7rK\nf6GvZRCHkV4IREBAb97j8bMvThxClMNqFfU0rFZyXP+0MOyhFQyertswrgQ6T+Fi\n2mnvKD8CgYAmJHP3NTDRMoMRyAzonJ6nEaGUbAgNmivTaUWMe0+leCvAdwD89gzC\nTKbm3eDUg/6Va3X6ANh3wsfIOe4RXXxcbcFDk9R4zO2M5gfLSjYm5Q87EBZ2hrdj\nM2gLI7dt6thx0J8lR8xRHBEMrVBdgwp0g1gQzo5dAV88/kpkZVps8Q==\n-----END RSA PRIVATE KEY-----\n",
-    "certificate":"-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-    "intermediateCertificate":"-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n"
-  }
-}
-		`)
-
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-	"certificateMapping": {
-		"id": 123,
-		"hostName": "rackspace.com",
-		"certificate":"-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-		"intermediateCertificate":"-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n"
-	}
-}
-			`)
-	})
-}
-
-func mockGetCertResponse(t *testing.T, lbID, certID int) {
-	th.Mux.HandleFunc(_certURL(lbID, certID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "certificateMapping": {
-    "id": 123,
-    "hostName": "rackspace.com",
-    "certificate":"-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-    "intermediateCertificate":"-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n"
-  }
-}
-`)
-	})
-}
-
-func mockUpdateCertResponse(t *testing.T, lbID, certID int) {
-	th.Mux.HandleFunc(_certURL(lbID, certID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "certificateMapping": {
-    "hostName": "rackspace.com",
-    "privateKey":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwIudSMpRZx7TS0/AtDVX3DgXwLD9g+XrNaoazlhwhpYALgzJ\nLAbAnOxT6OT0gTpkPus/B7QhW6y6Auf2cdBeW31XoIwPsSoyNhxgErGBxzNARRB9\nlI1HCa1ojFrcULluj4W6rpaOycI5soDBJiJHin/hbZBPZq6vhPCuNP7Ya48Zd/2X\nCQ9ft3XKfmbs1SdrdROIhigse/SGRbMrCorn/vhNIuohr7yOlHG3GcVcUI9k6ZSZ\nBbqF+ZA4ApSF/Q6/cumieEgofhkYbx5fg02s9Jwr4IWnIR2bSHs7UQ6sVgKYzjs7\nPd3Unpa74jFw6/H6shABoO2CIYLotGmQbFgnpwIDAQABAoIBAQCBCQ+PCIclJHNV\ntUzfeCA5ZR4F9JbxHdRTUnxEbOB8UWotckQfTScoAvj4yvdQ42DrCZxj/UOdvFOs\nPufZvlp91bIz1alugWjE+p8n5+2hIaegoTyHoWZKBfxak0myj5KYfHZvKlbmv1ML\nXV4TwEVRfAIG+v87QTY/UUxuF5vR+BpKIbgUJLfPUFFvJUdl84qsJ44pToxaYUd/\nh5YAGC00U4ay1KVSAUnTkkPNZ0lPG/rWU6w6WcTvNRLMd8DzFLTKLOgQfHhbExAF\n+sXPWjWSzbBRP1O7fHqq96QQh4VFiY/7w9W+sDKQyV6Ul17OSXs6aZ4f+lq4rJTI\n1FG96YiBAoGBAO1tiH0h1oWDBYfJB3KJJ6CQQsDGwtHo/DEgznFVP4XwEVbZ98Ha\nBfBCn3sAybbaikyCV1Hwj7kfHMZPDHbrcUSFX7quu/2zPK+wO3lZKXSyu4YsguSa\nRedInN33PpdnlPhLyQdWSuD5sVHJDF6xn22vlyxeILH3ooLg2WOFMPmVAoGBAM+b\nUG/a7iyfpAQKYyuFAsXz6SeFaDY+ZYeX45L112H8Pu+Ie/qzon+bzLB9FIH8GP6+\nQpQgmm/p37U2gD1zChUv7iW6OfQBKk9rWvMpfRF6d7YHquElejhizfTZ+ntBV/VY\ndOYEczxhrdW7keLpatYaaWUy/VboRZmlz/9JGqVLAoGAHfqNmFc0cgk4IowEj7a3\ntTNh6ltub/i+FynwRykfazcDyXaeLPDtfQe8gVh5H8h6W+y9P9BjJVnDVVrX1RAn\nbiJ1EupLPF5sVDapW8ohTOXgfbGTGXBNUUW+4Nv+IDno+mz/RhjkPYHpnM0I7c/5\ntGzOZsC/2hjNgT8I0+MWav0CgYEAuULdJeQVlKalI6HtW2Gn1uRRVJ49H+LQkY6e\nW3+cw2jo9LI0CMWSphNvNrN3wIMp/vHj0fHCP0pSApDvIWbuQXfzKaGko7UCf7rK\nf6GvZRCHkV4IREBAb97j8bMvThxClMNqFfU0rFZyXP+0MOyhFQyertswrgQ6T+Fi\n2mnvKD8CgYAmJHP3NTDRMoMRyAzonJ6nEaGUbAgNmivTaUWMe0+leCvAdwD89gzC\nTKbm3eDUg/6Va3X6ANh3wsfIOe4RXXxcbcFDk9R4zO2M5gfLSjYm5Q87EBZ2hrdj\nM2gLI7dt6thx0J8lR8xRHBEMrVBdgwp0g1gQzo5dAV88/kpkZVps8Q==\n-----END RSA PRIVATE KEY-----\n",
-    "certificate":"-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-    "intermediateCertificate":"-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n"
-  }
-}
-		`)
-
-		w.WriteHeader(http.StatusAccepted)
-
-		fmt.Fprintf(w, `
-{
-	"certificateMapping": {
-		"id": 123,
-		"hostName": "rackspace.com",
-		"certificate":"-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-		"intermediateCertificate":"-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n"
-	}
-}
-			`)
-	})
-}
-
-func mockDeleteCertResponse(t *testing.T, lbID, certID int) {
-	th.Mux.HandleFunc(_certURL(lbID, certID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusOK)
-	})
-}
diff --git a/rackspace/lb/v1/ssl/requests.go b/rackspace/lb/v1/ssl/requests.go
deleted file mode 100644
index bb53ef8..0000000
--- a/rackspace/lb/v1/ssl/requests.go
+++ /dev/null
@@ -1,247 +0,0 @@
-package ssl
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-var (
-	errPrivateKey     = errors.New("PrivateKey is a required field")
-	errCertificate    = errors.New("Certificate is a required field")
-	errIntCertificate = errors.New("IntCertificate is a required field")
-)
-
-// UpdateOptsBuilder is the interface options structs have to satisfy in order
-// to be used in the main Update operation in this package.
-type UpdateOptsBuilder interface {
-	ToSSLUpdateMap() (map[string]interface{}, error)
-}
-
-// UpdateOpts is the common options struct used in this package's Update
-// operation.
-type UpdateOpts struct {
-	// Required - consult the SSLTermConfig struct for more info.
-	SecurePort int
-
-	// Required - consult the SSLTermConfig struct for more info.
-	PrivateKey string
-
-	// Required - consult the SSLTermConfig struct for more info.
-	Certificate string
-
-	// Required - consult the SSLTermConfig struct for more info.
-	IntCertificate string
-
-	// Optional - consult the SSLTermConfig struct for more info.
-	Enabled *bool
-
-	// Optional - consult the SSLTermConfig struct for more info.
-	SecureTrafficOnly *bool
-}
-
-// ToSSLUpdateMap casts a CreateOpts struct to a map.
-func (opts UpdateOpts) ToSSLUpdateMap() (map[string]interface{}, error) {
-	ssl := make(map[string]interface{})
-
-	if opts.SecurePort == 0 {
-		return ssl, errors.New("SecurePort needs to be an integer greater than 0")
-	}
-	if opts.PrivateKey == "" {
-		return ssl, errPrivateKey
-	}
-	if opts.Certificate == "" {
-		return ssl, errCertificate
-	}
-	if opts.IntCertificate == "" {
-		return ssl, errIntCertificate
-	}
-
-	ssl["securePort"] = opts.SecurePort
-	ssl["privateKey"] = opts.PrivateKey
-	ssl["certificate"] = opts.Certificate
-	ssl["intermediateCertificate"] = opts.IntCertificate
-
-	if opts.Enabled != nil {
-		ssl["enabled"] = &opts.Enabled
-	}
-
-	if opts.SecureTrafficOnly != nil {
-		ssl["secureTrafficOnly"] = &opts.SecureTrafficOnly
-	}
-
-	return map[string]interface{}{"sslTermination": ssl}, nil
-}
-
-// Update is the operation responsible for updating the SSL Termination
-// configuration for a load balancer.
-func Update(c *gophercloud.ServiceClient, lbID int, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-
-	reqBody, err := opts.ToSSLUpdateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Put(rootURL(c, lbID), reqBody, &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-	return res
-}
-
-// Get is the operation responsible for showing the details of the SSL
-// Termination configuration for a load balancer.
-func Get(c *gophercloud.ServiceClient, lbID int) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(rootURL(c, lbID), &res.Body, nil)
-	return res
-}
-
-// Delete is the operation responsible for deleting the SSL Termination
-// configuration for a load balancer.
-func Delete(c *gophercloud.ServiceClient, lbID int) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(rootURL(c, lbID), &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-	return res
-}
-
-// ListCerts will list all of the certificate mappings associated with a
-// SSL-terminated HTTP load balancer.
-func ListCerts(c *gophercloud.ServiceClient, lbID int) pagination.Pager {
-	url := certURL(c, lbID)
-	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
-		return CertPage{pagination.LinkedPageBase{PageResult: r}}
-	})
-}
-
-// CreateCertOptsBuilder is the interface options structs have to satisfy in
-// order to be used in the AddCert operation in this package.
-type CreateCertOptsBuilder interface {
-	ToCertCreateMap() (map[string]interface{}, error)
-}
-
-// CreateCertOpts represents the options used when adding a new certificate mapping.
-type CreateCertOpts struct {
-	HostName       string
-	PrivateKey     string
-	Certificate    string
-	IntCertificate string
-}
-
-// ToCertCreateMap will cast an CreateCertOpts struct to a map for JSON serialization.
-func (opts CreateCertOpts) ToCertCreateMap() (map[string]interface{}, error) {
-	cm := make(map[string]interface{})
-
-	if opts.HostName == "" {
-		return cm, errors.New("HostName is a required option")
-	}
-	if opts.PrivateKey == "" {
-		return cm, errPrivateKey
-	}
-	if opts.Certificate == "" {
-		return cm, errCertificate
-	}
-
-	cm["hostName"] = opts.HostName
-	cm["privateKey"] = opts.PrivateKey
-	cm["certificate"] = opts.Certificate
-
-	if opts.IntCertificate != "" {
-		cm["intermediateCertificate"] = opts.IntCertificate
-	}
-
-	return map[string]interface{}{"certificateMapping": cm}, nil
-}
-
-// CreateCert will add a new SSL certificate and allow an SSL-terminated HTTP
-// load balancer to use it. This feature is useful because it allows multiple
-// certificates to be used. The maximum number of certificates that can be
-// stored per LB is 20.
-func CreateCert(c *gophercloud.ServiceClient, lbID int, opts CreateCertOptsBuilder) CreateCertResult {
-	var res CreateCertResult
-
-	reqBody, err := opts.ToCertCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Post(certURL(c, lbID), reqBody, &res.Body, &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-
-	return res
-}
-
-// GetCert will show the details of an existing SSL certificate.
-func GetCert(c *gophercloud.ServiceClient, lbID, certID int) GetCertResult {
-	var res GetCertResult
-	_, res.Err = c.Get(certResourceURL(c, lbID, certID), &res.Body, nil)
-	return res
-}
-
-// UpdateCertOptsBuilder is the interface options structs have to satisfy in
-// order to be used in the UpdateCert operation in this package.
-type UpdateCertOptsBuilder interface {
-	ToCertUpdateMap() (map[string]interface{}, error)
-}
-
-// UpdateCertOpts represents the options needed to update a SSL certificate.
-type UpdateCertOpts struct {
-	HostName       string
-	PrivateKey     string
-	Certificate    string
-	IntCertificate string
-}
-
-// ToCertUpdateMap will cast an UpdateCertOpts struct into a map for JSON
-// seralization.
-func (opts UpdateCertOpts) ToCertUpdateMap() (map[string]interface{}, error) {
-	cm := make(map[string]interface{})
-
-	if opts.HostName != "" {
-		cm["hostName"] = opts.HostName
-	}
-	if opts.PrivateKey != "" {
-		cm["privateKey"] = opts.PrivateKey
-	}
-	if opts.Certificate != "" {
-		cm["certificate"] = opts.Certificate
-	}
-	if opts.IntCertificate != "" {
-		cm["intermediateCertificate"] = opts.IntCertificate
-	}
-
-	return map[string]interface{}{"certificateMapping": cm}, nil
-}
-
-// UpdateCert is the operation responsible for updating the details of an
-// existing SSL certificate.
-func UpdateCert(c *gophercloud.ServiceClient, lbID, certID int, opts UpdateCertOptsBuilder) UpdateCertResult {
-	var res UpdateCertResult
-
-	reqBody, err := opts.ToCertUpdateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Put(certResourceURL(c, lbID, certID), reqBody, &res.Body, nil)
-	return res
-}
-
-// DeleteCert is the operation responsible for permanently removing a SSL
-// certificate.
-func DeleteCert(c *gophercloud.ServiceClient, lbID, certID int) DeleteResult {
-	var res DeleteResult
-
-	_, res.Err = c.Delete(certResourceURL(c, lbID, certID), &gophercloud.RequestOpts{
-		OkCodes: []int{200},
-	})
-
-	return res
-}
diff --git a/rackspace/lb/v1/ssl/requests_test.go b/rackspace/lb/v1/ssl/requests_test.go
deleted file mode 100644
index fb14c4a..0000000
--- a/rackspace/lb/v1/ssl/requests_test.go
+++ /dev/null
@@ -1,167 +0,0 @@
-package ssl
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const (
-	lbID   = 12345
-	certID = 67890
-)
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetResponse(t, lbID)
-
-	sp, err := Get(client.ServiceClient(), lbID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &SSLTermConfig{
-		Certificate:       "-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-		Enabled:           true,
-		SecureTrafficOnly: false,
-		IntCertificate:    "-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n",
-		SecurePort:        443,
-	}
-	th.AssertDeepEquals(t, expected, sp)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockUpdateResponse(t, lbID)
-
-	opts := UpdateOpts{
-		Enabled:           gophercloud.Enabled,
-		SecurePort:        443,
-		SecureTrafficOnly: gophercloud.Disabled,
-		PrivateKey:        "foo",
-		Certificate:       "-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-		IntCertificate:    "-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n",
-	}
-	err := Update(client.ServiceClient(), lbID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteResponse(t, lbID)
-
-	err := Delete(client.ServiceClient(), lbID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestListCerts(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListCertsResponse(t, lbID)
-
-	count := 0
-
-	err := ListCerts(client.ServiceClient(), lbID).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractCerts(page)
-		th.AssertNoErr(t, err)
-
-		expected := []Certificate{
-			Certificate{ID: 123, HostName: "rackspace.com"},
-			Certificate{ID: 124, HostName: "*.rackspace.com"},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestCreateCert(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockAddCertResponse(t, lbID)
-
-	opts := CreateCertOpts{
-		HostName:       "rackspace.com",
-		PrivateKey:     "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwIudSMpRZx7TS0/AtDVX3DgXwLD9g+XrNaoazlhwhpYALgzJ\nLAbAnOxT6OT0gTpkPus/B7QhW6y6Auf2cdBeW31XoIwPsSoyNhxgErGBxzNARRB9\nlI1HCa1ojFrcULluj4W6rpaOycI5soDBJiJHin/hbZBPZq6vhPCuNP7Ya48Zd/2X\nCQ9ft3XKfmbs1SdrdROIhigse/SGRbMrCorn/vhNIuohr7yOlHG3GcVcUI9k6ZSZ\nBbqF+ZA4ApSF/Q6/cumieEgofhkYbx5fg02s9Jwr4IWnIR2bSHs7UQ6sVgKYzjs7\nPd3Unpa74jFw6/H6shABoO2CIYLotGmQbFgnpwIDAQABAoIBAQCBCQ+PCIclJHNV\ntUzfeCA5ZR4F9JbxHdRTUnxEbOB8UWotckQfTScoAvj4yvdQ42DrCZxj/UOdvFOs\nPufZvlp91bIz1alugWjE+p8n5+2hIaegoTyHoWZKBfxak0myj5KYfHZvKlbmv1ML\nXV4TwEVRfAIG+v87QTY/UUxuF5vR+BpKIbgUJLfPUFFvJUdl84qsJ44pToxaYUd/\nh5YAGC00U4ay1KVSAUnTkkPNZ0lPG/rWU6w6WcTvNRLMd8DzFLTKLOgQfHhbExAF\n+sXPWjWSzbBRP1O7fHqq96QQh4VFiY/7w9W+sDKQyV6Ul17OSXs6aZ4f+lq4rJTI\n1FG96YiBAoGBAO1tiH0h1oWDBYfJB3KJJ6CQQsDGwtHo/DEgznFVP4XwEVbZ98Ha\nBfBCn3sAybbaikyCV1Hwj7kfHMZPDHbrcUSFX7quu/2zPK+wO3lZKXSyu4YsguSa\nRedInN33PpdnlPhLyQdWSuD5sVHJDF6xn22vlyxeILH3ooLg2WOFMPmVAoGBAM+b\nUG/a7iyfpAQKYyuFAsXz6SeFaDY+ZYeX45L112H8Pu+Ie/qzon+bzLB9FIH8GP6+\nQpQgmm/p37U2gD1zChUv7iW6OfQBKk9rWvMpfRF6d7YHquElejhizfTZ+ntBV/VY\ndOYEczxhrdW7keLpatYaaWUy/VboRZmlz/9JGqVLAoGAHfqNmFc0cgk4IowEj7a3\ntTNh6ltub/i+FynwRykfazcDyXaeLPDtfQe8gVh5H8h6W+y9P9BjJVnDVVrX1RAn\nbiJ1EupLPF5sVDapW8ohTOXgfbGTGXBNUUW+4Nv+IDno+mz/RhjkPYHpnM0I7c/5\ntGzOZsC/2hjNgT8I0+MWav0CgYEAuULdJeQVlKalI6HtW2Gn1uRRVJ49H+LQkY6e\nW3+cw2jo9LI0CMWSphNvNrN3wIMp/vHj0fHCP0pSApDvIWbuQXfzKaGko7UCf7rK\nf6GvZRCHkV4IREBAb97j8bMvThxClMNqFfU0rFZyXP+0MOyhFQyertswrgQ6T+Fi\n2mnvKD8CgYAmJHP3NTDRMoMRyAzonJ6nEaGUbAgNmivTaUWMe0+leCvAdwD89gzC\nTKbm3eDUg/6Va3X6ANh3wsfIOe4RXXxcbcFDk9R4zO2M5gfLSjYm5Q87EBZ2hrdj\nM2gLI7dt6thx0J8lR8xRHBEMrVBdgwp0g1gQzo5dAV88/kpkZVps8Q==\n-----END RSA PRIVATE KEY-----\n",
-		Certificate:    "-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-		IntCertificate: "-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n",
-	}
-
-	cm, err := CreateCert(client.ServiceClient(), lbID, opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Certificate{
-		ID:             123,
-		HostName:       "rackspace.com",
-		Certificate:    "-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-		IntCertificate: "-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n",
-	}
-	th.AssertDeepEquals(t, expected, cm)
-}
-
-func TestGetCertMapping(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetCertResponse(t, lbID, certID)
-
-	sp, err := GetCert(client.ServiceClient(), lbID, certID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Certificate{
-		ID:             123,
-		HostName:       "rackspace.com",
-		Certificate:    "-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-		IntCertificate: "-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n",
-	}
-	th.AssertDeepEquals(t, expected, sp)
-}
-
-func TestUpdateCert(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockUpdateCertResponse(t, lbID, certID)
-
-	opts := UpdateCertOpts{
-		HostName:       "rackspace.com",
-		PrivateKey:     "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwIudSMpRZx7TS0/AtDVX3DgXwLD9g+XrNaoazlhwhpYALgzJ\nLAbAnOxT6OT0gTpkPus/B7QhW6y6Auf2cdBeW31XoIwPsSoyNhxgErGBxzNARRB9\nlI1HCa1ojFrcULluj4W6rpaOycI5soDBJiJHin/hbZBPZq6vhPCuNP7Ya48Zd/2X\nCQ9ft3XKfmbs1SdrdROIhigse/SGRbMrCorn/vhNIuohr7yOlHG3GcVcUI9k6ZSZ\nBbqF+ZA4ApSF/Q6/cumieEgofhkYbx5fg02s9Jwr4IWnIR2bSHs7UQ6sVgKYzjs7\nPd3Unpa74jFw6/H6shABoO2CIYLotGmQbFgnpwIDAQABAoIBAQCBCQ+PCIclJHNV\ntUzfeCA5ZR4F9JbxHdRTUnxEbOB8UWotckQfTScoAvj4yvdQ42DrCZxj/UOdvFOs\nPufZvlp91bIz1alugWjE+p8n5+2hIaegoTyHoWZKBfxak0myj5KYfHZvKlbmv1ML\nXV4TwEVRfAIG+v87QTY/UUxuF5vR+BpKIbgUJLfPUFFvJUdl84qsJ44pToxaYUd/\nh5YAGC00U4ay1KVSAUnTkkPNZ0lPG/rWU6w6WcTvNRLMd8DzFLTKLOgQfHhbExAF\n+sXPWjWSzbBRP1O7fHqq96QQh4VFiY/7w9W+sDKQyV6Ul17OSXs6aZ4f+lq4rJTI\n1FG96YiBAoGBAO1tiH0h1oWDBYfJB3KJJ6CQQsDGwtHo/DEgznFVP4XwEVbZ98Ha\nBfBCn3sAybbaikyCV1Hwj7kfHMZPDHbrcUSFX7quu/2zPK+wO3lZKXSyu4YsguSa\nRedInN33PpdnlPhLyQdWSuD5sVHJDF6xn22vlyxeILH3ooLg2WOFMPmVAoGBAM+b\nUG/a7iyfpAQKYyuFAsXz6SeFaDY+ZYeX45L112H8Pu+Ie/qzon+bzLB9FIH8GP6+\nQpQgmm/p37U2gD1zChUv7iW6OfQBKk9rWvMpfRF6d7YHquElejhizfTZ+ntBV/VY\ndOYEczxhrdW7keLpatYaaWUy/VboRZmlz/9JGqVLAoGAHfqNmFc0cgk4IowEj7a3\ntTNh6ltub/i+FynwRykfazcDyXaeLPDtfQe8gVh5H8h6W+y9P9BjJVnDVVrX1RAn\nbiJ1EupLPF5sVDapW8ohTOXgfbGTGXBNUUW+4Nv+IDno+mz/RhjkPYHpnM0I7c/5\ntGzOZsC/2hjNgT8I0+MWav0CgYEAuULdJeQVlKalI6HtW2Gn1uRRVJ49H+LQkY6e\nW3+cw2jo9LI0CMWSphNvNrN3wIMp/vHj0fHCP0pSApDvIWbuQXfzKaGko7UCf7rK\nf6GvZRCHkV4IREBAb97j8bMvThxClMNqFfU0rFZyXP+0MOyhFQyertswrgQ6T+Fi\n2mnvKD8CgYAmJHP3NTDRMoMRyAzonJ6nEaGUbAgNmivTaUWMe0+leCvAdwD89gzC\nTKbm3eDUg/6Va3X6ANh3wsfIOe4RXXxcbcFDk9R4zO2M5gfLSjYm5Q87EBZ2hrdj\nM2gLI7dt6thx0J8lR8xRHBEMrVBdgwp0g1gQzo5dAV88/kpkZVps8Q==\n-----END RSA PRIVATE KEY-----\n",
-		Certificate:    "-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-		IntCertificate: "-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n",
-	}
-
-	cm, err := UpdateCert(client.ServiceClient(), lbID, certID, opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &Certificate{
-		ID:             123,
-		HostName:       "rackspace.com",
-		Certificate:    "-----BEGIN CERTIFICATE-----\nMIIEXTCCA0WgAwIBAgIGATTEAjK3MA0GCSqGSIb3DQEBBQUAMIGDMRkwFwYDVQQD\nExBUZXN0IENBIFNUdWIgS2V5MRcwFQYDVQQLEw5QbGF0Zm9ybSBMYmFhczEaMBgG\nA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFDASBgNVBAcTC1NhbiBBbnRvbmlvMQ4w\nDAYDVQQIEwVUZXhhczELMAkGA1UEBhMCVVMwHhcNMTIwMTA5MTk0NjQ1WhcNMTQw\nMTA4MTk0NjQ1WjCBgjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMRQwEgYD\nVQQHEwtTYW4gQW50b25pbzEaMBgGA1UEChMRUmFja3NwYWNlIEhvc3RpbmcxFzAV\nBgNVBAsTDlBsYXRmb3JtIExiYWFzMRgwFgYDVQQDEw9UZXN0IENsaWVudCBLZXkw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAi51IylFnHtNLT8C0NVfc\nOBfAsP2D5es1qhrOWHCGlgAuDMksBsCc7FPo5PSBOmQ+6z8HtCFbrLoC5/Zx0F5b\nfVegjA+xKjI2HGASsYHHM0BFEH2UjUcJrWiMWtxQuW6Phbqulo7JwjmygMEmIkeK\nf+FtkE9mrq+E8K40/thrjxl3/ZcJD1+3dcp+ZuzVJ2t1E4iGKCx79IZFsysKiuf+\n+E0i6iGvvI6UcbcZxVxQj2TplJkFuoX5kDgClIX9Dr9y6aJ4SCh+GRhvHl+DTaz0\nnCvghachHZtIeztRDqxWApjOOzs93dSelrviMXDr8fqyEAGg7YIhgui0aZBsWCen\nAgMBAAGjgdUwgdIwgbAGA1UdIwSBqDCBpYAUNpx1Pc6cGA7KqEwHMmHBTZMA7lSh\ngYmkgYYwgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVU4IBATAd\nBgNVHQ4EFgQULueOfsjZZOHwJHZwBy6u0swnpccwDQYJKoZIhvcNAQEFBQADggEB\nAFNuqSVUaotUJoWDv4z7Kbi6JFpTjDht5ORw4BdVYlRD4h9DACAFzPrPV2ym/Osp\nhNMdZq6msZku7MdOSQVhdeGWrSNk3M8O9Hg7cVzPNXOF3iNoo3irQ5tURut44xs4\nWw5YWQqS9WyUY5snD8tm7Y1rQTPfhg+678xIq/zWCv/u+FSnfVv1nlhLVQkEeG/Y\ngh1uMaTIpUKTGEjIAGtpGP7wwIcXptR/HyfzhTUSTaWc1Ef7zoKT9LL5z3IV1hC2\njVWz+RwYs98LjMuksJFoHqRfWyYhCIym0jb6GTwaEmpxAjc+d7OLNQdnoEGoUYGP\nYjtfkRYg265ESMA+Kww4Xy8=\n-----END CERTIFICATE-----\n",
-		IntCertificate: "-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzEZMBcGA1UEAxMQVGVz\ndCBDQSBTVHViIEtleTEXMBUGA1UECxMOUGxhdGZvcm0gTGJhYXMxGjAYBgNVBAoT\nEVJhY2tzcGFjZSBIb3N0aW5nMRQwEgYDVQQHEwtTYW4gQW50b25pbzEOMAwGA1UE\nCBMFVGV4YXMxCzAJBgNVBAYTAlVTMB4XDTEyMDEwOTE5NDU0OVoXDTE0MDEwODE5\nNDU0OVowgYMxGTAXBgNVBAMTEFRlc3QgQ0EgU1R1YiBLZXkxFzAVBgNVBAsTDlBs\nYXRmb3JtIExiYWFzMRowGAYDVQQKExFSYWNrc3BhY2UgSG9zdGluZzEUMBIGA1UE\nBxMLU2FuIEFudG9uaW8xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNh55lwTVwQvNoEZjq1zGdYz9jA\nXXdjizn8AJhjHLOAallfPtvCfTEgKanhdoyz5FnhQE8HbDAop/KNS1lN2UMvdl5f\nZNLTSjJrNtedqxQwxN/i3bpyBxNVejUH2NjV1mmyj+5CJYwCzWalvI/gLPq/A3as\nO2EQqtf3U8unRgn0zXLRdYxV9MrUzNAmdipPNvNrsVdrCgA42rgF/8KsyRVQfJCX\nfN7PGCfrsC3YaUvhymraWxNnXIzMYTNa9wEeBZLUw8SlEtpa1Zsvui+TPXu3USNZ\nVnWH8Lb6ENlnoX0VBwo62fjOG3JzhNKoJawi3bRqyDdINOvafr7iPrrs/T8CAwEA\nAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNpx1Pc6cGA7KqEwHMmHB\nTZMA7lQwDQYJKoZIhvcNAQEFBQADggEBAMoRgH3iTG3t317viLKoY+lNMHUgHuR7\nb3mn9MidJKyYVewe6hCDIN6WY4fUojmMW9wFJWJIo0hRMNHL3n3tq8HP2j20Mxy8\nacPdfGZJa+jiBw72CrIGdobKaFduIlIEDBA1pNdZIJ+EulrtqrMesnIt92WaypIS\n8JycbIgDMCiyC0ENHEk8UWlC6429c7OZAsplMTbHME/1R4btxjkdfrYZJjdJ2yL2\n8cjZDUDMCPTdW/ycP07Gkq30RB5tACB5aZdaCn2YaKC8FsEdhff4X7xEOfOEHWEq\nSRxADDj8Lx1MT6QpR07hCiDyHfTCtbqzI0iGjX63Oh7xXSa0f+JVTa8=\n-----END CERTIFICATE-----\n",
-	}
-	th.AssertDeepEquals(t, expected, cm)
-}
-
-func TestDeleteCert(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteCertResponse(t, lbID, certID)
-
-	err := DeleteCert(client.ServiceClient(), lbID, certID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/lb/v1/ssl/results.go b/rackspace/lb/v1/ssl/results.go
deleted file mode 100644
index ead9fcd..0000000
--- a/rackspace/lb/v1/ssl/results.go
+++ /dev/null
@@ -1,148 +0,0 @@
-package ssl
-
-import (
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// SSLTermConfig represents the SSL configuration for a particular load balancer.
-type SSLTermConfig struct {
-	// The port on which the SSL termination load balancer listens for secure
-	// traffic. The value must be unique to the existing LB protocol/port
-	// combination
-	SecurePort int `mapstructure:"securePort"`
-
-	// The private key for the SSL certificate which is validated and verified
-	// against the provided certificates.
-	PrivateKey string `mapstructure:"privatekey"`
-
-	// The certificate used for SSL termination, which is validated and verified
-	// against the key and intermediate certificate if provided.
-	Certificate string
-
-	// The intermediate certificate (for the user). The intermediate certificate
-	// is validated and verified against the key and certificate credentials
-	// provided. A user may only provide this value when accompanied by a
-	// Certificate, PrivateKey, and SecurePort. It may not be added or updated as
-	// a single attribute in a future operation.
-	IntCertificate string `mapstructure:"intermediatecertificate"`
-
-	// Determines if the load balancer is enabled to terminate SSL traffic or not.
-	// If this is set to false, the load balancer retains its specified SSL
-	// attributes but does not terminate SSL traffic.
-	Enabled bool
-
-	// Determines if the load balancer can only accept secure traffic. If set to
-	// true, the load balancer will not accept non-secure traffic.
-	SecureTrafficOnly bool
-}
-
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-// UpdateResult represents the result of an update operation.
-type UpdateResult struct {
-	gophercloud.ErrResult
-}
-
-// GetResult represents the result of a get operation.
-type GetResult struct {
-	gophercloud.Result
-}
-
-// Extract interprets a GetResult as a SSLTermConfig struct, if possible.
-func (r GetResult) Extract() (*SSLTermConfig, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		SSL SSLTermConfig `mapstructure:"sslTermination"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.SSL, err
-}
-
-// Certificate represents an SSL certificate associated with an SSL-terminated
-// HTTP load balancer.
-type Certificate struct {
-	ID             int
-	HostName       string
-	Certificate    string
-	IntCertificate string `mapstructure:"intermediateCertificate"`
-}
-
-// CertPage represents a page of certificates.
-type CertPage struct {
-	pagination.LinkedPageBase
-}
-
-// IsEmpty checks whether a CertMappingPage struct is empty.
-func (p CertPage) IsEmpty() (bool, error) {
-	is, err := ExtractCerts(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
-}
-
-// ExtractCerts accepts a Page struct, specifically a CertPage struct, and
-// extracts the elements into a slice of Cert structs. In other words, a generic
-// collection is mapped into a relevant slice.
-func ExtractCerts(page pagination.Page) ([]Certificate, error) {
-	type NestedMap struct {
-		Cert Certificate `mapstructure:"certificateMapping" json:"certificateMapping"`
-	}
-	var resp struct {
-		Certs []NestedMap `mapstructure:"certificateMappings" json:"certificateMappings"`
-	}
-
-	err := mapstructure.Decode(page.(CertPage).Body, &resp)
-
-	slice := []Certificate{}
-	for _, cert := range resp.Certs {
-		slice = append(slice, cert.Cert)
-	}
-
-	return slice, err
-}
-
-type certResult struct {
-	gophercloud.Result
-}
-
-// Extract interprets a result as a CertMapping struct, if possible.
-func (r certResult) Extract() (*Certificate, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		Cert Certificate `mapstructure:"certificateMapping"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.Cert, err
-}
-
-// CreateCertResult represents the result of an CreateCert operation.
-type CreateCertResult struct {
-	certResult
-}
-
-// GetCertResult represents the result of a GetCert operation.
-type GetCertResult struct {
-	certResult
-}
-
-// UpdateCertResult represents the result of an UpdateCert operation.
-type UpdateCertResult struct {
-	certResult
-}
diff --git a/rackspace/lb/v1/ssl/urls.go b/rackspace/lb/v1/ssl/urls.go
deleted file mode 100644
index aa814b3..0000000
--- a/rackspace/lb/v1/ssl/urls.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package ssl
-
-import (
-	"strconv"
-
-	"github.com/rackspace/gophercloud"
-)
-
-const (
-	path     = "loadbalancers"
-	sslPath  = "ssltermination"
-	certPath = "certificatemappings"
-)
-
-func rootURL(c *gophercloud.ServiceClient, id int) string {
-	return c.ServiceURL(path, strconv.Itoa(id), sslPath)
-}
-
-func certURL(c *gophercloud.ServiceClient, id int) string {
-	return c.ServiceURL(path, strconv.Itoa(id), sslPath, certPath)
-}
-
-func certResourceURL(c *gophercloud.ServiceClient, id, certID int) string {
-	return c.ServiceURL(path, strconv.Itoa(id), sslPath, certPath, strconv.Itoa(certID))
-}
diff --git a/rackspace/lb/v1/throttle/doc.go b/rackspace/lb/v1/throttle/doc.go
deleted file mode 100644
index 1ed605d..0000000
--- a/rackspace/lb/v1/throttle/doc.go
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
-Package throttle provides information and interaction with the Connection
-Throttling feature of the Rackspace Cloud Load Balancer service.
-*/
-package throttle
diff --git a/rackspace/lb/v1/throttle/fixtures.go b/rackspace/lb/v1/throttle/fixtures.go
deleted file mode 100644
index 61e601c..0000000
--- a/rackspace/lb/v1/throttle/fixtures.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// +build fixtures
-
-package throttle
-
-import (
-	"fmt"
-	"net/http"
-	"strconv"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func _rootURL(id int) string {
-	return "/loadbalancers/" + strconv.Itoa(id) + "/connectionthrottle"
-}
-
-func mockGetResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "connectionThrottle": {
-    "maxConnections": 100
-  }
-}
-`)
-	})
-}
-
-func mockCreateResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-  "connectionThrottle": {
-    "maxConnectionRate": 0,
-    "maxConnections": 200,
-    "minConnections": 0,
-    "rateInterval": 0
-  }
-}
-    `)
-
-		w.WriteHeader(http.StatusAccepted)
-		fmt.Fprintf(w, `{}`)
-	})
-}
-
-func mockDeleteResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/rackspace/lb/v1/throttle/requests.go b/rackspace/lb/v1/throttle/requests.go
deleted file mode 100644
index 0446b97..0000000
--- a/rackspace/lb/v1/throttle/requests.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package throttle
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-)
-
-// CreateOptsBuilder is the interface options structs have to satisfy in order
-// to be used in the main Create operation in this package.
-type CreateOptsBuilder interface {
-	ToCTCreateMap() (map[string]interface{}, error)
-}
-
-// CreateOpts is the common options struct used in this package's Create
-// operation.
-type CreateOpts struct {
-	// Required - the maximum amount of connections per IP address to allow per LB.
-	MaxConnections int
-
-	// Deprecated as of v1.22.
-	MaxConnectionRate int
-
-	// Deprecated as of v1.22.
-	MinConnections int
-
-	// Deprecated as of v1.22.
-	RateInterval int
-}
-
-// ToCTCreateMap casts a CreateOpts struct to a map.
-func (opts CreateOpts) ToCTCreateMap() (map[string]interface{}, error) {
-	ct := make(map[string]interface{})
-
-	if opts.MaxConnections < 0 || opts.MaxConnections > 100000 {
-		return ct, errors.New("MaxConnections must be an int between 0 and 100000")
-	}
-
-	ct["maxConnections"] = opts.MaxConnections
-	ct["maxConnectionRate"] = opts.MaxConnectionRate
-	ct["minConnections"] = opts.MinConnections
-	ct["rateInterval"] = opts.RateInterval
-
-	return map[string]interface{}{"connectionThrottle": ct}, nil
-}
-
-// Create is the operation responsible for creating or updating the connection
-// throttling configuration for a load balancer.
-func Create(c *gophercloud.ServiceClient, lbID int, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToCTCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Put(rootURL(c, lbID), reqBody, &res.Body, nil)
-	return res
-}
-
-// Get is the operation responsible for showing the details of the connection
-// throttling configuration for a load balancer.
-func Get(c *gophercloud.ServiceClient, lbID int) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(rootURL(c, lbID), &res.Body, nil)
-	return res
-}
-
-// Delete is the operation responsible for deleting the connection throttling
-// configuration for a load balancer.
-func Delete(c *gophercloud.ServiceClient, lbID int) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(rootURL(c, lbID), nil)
-	return res
-}
diff --git a/rackspace/lb/v1/throttle/requests_test.go b/rackspace/lb/v1/throttle/requests_test.go
deleted file mode 100644
index 6e9703f..0000000
--- a/rackspace/lb/v1/throttle/requests_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package throttle
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const lbID = 12345
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockCreateResponse(t, lbID)
-
-	opts := CreateOpts{MaxConnections: 200}
-	err := Create(client.ServiceClient(), lbID, opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockGetResponse(t, lbID)
-
-	sp, err := Get(client.ServiceClient(), lbID).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &ConnectionThrottle{MaxConnections: 100}
-	th.AssertDeepEquals(t, expected, sp)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteResponse(t, lbID)
-
-	err := Delete(client.ServiceClient(), lbID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/lb/v1/throttle/results.go b/rackspace/lb/v1/throttle/results.go
deleted file mode 100644
index db93c6f..0000000
--- a/rackspace/lb/v1/throttle/results.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package throttle
-
-import (
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-)
-
-// ConnectionThrottle represents the connection throttle configuration for a
-// particular load balancer.
-type ConnectionThrottle struct {
-	MaxConnections int
-}
-
-// CreateResult represents the result of a create operation.
-type CreateResult struct {
-	gophercloud.ErrResult
-}
-
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
-
-// GetResult represents the result of a get operation.
-type GetResult struct {
-	gophercloud.Result
-}
-
-// Extract interprets a GetResult as a SP, if possible.
-func (r GetResult) Extract() (*ConnectionThrottle, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	var response struct {
-		CT ConnectionThrottle `mapstructure:"connectionThrottle"`
-	}
-
-	err := mapstructure.Decode(r.Body, &response)
-
-	return &response.CT, err
-}
diff --git a/rackspace/lb/v1/throttle/urls.go b/rackspace/lb/v1/throttle/urls.go
deleted file mode 100644
index b77f0ac..0000000
--- a/rackspace/lb/v1/throttle/urls.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package throttle
-
-import (
-	"strconv"
-
-	"github.com/rackspace/gophercloud"
-)
-
-const (
-	path   = "loadbalancers"
-	ctPath = "connectionthrottle"
-)
-
-func rootURL(c *gophercloud.ServiceClient, id int) string {
-	return c.ServiceURL(path, strconv.Itoa(id), ctPath)
-}
diff --git a/rackspace/lb/v1/vips/doc.go b/rackspace/lb/v1/vips/doc.go
deleted file mode 100644
index 5c3846d..0000000
--- a/rackspace/lb/v1/vips/doc.go
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
-Package vips provides information and interaction with the Virtual IP API
-resource for the Rackspace Cloud Load Balancer service.
-
-A virtual IP (VIP) makes a load balancer accessible by clients. The load
-balancing service supports either a public VIP, routable on the public Internet,
-or a ServiceNet address, routable only within the region in which the load
-balancer resides.
-
-All load balancers must have at least one virtual IP associated with them at
-all times.
-*/
-package vips
diff --git a/rackspace/lb/v1/vips/fixtures.go b/rackspace/lb/v1/vips/fixtures.go
deleted file mode 100644
index 080e617..0000000
--- a/rackspace/lb/v1/vips/fixtures.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// +build fixtures
-
-package vips
-
-import (
-	"fmt"
-	"net/http"
-	"strconv"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func _rootURL(lbID int) string {
-	return "/loadbalancers/" + strconv.Itoa(lbID) + "/virtualips"
-}
-
-func mockListResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "virtualIps": [
-    {
-      "id": 1000,
-      "address": "206.10.10.210",
-      "type": "PUBLIC"
-    }
-  ]
-}
-  `)
-	})
-}
-
-func mockCreateResponse(t *testing.T, lbID int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		th.TestJSONRequest(t, r, `
-{
-    "type":"PUBLIC",
-    "ipVersion":"IPV6"
-}
-    `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-
-		fmt.Fprintf(w, `
-{
-    "address":"fd24:f480:ce44:91bc:1af2:15ff:0000:0002",
-    "id":9000134,
-    "type":"PUBLIC",
-    "ipVersion":"IPV6"
-}
-  `)
-	})
-}
-
-func mockBatchDeleteResponse(t *testing.T, lbID int, ids []int) {
-	th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		r.ParseForm()
-
-		for k, v := range ids {
-			fids := r.Form["id"]
-			th.AssertEquals(t, strconv.Itoa(v), fids[k])
-		}
-
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
-
-func mockDeleteResponse(t *testing.T, lbID, vipID int) {
-	url := _rootURL(lbID) + "/" + strconv.Itoa(vipID)
-	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusAccepted)
-	})
-}
diff --git a/rackspace/lb/v1/vips/requests.go b/rackspace/lb/v1/vips/requests.go
deleted file mode 100644
index 2bc924f..0000000
--- a/rackspace/lb/v1/vips/requests.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package vips
-
-import (
-	"errors"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List is the operation responsible for returning a paginated collection of
-// load balancer virtual IP addresses.
-func List(client *gophercloud.ServiceClient, loadBalancerID int) pagination.Pager {
-	url := rootURL(client, loadBalancerID)
-	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
-		return VIPPage{pagination.SinglePageBase(r)}
-	})
-}
-
-// CreateOptsBuilder is the interface options structs have to satisfy in order
-// to be used in the main Create operation in this package. Since many
-// extensions decorate or modify the common logic, it is useful for them to
-// satisfy a basic interface in order for them to be used.
-type CreateOptsBuilder interface {
-	ToVIPCreateMap() (map[string]interface{}, error)
-}
-
-// CreateOpts is the common options struct used in this package's Create
-// operation.
-type CreateOpts struct {
-	// Optional - the ID of an existing virtual IP. By doing this, you are
-	// allowing load balancers to share IPV6 addresses.
-	ID string
-
-	// Optional - the type of address.
-	Type Type
-
-	// Optional - the version of address.
-	Version Version
-}
-
-// ToVIPCreateMap casts a CreateOpts struct to a map.
-func (opts CreateOpts) ToVIPCreateMap() (map[string]interface{}, error) {
-	lb := make(map[string]interface{})
-
-	if opts.ID != "" {
-		lb["id"] = opts.ID
-	}
-	if opts.Type != "" {
-		lb["type"] = opts.Type
-	}
-	if opts.Version != "" {
-		lb["ipVersion"] = opts.Version
-	}
-
-	return lb, nil
-}
-
-// Create is the operation responsible for assigning a new Virtual IP to an
-// existing load balancer resource. Currently, only version 6 IP addresses may
-// be added.
-func Create(c *gophercloud.ServiceClient, lbID int, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToVIPCreateMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Post(rootURL(c, lbID), reqBody, &res.Body, nil)
-	return res
-}
-
-// BulkDelete is the operation responsible for batch deleting multiple VIPs in
-// a single operation. It accepts a slice of integer IDs and will remove them
-// from the load balancer. The maximum limit is 10 VIP removals at once.
-func BulkDelete(c *gophercloud.ServiceClient, loadBalancerID int, vipIDs []int) DeleteResult {
-	var res DeleteResult
-
-	if len(vipIDs) > 10 || len(vipIDs) == 0 {
-		res.Err = errors.New("You must provide a minimum of 1 and a maximum of 10 VIP IDs")
-		return res
-	}
-
-	url := rootURL(c, loadBalancerID)
-	url += gophercloud.IDSliceToQueryString("id", vipIDs)
-
-	_, res.Err = c.Delete(url, nil)
-	return res
-}
-
-// Delete is the operation responsible for permanently deleting a VIP.
-func Delete(c *gophercloud.ServiceClient, lbID, vipID int) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(resourceURL(c, lbID, vipID), nil)
-	return res
-}
diff --git a/rackspace/lb/v1/vips/requests_test.go b/rackspace/lb/v1/vips/requests_test.go
deleted file mode 100644
index 74ac461..0000000
--- a/rackspace/lb/v1/vips/requests_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package vips
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const (
-	lbID   = 12345
-	vipID  = 67890
-	vipID2 = 67891
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockListResponse(t, lbID)
-
-	count := 0
-
-	err := List(client.ServiceClient(), lbID).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractVIPs(page)
-		th.AssertNoErr(t, err)
-
-		expected := []VIP{
-			VIP{ID: 1000, Address: "206.10.10.210", Type: "PUBLIC"},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, 1, count)
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockCreateResponse(t, lbID)
-
-	opts := CreateOpts{
-		Type:    "PUBLIC",
-		Version: "IPV6",
-	}
-
-	vip, err := Create(client.ServiceClient(), lbID, opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := &VIP{
-		Address: "fd24:f480:ce44:91bc:1af2:15ff:0000:0002",
-		ID:      9000134,
-		Type:    "PUBLIC",
-		Version: "IPV6",
-	}
-
-	th.CheckDeepEquals(t, expected, vip)
-}
-
-func TestBulkDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	ids := []int{vipID, vipID2}
-
-	mockBatchDeleteResponse(t, lbID, ids)
-
-	err := BulkDelete(client.ServiceClient(), lbID, ids).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	mockDeleteResponse(t, lbID, vipID)
-
-	err := Delete(client.ServiceClient(), lbID, vipID).ExtractErr()
-	th.AssertNoErr(t, err)
-}
diff --git a/rackspace/lb/v1/vips/results.go b/rackspace/lb/v1/vips/results.go
deleted file mode 100644
index 678b2af..0000000
--- a/rackspace/lb/v1/vips/results.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package vips
-
-import (
-	"github.com/mitchellh/mapstructure"
-
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// VIP represents a Virtual IP API resource.
-type VIP struct {
-	Address string  `json:"address,omitempty"`
-	ID      int     `json:"id,omitempty"`
-	Type    Type    `json:"type,omitempty"`
-	Version Version `json:"ipVersion,omitempty" mapstructure:"ipVersion"`
-}
-
-// Version represents the version of a VIP.
-type Version string
-
-// Convenient constants to use for type
-const (
-	IPV4 Version = "IPV4"
-	IPV6 Version = "IPV6"
-)
-
-// Type represents the type of a VIP.
-type Type string
-
-const (
-	// PUBLIC indicates a VIP type that is routable on the public Internet.
-	PUBLIC Type = "PUBLIC"
-
-	// PRIVATE indicates a VIP type that is routable only on ServiceNet.
-	PRIVATE Type = "SERVICENET"
-)
-
-// VIPPage is the page returned by a pager when traversing over a collection
-// of VIPs.
-type VIPPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty checks whether a VIPPage struct is empty.
-func (p VIPPage) IsEmpty() (bool, error) {
-	is, err := ExtractVIPs(p)
-	if err != nil {
-		return true, nil
-	}
-	return len(is) == 0, nil
-}
-
-// ExtractVIPs accepts a Page struct, specifically a VIPPage struct, and
-// extracts the elements into a slice of VIP structs. In other words, a
-// generic collection is mapped into a relevant slice.
-func ExtractVIPs(page pagination.Page) ([]VIP, error) {
-	var resp struct {
-		VIPs []VIP `mapstructure:"virtualIps" json:"virtualIps"`
-	}
-
-	err := mapstructure.Decode(page.(VIPPage).Body, &resp)
-
-	return resp.VIPs, err
-}
-
-type commonResult struct {
-	gophercloud.Result
-}
-
-func (r commonResult) Extract() (*VIP, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-
-	resp := &VIP{}
-	err := mapstructure.Decode(r.Body, resp)
-
-	return resp, err
-}
-
-// CreateResult represents the result of a create operation.
-type CreateResult struct {
-	commonResult
-}
-
-// DeleteResult represents the result of a delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
diff --git a/rackspace/lb/v1/vips/urls.go b/rackspace/lb/v1/vips/urls.go
deleted file mode 100644
index 28f063a..0000000
--- a/rackspace/lb/v1/vips/urls.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package vips
-
-import (
-	"strconv"
-
-	"github.com/rackspace/gophercloud"
-)
-
-const (
-	lbPath  = "loadbalancers"
-	vipPath = "virtualips"
-)
-
-func resourceURL(c *gophercloud.ServiceClient, lbID, nodeID int) string {
-	return c.ServiceURL(lbPath, strconv.Itoa(lbID), vipPath, strconv.Itoa(nodeID))
-}
-
-func rootURL(c *gophercloud.ServiceClient, lbID int) string {
-	return c.ServiceURL(lbPath, strconv.Itoa(lbID), vipPath)
-}
diff --git a/rackspace/networking/v2/common/common_tests.go b/rackspace/networking/v2/common/common_tests.go
deleted file mode 100644
index 129cd63..0000000
--- a/rackspace/networking/v2/common/common_tests.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package common
-
-import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/testhelper/client"
-)
-
-const TokenID = client.TokenID
-
-func ServiceClient() *gophercloud.ServiceClient {
-	return client.ServiceClient()
-}
diff --git a/rackspace/networking/v2/networks/delegate.go b/rackspace/networking/v2/networks/delegate.go
deleted file mode 100644
index dcb0855..0000000
--- a/rackspace/networking/v2/networks/delegate.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package networks
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a Pager which allows you to iterate over a collection of
-// networks. It accepts a ListOpts struct, which allows you to filter and sort
-// the returned collection for greater efficiency.
-func List(c *gophercloud.ServiceClient, opts os.ListOptsBuilder) pagination.Pager {
-	return os.List(c, opts)
-}
-
-// Get retrieves a specific network based on its unique ID.
-func Get(c *gophercloud.ServiceClient, networkID string) os.GetResult {
-	return os.Get(c, networkID)
-}
-
-// Create accepts a CreateOpts struct and creates a new network using the values
-// provided. This operation does not actually require a request body, i.e. the
-// CreateOpts struct argument can be empty.
-//
-// The tenant ID that is contained in the URI is the tenant that creates the
-// network. An admin user, however, has the option of specifying another tenant
-// ID in the CreateOpts struct.
-func Create(c *gophercloud.ServiceClient, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(c, opts)
-}
-
-// Update accepts a UpdateOpts struct and updates an existing network using the
-// values provided. For more information, see the Create function.
-func Update(c *gophercloud.ServiceClient, networkID string, opts os.UpdateOptsBuilder) os.UpdateResult {
-	return os.Update(c, networkID, opts)
-}
-
-// Delete accepts a unique ID and deletes the network associated with it.
-func Delete(c *gophercloud.ServiceClient, networkID string) os.DeleteResult {
-	return os.Delete(c, networkID)
-}
diff --git a/rackspace/networking/v2/networks/delegate_test.go b/rackspace/networking/v2/networks/delegate_test.go
deleted file mode 100644
index 0b3a6b1..0000000
--- a/rackspace/networking/v2/networks/delegate_test.go
+++ /dev/null
@@ -1,285 +0,0 @@
-package networks
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
-	"github.com/rackspace/gophercloud/pagination"
-	fake "github.com/rackspace/gophercloud/rackspace/networking/v2/common"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "networks": [
-        {
-            "status": "ACTIVE",
-            "subnets": [
-                "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-            ],
-            "name": "private-network",
-            "admin_state_up": true,
-            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-            "shared": true,
-            "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
-        },
-        {
-            "status": "ACTIVE",
-            "subnets": [
-                "08eae331-0402-425a-923c-34f7cfe39c1b"
-            ],
-            "name": "private",
-            "admin_state_up": true,
-            "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
-            "shared": true,
-            "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324"
-        }
-    ]
-}
-      `)
-	})
-
-	client := fake.ServiceClient()
-	count := 0
-
-	List(client, os.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractNetworks(page)
-		if err != nil {
-			t.Errorf("Failed to extract networks: %v", err)
-			return false, err
-		}
-
-		expected := []os.Network{
-			os.Network{
-				Status:       "ACTIVE",
-				Subnets:      []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"},
-				Name:         "private-network",
-				AdminStateUp: true,
-				TenantID:     "4fd44f30292945e481c7b8a0c8908869",
-				Shared:       true,
-				ID:           "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-			},
-			os.Network{
-				Status:       "ACTIVE",
-				Subnets:      []string{"08eae331-0402-425a-923c-34f7cfe39c1b"},
-				Name:         "private",
-				AdminStateUp: true,
-				TenantID:     "26a7980765d0414dbc1fc1f88cdb7e6e",
-				Shared:       true,
-				ID:           "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-		"network": {
-				"status": "ACTIVE",
-				"subnets": [
-						"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-				],
-				"name": "private-network",
-				"admin_state_up": true,
-				"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-				"shared": true,
-				"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
-		}
-}
-			`)
-	})
-
-	n, err := Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertDeepEquals(t, n.Subnets, []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"})
-	th.AssertEquals(t, n.Name, "private-network")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869")
-	th.AssertEquals(t, n.Shared, true)
-	th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-		"network": {
-				"name": "sample_network",
-				"admin_state_up": true
-		}
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-		"network": {
-				"status": "ACTIVE",
-				"subnets": [],
-				"name": "net1",
-				"admin_state_up": true,
-				"tenant_id": "9bacb3c5d39d41a79512987f338cf177",
-				"shared": false,
-				"id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c"
-		}
-}
-		`)
-	})
-
-	iTrue := true
-	options := os.CreateOpts{Name: "sample_network", AdminStateUp: &iTrue}
-	n, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertDeepEquals(t, n.Subnets, []string{})
-	th.AssertEquals(t, n.Name, "net1")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.TenantID, "9bacb3c5d39d41a79512987f338cf177")
-	th.AssertEquals(t, n.Shared, false)
-	th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
-}
-
-func TestCreateWithOptionalFields(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-	"network": {
-			"name": "sample_network",
-			"admin_state_up": true,
-			"shared": true,
-			"tenant_id": "12345"
-	}
-}
-		`)
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, `
-{
-	"network": {
-			"name": "sample_network",
-			"admin_state_up": true,
-			"shared": true,
-			"tenant_id": "12345"
-	}
-}
-		`)
-	})
-
-	iTrue := true
-	options := os.CreateOpts{Name: "sample_network", AdminStateUp: &iTrue, Shared: &iTrue, TenantID: "12345"}
-	_, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-		"network": {
-				"name": "new_network_name",
-				"admin_state_up": false,
-				"shared": true
-		}
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-		"network": {
-				"status": "ACTIVE",
-				"subnets": [],
-				"name": "new_network_name",
-				"admin_state_up": false,
-				"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-				"shared": true,
-				"id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c"
-		}
-}
-		`)
-	})
-
-	iTrue := true
-	options := os.UpdateOpts{Name: "new_network_name", AdminStateUp: os.Down, Shared: &iTrue}
-	n, err := Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Name, "new_network_name")
-	th.AssertEquals(t, n.AdminStateUp, false)
-	th.AssertEquals(t, n.Shared, true)
-	th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/rackspace/networking/v2/ports/delegate.go b/rackspace/networking/v2/ports/delegate.go
deleted file mode 100644
index 95728d1..0000000
--- a/rackspace/networking/v2/ports/delegate.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package ports
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/networking/v2/ports"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a Pager which allows you to iterate over a collection of
-// ports. It accepts a ListOpts struct, which allows you to filter and sort
-// the returned collection for greater efficiency.
-//
-// Default policy settings return only those ports that are owned by the tenant
-// who submits the request, unless the request is submitted by a user with
-// administrative rights.
-func List(c *gophercloud.ServiceClient, opts os.ListOptsBuilder) pagination.Pager {
-	return os.List(c, opts)
-}
-
-// Get retrieves a specific port based on its unique ID.
-func Get(c *gophercloud.ServiceClient, networkID string) os.GetResult {
-	return os.Get(c, networkID)
-}
-
-// Create accepts a CreateOpts struct and creates a new network using the values
-// provided. You must remember to provide a NetworkID value.
-//
-// NOTE: Currently the SecurityGroup option is not implemented to work with
-// Rackspace.
-func Create(c *gophercloud.ServiceClient, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(c, opts)
-}
-
-// Update accepts a UpdateOpts struct and updates an existing port using the
-// values provided.
-func Update(c *gophercloud.ServiceClient, networkID string, opts os.UpdateOptsBuilder) os.UpdateResult {
-	return os.Update(c, networkID, opts)
-}
-
-// Delete accepts a unique ID and deletes the port associated with it.
-func Delete(c *gophercloud.ServiceClient, networkID string) os.DeleteResult {
-	return os.Delete(c, networkID)
-}
diff --git a/rackspace/networking/v2/ports/delegate_test.go b/rackspace/networking/v2/ports/delegate_test.go
deleted file mode 100644
index f53ff59..0000000
--- a/rackspace/networking/v2/ports/delegate_test.go
+++ /dev/null
@@ -1,322 +0,0 @@
-package ports
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/networking/v2/ports"
-	"github.com/rackspace/gophercloud/pagination"
-	fake "github.com/rackspace/gophercloud/rackspace/networking/v2/common"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/ports", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "ports": [
-        {
-            "status": "ACTIVE",
-            "binding:host_id": "devstack",
-            "name": "",
-            "admin_state_up": true,
-            "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3",
-            "tenant_id": "",
-            "device_owner": "network:router_gateway",
-            "mac_address": "fa:16:3e:58:42:ed",
-            "fixed_ips": [
-                {
-                    "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062",
-                    "ip_address": "172.24.4.2"
-                }
-            ],
-            "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
-            "security_groups": [],
-            "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824"
-        }
-    ]
-}
-      `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), os.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractPorts(page)
-		if err != nil {
-			t.Errorf("Failed to extract subnets: %v", err)
-			return false, nil
-		}
-
-		expected := []os.Port{
-			os.Port{
-				Status:       "ACTIVE",
-				Name:         "",
-				AdminStateUp: true,
-				NetworkID:    "70c1db1f-b701-45bd-96e0-a313ee3430b3",
-				TenantID:     "",
-				DeviceOwner:  "network:router_gateway",
-				MACAddress:   "fa:16:3e:58:42:ed",
-				FixedIPs: []os.IP{
-					os.IP{
-						SubnetID:  "008ba151-0b8c-4a67-98b5-0d2b87666062",
-						IPAddress: "172.24.4.2",
-					},
-				},
-				ID:             "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
-				SecurityGroups: []string{},
-				DeviceID:       "9ae135f4-b6e0-4dad-9e91-3c223e385824",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "port": {
-        "status": "ACTIVE",
-        "name": "",
-        "admin_state_up": true,
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "tenant_id": "7e02058126cc4950b75f9970368ba177",
-        "device_owner": "network:router_interface",
-        "mac_address": "fa:16:3e:23:fd:d7",
-        "fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.1"
-            }
-        ],
-        "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2",
-        "security_groups": [],
-        "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e"
-    }
-}
-      `)
-	})
-
-	n, err := Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertEquals(t, n.Name, "")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
-	th.AssertEquals(t, n.TenantID, "7e02058126cc4950b75f9970368ba177")
-	th.AssertEquals(t, n.DeviceOwner, "network:router_interface")
-	th.AssertEquals(t, n.MACAddress, "fa:16:3e:23:fd:d7")
-	th.AssertDeepEquals(t, n.FixedIPs, []os.IP{
-		os.IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"},
-	})
-	th.AssertEquals(t, n.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2")
-	th.AssertDeepEquals(t, n.SecurityGroups, []string{})
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertEquals(t, n.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/ports", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "port": {
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "name": "private-port",
-        "admin_state_up": true,
-        "fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.2"
-            }
-        ],
-        "security_groups": ["foo"]
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "port": {
-        "status": "DOWN",
-        "name": "private-port",
-        "allowed_address_pairs": [],
-        "admin_state_up": true,
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
-        "device_owner": "",
-        "mac_address": "fa:16:3e:c9:cb:f0",
-        "fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.2"
-            }
-        ],
-        "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
-        "security_groups": [
-            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
-        ],
-        "device_id": ""
-    }
-}
-    `)
-	})
-
-	asu := true
-	options := os.CreateOpts{
-		Name:         "private-port",
-		AdminStateUp: &asu,
-		NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-		FixedIPs: []os.IP{
-			os.IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
-		},
-		SecurityGroups: []string{"foo"},
-	}
-	n, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.Status, "DOWN")
-	th.AssertEquals(t, n.Name, "private-port")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
-	th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
-	th.AssertEquals(t, n.DeviceOwner, "")
-	th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0")
-	th.AssertDeepEquals(t, n.FixedIPs, []os.IP{
-		os.IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
-	})
-	th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
-	th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), os.CreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "port": {
-        "name": "new_port_name",
-        "fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.3"
-            }
-        ],
-        "security_groups": [
-            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
-        ]
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "port": {
-        "status": "DOWN",
-        "name": "new_port_name",
-        "admin_state_up": true,
-        "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
-        "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
-        "device_owner": "",
-        "mac_address": "fa:16:3e:c9:cb:f0",
-        "fixed_ips": [
-            {
-                "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
-                "ip_address": "10.0.0.3"
-            }
-        ],
-        "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
-        "security_groups": [
-            "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
-        ],
-        "device_id": ""
-    }
-}
-    `)
-	})
-
-	options := os.UpdateOpts{
-		Name: "new_port_name",
-		FixedIPs: []os.IP{
-			os.IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
-		},
-		SecurityGroups: []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"},
-	}
-
-	s, err := Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.Name, "new_port_name")
-	th.AssertDeepEquals(t, s.FixedIPs, []os.IP{
-		os.IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
-	})
-	th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/rackspace/networking/v2/security/doc.go b/rackspace/networking/v2/security/doc.go
deleted file mode 100644
index 31f744c..0000000
--- a/rackspace/networking/v2/security/doc.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Package security contains functionality to work with security group and
-// security group rules Neutron resources.
-//
-// Security groups and security group rules allows administrators and tenants
-// the ability to specify the type of traffic and direction (ingress/egress)
-// that is allowed to pass through a port. A security group is a container for
-// security group rules.
-//
-// When a port is created in Networking it is associated with a security group.
-// If a security group is not specified the port is associated with a 'default'
-// security group. By default, this group drops all ingress traffic and allows
-// all egress. Rules can be added to this group in order to change the behaviour.
-//
-// The basic characteristics of Neutron Security Groups are:
-//
-// For ingress traffic (to an instance)
-//  - Only traffic matched with security group rules are allowed.
-//  - When there is no rule defined, all traffic is dropped.
-//
-// For egress traffic (from an instance)
-//  - Only traffic matched with security group rules are allowed.
-//  - When there is no rule defined, all egress traffic are dropped.
-//  - When a new security group is created, rules to allow all egress traffic
-//    is automatically added.
-//
-// "default security group" is defined for each tenant.
-//  - For the default security group a rule which allows intercommunication
-//    among hosts associated with the default security group is defined by default.
-//  - As a result, all egress traffic and intercommunication in the default
-//    group are allowed and all ingress from outside of the default group is
-//    dropped by default (in the default security group).
-package security
diff --git a/rackspace/networking/v2/security/groups/delegate.go b/rackspace/networking/v2/security/groups/delegate.go
deleted file mode 100644
index 1e9a23a..0000000
--- a/rackspace/networking/v2/security/groups/delegate.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package groups
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a Pager which allows you to iterate over a collection of
-// security groups. It accepts a ListOpts struct, which allows you to filter
-// and sort the returned collection for greater efficiency.
-func List(c *gophercloud.ServiceClient, opts os.ListOpts) pagination.Pager {
-	return os.List(c, opts)
-}
-
-// Create is an operation which provisions a new security group with default
-// security group rules for the IPv4 and IPv6 ether types.
-func Create(c *gophercloud.ServiceClient, opts os.CreateOpts) os.CreateResult {
-	return os.Create(c, opts)
-}
-
-// Get retrieves a particular security group based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) os.GetResult {
-	return os.Get(c, id)
-}
-
-// Delete will permanently delete a particular security group based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) os.DeleteResult {
-	return os.Delete(c, id)
-}
diff --git a/rackspace/networking/v2/security/groups/delegate_test.go b/rackspace/networking/v2/security/groups/delegate_test.go
deleted file mode 100644
index 45cd3ba..0000000
--- a/rackspace/networking/v2/security/groups/delegate_test.go
+++ /dev/null
@@ -1,206 +0,0 @@
-package groups
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	osGroups "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups"
-	osRules "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-groups", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-        {
-          "security_groups": [
-          {
-            "description": "default",
-            "id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-            "name": "default",
-            "security_group_rules": [],
-            "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-          }
-          ]
-        }
-        `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), osGroups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := osGroups.ExtractGroups(page)
-		if err != nil {
-			t.Errorf("Failed to extract secgroups: %v", err)
-			return false, err
-		}
-
-		expected := []osGroups.SecGroup{
-			osGroups.SecGroup{
-				Description: "default",
-				ID:          "85cc3048-abc3-43cc-89b3-377341426ac5",
-				Name:        "default",
-				Rules:       []osRules.SecGroupRule{},
-				TenantID:    "e4f50856753b4dc6afee5fa6b9b6c550",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-groups", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-      {
-        "security_group": {
-          "name": "new-webservers",
-          "description": "security group for webservers"
-        }
-      }
-    `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-    {
-      "security_group": {
-        "description": "security group for webservers",
-        "id": "2076db17-a522-4506-91de-c6dd8e837028",
-        "name": "new-webservers",
-        "security_group_rules": [
-        {
-          "direction": "egress",
-          "ethertype": "IPv4",
-          "id": "38ce2d8e-e8f1-48bd-83c2-d33cb9f50c3d",
-          "port_range_max": null,
-          "port_range_min": null,
-          "protocol": null,
-          "remote_group_id": null,
-          "remote_ip_prefix": null,
-          "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028",
-          "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-        },
-        {
-          "direction": "egress",
-          "ethertype": "IPv6",
-          "id": "565b9502-12de-4ffd-91e9-68885cff6ae1",
-          "port_range_max": null,
-          "port_range_min": null,
-          "protocol": null,
-          "remote_group_id": null,
-          "remote_ip_prefix": null,
-          "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028",
-          "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-        }
-        ],
-        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-      }
-    }
-    `)
-	})
-
-	opts := osGroups.CreateOpts{Name: "new-webservers", Description: "security group for webservers"}
-	_, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-groups/85cc3048-abc3-43cc-89b3-377341426ac5", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-    {
-      "security_group": {
-        "description": "default",
-        "id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-        "name": "default",
-        "security_group_rules": [
-        {
-          "direction": "egress",
-          "ethertype": "IPv6",
-          "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
-          "port_range_max": null,
-          "port_range_min": null,
-          "protocol": null,
-          "remote_group_id": null,
-          "remote_ip_prefix": null,
-          "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-          "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-          },
-        {
-          "direction": "egress",
-          "ethertype": "IPv4",
-          "id": "93aa42e5-80db-4581-9391-3a608bd0e448",
-          "port_range_max": null,
-          "port_range_min": null,
-          "protocol": null,
-          "remote_group_id": null,
-          "remote_ip_prefix": null,
-          "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-          "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-        }
-        ],
-        "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-      }
-    }
-  `)
-	})
-
-	sg, err := Get(fake.ServiceClient(), "85cc3048-abc3-43cc-89b3-377341426ac5").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "default", sg.Description)
-	th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sg.ID)
-	th.AssertEquals(t, "default", sg.Name)
-	th.AssertEquals(t, 2, len(sg.Rules))
-	th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sg.TenantID)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-groups/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/rackspace/networking/v2/security/rules/delegate.go b/rackspace/networking/v2/security/rules/delegate.go
deleted file mode 100644
index 23b4b31..0000000
--- a/rackspace/networking/v2/security/rules/delegate.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package rules
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a Pager which allows you to iterate over a collection of
-// security group rules. It accepts a ListOpts struct, which allows you to filter
-// and sort the returned collection for greater efficiency.
-func List(c *gophercloud.ServiceClient, opts os.ListOpts) pagination.Pager {
-	return os.List(c, opts)
-}
-
-// Create is an operation which provisions a new security group with default
-// security group rules for the IPv4 and IPv6 ether types.
-func Create(c *gophercloud.ServiceClient, opts os.CreateOpts) os.CreateResult {
-	return os.Create(c, opts)
-}
-
-// Get retrieves a particular security group based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) os.GetResult {
-	return os.Get(c, id)
-}
-
-// Delete will permanently delete a particular security group based on its unique ID.
-func Delete(c *gophercloud.ServiceClient, id string) os.DeleteResult {
-	return os.Delete(c, id)
-}
diff --git a/rackspace/networking/v2/security/rules/delegate_test.go b/rackspace/networking/v2/security/rules/delegate_test.go
deleted file mode 100644
index 3563fbe..0000000
--- a/rackspace/networking/v2/security/rules/delegate_test.go
+++ /dev/null
@@ -1,236 +0,0 @@
-package rules
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
-	osRules "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-        {
-          "security_group_rules": [
-          {
-            "direction": "egress",
-            "ethertype": "IPv6",
-            "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
-            "port_range_max": null,
-            "port_range_min": null,
-            "protocol": null,
-            "remote_group_id": null,
-            "remote_ip_prefix": null,
-            "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-            "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-            },
-            {
-              "direction": "egress",
-              "ethertype": "IPv4",
-              "id": "93aa42e5-80db-4581-9391-3a608bd0e448",
-              "port_range_max": null,
-              "port_range_min": null,
-              "protocol": null,
-              "remote_group_id": null,
-              "remote_ip_prefix": null,
-              "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-              "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-            }
-            ]
-          }
-          `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), osRules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := osRules.ExtractRules(page)
-		if err != nil {
-			t.Errorf("Failed to extract secrules: %v", err)
-			return false, err
-		}
-
-		expected := []osRules.SecGroupRule{
-			osRules.SecGroupRule{
-				Direction:      "egress",
-				EtherType:      "IPv6",
-				ID:             "3c0e45ff-adaf-4124-b083-bf390e5482ff",
-				PortRangeMax:   0,
-				PortRangeMin:   0,
-				Protocol:       "",
-				RemoteGroupID:  "",
-				RemoteIPPrefix: "",
-				SecGroupID:     "85cc3048-abc3-43cc-89b3-377341426ac5",
-				TenantID:       "e4f50856753b4dc6afee5fa6b9b6c550",
-			},
-			osRules.SecGroupRule{
-				Direction:      "egress",
-				EtherType:      "IPv4",
-				ID:             "93aa42e5-80db-4581-9391-3a608bd0e448",
-				PortRangeMax:   0,
-				PortRangeMin:   0,
-				Protocol:       "",
-				RemoteGroupID:  "",
-				RemoteIPPrefix: "",
-				SecGroupID:     "85cc3048-abc3-43cc-89b3-377341426ac5",
-				TenantID:       "e4f50856753b4dc6afee5fa6b9b6c550",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-                    {
-                      "security_group_rule": {
-                        "direction": "ingress",
-                        "port_range_min": 80,
-                        "ethertype": "IPv4",
-                        "port_range_max": 80,
-                        "protocol": "tcp",
-                        "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-                        "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a"
-                      }
-                    }
-                    `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-                      {
-                        "security_group_rule": {
-                          "direction": "ingress",
-                          "ethertype": "IPv4",
-                          "id": "2bc0accf-312e-429a-956e-e4407625eb62",
-                          "port_range_max": 80,
-                          "port_range_min": 80,
-                          "protocol": "tcp",
-                          "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-                          "remote_ip_prefix": null,
-                          "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a",
-                          "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-                        }
-                      }
-                      `)
-	})
-
-	opts := osRules.CreateOpts{
-		Direction:     "ingress",
-		PortRangeMin:  80,
-		EtherType:     "IPv4",
-		PortRangeMax:  80,
-		Protocol:      "tcp",
-		RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5",
-		SecGroupID:    "a7734e61-b545-452d-a3cd-0189cbd9747a",
-	}
-	_, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), osRules.CreateOpts{Direction: "something"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), osRules.CreateOpts{Direction: osRules.DirIngress, EtherType: "something"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), osRules.CreateOpts{Direction: osRules.DirIngress, EtherType: osRules.Ether4})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), osRules.CreateOpts{Direction: osRules.DirIngress, EtherType: osRules.Ether4, SecGroupID: "something", Protocol: "foo"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-group-rules/3c0e45ff-adaf-4124-b083-bf390e5482ff", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-                          {
-                            "security_group_rule": {
-                              "direction": "egress",
-                              "ethertype": "IPv6",
-                              "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
-                              "port_range_max": null,
-                              "port_range_min": null,
-                              "protocol": null,
-                              "remote_group_id": null,
-                              "remote_ip_prefix": null,
-                              "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
-                              "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
-                            }
-                          }
-                          `)
-	})
-
-	sr, err := Get(fake.ServiceClient(), "3c0e45ff-adaf-4124-b083-bf390e5482ff").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "egress", sr.Direction)
-	th.AssertEquals(t, "IPv6", sr.EtherType)
-	th.AssertEquals(t, "3c0e45ff-adaf-4124-b083-bf390e5482ff", sr.ID)
-	th.AssertEquals(t, 0, sr.PortRangeMax)
-	th.AssertEquals(t, 0, sr.PortRangeMin)
-	th.AssertEquals(t, "", sr.Protocol)
-	th.AssertEquals(t, "", sr.RemoteGroupID)
-	th.AssertEquals(t, "", sr.RemoteIPPrefix)
-	th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sr.SecGroupID)
-	th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sr.TenantID)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/security-group-rules/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/rackspace/networking/v2/subnets/delegate.go b/rackspace/networking/v2/subnets/delegate.go
deleted file mode 100644
index a7fb7bb..0000000
--- a/rackspace/networking/v2/subnets/delegate.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package subnets
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns a Pager which allows you to iterate over a collection of
-// subnets. It accepts a ListOpts struct, which allows you to filter and sort
-// the returned collection for greater efficiency.
-//
-// Default policy settings return only those subnets that are owned by the tenant
-// who submits the request, unless the request is submitted by a user with
-// administrative rights.
-func List(c *gophercloud.ServiceClient, opts os.ListOptsBuilder) pagination.Pager {
-	return os.List(c, opts)
-}
-
-// Get retrieves a specific subnet based on its unique ID.
-func Get(c *gophercloud.ServiceClient, networkID string) os.GetResult {
-	return os.Get(c, networkID)
-}
-
-// Create accepts a CreateOpts struct and creates a new subnet using the values
-// provided. You must remember to provide a valid NetworkID, CIDR and IP version.
-func Create(c *gophercloud.ServiceClient, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(c, opts)
-}
-
-// Update accepts a UpdateOpts struct and updates an existing subnet using the
-// values provided.
-func Update(c *gophercloud.ServiceClient, networkID string, opts os.UpdateOptsBuilder) os.UpdateResult {
-	return os.Update(c, networkID, opts)
-}
-
-// Delete accepts a unique ID and deletes the subnet associated with it.
-func Delete(c *gophercloud.ServiceClient, networkID string) os.DeleteResult {
-	return os.Delete(c, networkID)
-}
diff --git a/rackspace/networking/v2/subnets/delegate_test.go b/rackspace/networking/v2/subnets/delegate_test.go
deleted file mode 100644
index fafc6fb..0000000
--- a/rackspace/networking/v2/subnets/delegate_test.go
+++ /dev/null
@@ -1,363 +0,0 @@
-package subnets
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
-	"github.com/rackspace/gophercloud/pagination"
-	fake "github.com/rackspace/gophercloud/rackspace/networking/v2/common"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/subnets", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "subnets": [
-        {
-            "name": "private-subnet",
-            "enable_dhcp": true,
-            "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-            "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
-            "dns_nameservers": [],
-            "allocation_pools": [
-                {
-                    "start": "10.0.0.2",
-                    "end": "10.0.0.254"
-                }
-            ],
-            "host_routes": [],
-            "ip_version": 4,
-            "gateway_ip": "10.0.0.1",
-            "cidr": "10.0.0.0/24",
-            "id": "08eae331-0402-425a-923c-34f7cfe39c1b"
-        },
-        {
-            "name": "my_subnet",
-            "enable_dhcp": true,
-            "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-            "dns_nameservers": [],
-            "allocation_pools": [
-                {
-                    "start": "192.0.0.2",
-                    "end": "192.255.255.254"
-                }
-            ],
-            "host_routes": [],
-            "ip_version": 4,
-            "gateway_ip": "192.0.0.1",
-            "cidr": "192.0.0.0/8",
-            "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-        }
-    ]
-}
-      `)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), os.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractSubnets(page)
-		if err != nil {
-			t.Errorf("Failed to extract subnets: %v", err)
-			return false, nil
-		}
-
-		expected := []os.Subnet{
-			os.Subnet{
-				Name:           "private-subnet",
-				EnableDHCP:     true,
-				NetworkID:      "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-				TenantID:       "26a7980765d0414dbc1fc1f88cdb7e6e",
-				DNSNameservers: []string{},
-				AllocationPools: []os.AllocationPool{
-					os.AllocationPool{
-						Start: "10.0.0.2",
-						End:   "10.0.0.254",
-					},
-				},
-				HostRoutes: []os.HostRoute{},
-				IPVersion:  4,
-				GatewayIP:  "10.0.0.1",
-				CIDR:       "10.0.0.0/24",
-				ID:         "08eae331-0402-425a-923c-34f7cfe39c1b",
-			},
-			os.Subnet{
-				Name:           "my_subnet",
-				EnableDHCP:     true,
-				NetworkID:      "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-				TenantID:       "4fd44f30292945e481c7b8a0c8908869",
-				DNSNameservers: []string{},
-				AllocationPools: []os.AllocationPool{
-					os.AllocationPool{
-						Start: "192.0.0.2",
-						End:   "192.255.255.254",
-					},
-				},
-				HostRoutes: []os.HostRoute{},
-				IPVersion:  4,
-				GatewayIP:  "192.0.0.1",
-				CIDR:       "192.0.0.0/8",
-				ID:         "54d6f61d-db07-451c-9ab3-b9609b6b6f0b",
-			},
-		}
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/subnets/54d6f61d-db07-451c-9ab3-b9609b6b6f0b", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "subnet": {
-        "name": "my_subnet",
-        "enable_dhcp": true,
-        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "dns_nameservers": [],
-        "allocation_pools": [
-            {
-                "start": "192.0.0.2",
-                "end": "192.255.255.254"
-            }
-        ],
-        "host_routes": [],
-        "ip_version": 4,
-        "gateway_ip": "192.0.0.1",
-        "cidr": "192.0.0.0/8",
-        "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
-    }
-}
-      `)
-	})
-
-	s, err := Get(fake.ServiceClient(), "54d6f61d-db07-451c-9ab3-b9609b6b6f0b").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.Name, "my_subnet")
-	th.AssertEquals(t, s.EnableDHCP, true)
-	th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869")
-	th.AssertDeepEquals(t, s.DNSNameservers, []string{})
-	th.AssertDeepEquals(t, s.AllocationPools, []os.AllocationPool{
-		os.AllocationPool{
-			Start: "192.0.0.2",
-			End:   "192.255.255.254",
-		},
-	})
-	th.AssertDeepEquals(t, s.HostRoutes, []os.HostRoute{})
-	th.AssertEquals(t, s.IPVersion, 4)
-	th.AssertEquals(t, s.GatewayIP, "192.0.0.1")
-	th.AssertEquals(t, s.CIDR, "192.0.0.0/8")
-	th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0b")
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/subnets", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "subnet": {
-        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-        "ip_version": 4,
-        "cidr": "192.168.199.0/24",
-        "dns_nameservers": ["foo"],
-        "allocation_pools": [
-            {
-                "start": "192.168.199.2",
-                "end": "192.168.199.254"
-            }
-        ],
-        "host_routes": [{"destination":"","nexthop": "bar"}]
-    }
-}
-      `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "subnet": {
-        "name": "",
-        "enable_dhcp": true,
-        "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "dns_nameservers": [],
-        "allocation_pools": [
-            {
-                "start": "192.168.199.2",
-                "end": "192.168.199.254"
-            }
-        ],
-        "host_routes": [],
-        "ip_version": 4,
-        "gateway_ip": "192.168.199.1",
-        "cidr": "192.168.199.0/24",
-        "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126"
-    }
-}
-    `)
-	})
-
-	opts := os.CreateOpts{
-		NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22",
-		IPVersion: 4,
-		CIDR:      "192.168.199.0/24",
-		AllocationPools: []os.AllocationPool{
-			os.AllocationPool{
-				Start: "192.168.199.2",
-				End:   "192.168.199.254",
-			},
-		},
-		DNSNameservers: []string{"foo"},
-		HostRoutes: []os.HostRoute{
-			os.HostRoute{NextHop: "bar"},
-		},
-	}
-	s, err := Create(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.Name, "")
-	th.AssertEquals(t, s.EnableDHCP, true)
-	th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869")
-	th.AssertDeepEquals(t, s.DNSNameservers, []string{})
-	th.AssertDeepEquals(t, s.AllocationPools, []os.AllocationPool{
-		os.AllocationPool{
-			Start: "192.168.199.2",
-			End:   "192.168.199.254",
-		},
-	})
-	th.AssertDeepEquals(t, s.HostRoutes, []os.HostRoute{})
-	th.AssertEquals(t, s.IPVersion, 4)
-	th.AssertEquals(t, s.GatewayIP, "192.168.199.1")
-	th.AssertEquals(t, s.CIDR, "192.168.199.0/24")
-	th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126")
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), os.CreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-
-	res = Create(fake.ServiceClient(), os.CreateOpts{NetworkID: "foo"})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-
-	res = Create(fake.ServiceClient(), os.CreateOpts{NetworkID: "foo", CIDR: "bar", IPVersion: 40})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "subnet": {
-        "name": "my_new_subnet",
-        "dns_nameservers": ["foo"],
-        "host_routes": [{"destination":"","nexthop": "bar"}]
-    }
-}
-    `)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "subnet": {
-        "name": "my_new_subnet",
-        "enable_dhcp": true,
-        "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
-        "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
-        "dns_nameservers": [],
-        "allocation_pools": [
-            {
-                "start": "10.0.0.2",
-                "end": "10.0.0.254"
-            }
-        ],
-        "host_routes": [],
-        "ip_version": 4,
-        "gateway_ip": "10.0.0.1",
-        "cidr": "10.0.0.0/24",
-        "id": "08eae331-0402-425a-923c-34f7cfe39c1b"
-    }
-}
-  `)
-	})
-
-	opts := os.UpdateOpts{
-		Name:           "my_new_subnet",
-		DNSNameservers: []string{"foo"},
-		HostRoutes: []os.HostRoute{
-			os.HostRoute{NextHop: "bar"},
-		},
-	}
-	s, err := Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.Name, "my_new_subnet")
-	th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b")
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/rackspace/objectstorage/v1/accounts/delegate.go b/rackspace/objectstorage/v1/accounts/delegate.go
deleted file mode 100644
index 9473930..0000000
--- a/rackspace/objectstorage/v1/accounts/delegate.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package accounts
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts"
-)
-
-// Get is a function that retrieves an account's metadata. To extract just the
-// custom metadata, call the ExtractMetadata method on the GetResult. To extract
-// all the headers that are returned (including the metadata), call the
-// ExtractHeader method on the GetResult.
-func Get(c *gophercloud.ServiceClient) os.GetResult {
-	return os.Get(c, nil)
-}
-
-// UpdateOpts is a structure that contains parameters for updating, creating, or
-// deleting an account's metadata.
-type UpdateOpts struct {
-	Metadata    map[string]string
-	TempURLKey  string `h:"X-Account-Meta-Temp-URL-Key"`
-	TempURLKey2 string `h:"X-Account-Meta-Temp-URL-Key-2"`
-}
-
-// ToAccountUpdateMap formats an UpdateOpts into a map[string]string of headers.
-func (opts UpdateOpts) ToAccountUpdateMap() (map[string]string, error) {
-	headers, err := gophercloud.BuildHeaders(opts)
-	if err != nil {
-		return nil, err
-	}
-	for k, v := range opts.Metadata {
-		headers["X-Account-Meta-"+k] = v
-	}
-	return headers, err
-}
-
-// Update will update an account's metadata with the Metadata in the UpdateOptsBuilder.
-func Update(c *gophercloud.ServiceClient, opts os.UpdateOptsBuilder) os.UpdateResult {
-	return os.Update(c, opts)
-}
diff --git a/rackspace/objectstorage/v1/accounts/delegate_test.go b/rackspace/objectstorage/v1/accounts/delegate_test.go
deleted file mode 100644
index a1ea98b..0000000
--- a/rackspace/objectstorage/v1/accounts/delegate_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package accounts
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestUpdateAccounts(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.HandleUpdateAccountSuccessfully(t)
-
-	options := &UpdateOpts{Metadata: map[string]string{"gophercloud-test": "accounts"}}
-	res := Update(fake.ServiceClient(), options)
-	th.CheckNoErr(t, res.Err)
-}
-
-func TestGetAccounts(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	os.HandleGetAccountSuccessfully(t)
-
-	expected := map[string]string{"Foo": "bar"}
-	actual, err := Get(fake.ServiceClient()).ExtractMetadata()
-	th.CheckNoErr(t, err)
-	th.CheckDeepEquals(t, expected, actual)
-}
diff --git a/rackspace/objectstorage/v1/accounts/doc.go b/rackspace/objectstorage/v1/accounts/doc.go
deleted file mode 100644
index 293a930..0000000
--- a/rackspace/objectstorage/v1/accounts/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package accounts provides information and interaction with the account
-// API resource for the Rackspace Cloud Files service.
-package accounts
diff --git a/rackspace/objectstorage/v1/bulk/doc.go b/rackspace/objectstorage/v1/bulk/doc.go
deleted file mode 100644
index 9c89e22..0000000
--- a/rackspace/objectstorage/v1/bulk/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package bulk provides functionality for working with bulk operations in the
-// Rackspace Cloud Files service.
-package bulk
diff --git a/rackspace/objectstorage/v1/bulk/requests.go b/rackspace/objectstorage/v1/bulk/requests.go
deleted file mode 100644
index 0aeec15..0000000
--- a/rackspace/objectstorage/v1/bulk/requests.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package bulk
-
-import (
-	"net/url"
-	"strings"
-
-	"github.com/rackspace/gophercloud"
-)
-
-// DeleteOptsBuilder allows extensions to add additional parameters to the
-// Delete request.
-type DeleteOptsBuilder interface {
-	ToBulkDeleteBody() (string, error)
-}
-
-// DeleteOpts is a structure that holds parameters for deleting an object.
-type DeleteOpts []string
-
-// ToBulkDeleteBody formats a DeleteOpts into a request body.
-func (opts DeleteOpts) ToBulkDeleteBody() (string, error) {
-	return url.QueryEscape(strings.Join(opts, "\n")), nil
-}
-
-// Delete will delete objects or containers in bulk.
-func Delete(c *gophercloud.ServiceClient, opts DeleteOptsBuilder) DeleteResult {
-	var res DeleteResult
-
-	if opts == nil {
-		return res
-	}
-
-	reqString, err := opts.ToBulkDeleteBody()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	reqBody := strings.NewReader(reqString)
-
-	resp, err := c.Request("DELETE", deleteURL(c), gophercloud.RequestOpts{
-		MoreHeaders:  map[string]string{"Content-Type": "text/plain"},
-		OkCodes:      []int{200},
-		JSONBody:     reqBody,
-		JSONResponse: &res.Body,
-	})
-	if resp != nil {
-		res.Header = resp.Header
-	}
-	res.Err = err
-	return res
-}
diff --git a/rackspace/objectstorage/v1/bulk/requests_test.go b/rackspace/objectstorage/v1/bulk/requests_test.go
deleted file mode 100644
index 8b5578e..0000000
--- a/rackspace/objectstorage/v1/bulk/requests_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package bulk
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestBulkDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.AssertEquals(t, r.URL.RawQuery, "bulk-delete")
-
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-      {
-        "Number Not Found": 1,
-        "Response Status": "200 OK",
-        "Errors": [],
-        "Number Deleted": 1,
-        "Response Body": ""
-      }
-    `)
-	})
-
-	options := DeleteOpts{"gophercloud-testcontainer1", "gophercloud-testcontainer2"}
-	actual, err := Delete(fake.ServiceClient(), options).ExtractBody()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, actual.NumberDeleted, 1)
-}
diff --git a/rackspace/objectstorage/v1/bulk/results.go b/rackspace/objectstorage/v1/bulk/results.go
deleted file mode 100644
index fddc125..0000000
--- a/rackspace/objectstorage/v1/bulk/results.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package bulk
-
-import (
-	"github.com/rackspace/gophercloud"
-
-	"github.com/mitchellh/mapstructure"
-)
-
-// DeleteResult represents the result of a bulk delete operation.
-type DeleteResult struct {
-	gophercloud.Result
-}
-
-// DeleteRespBody is the form of the response body returned by a bulk delete request.
-type DeleteRespBody struct {
-	NumberNotFound int      `mapstructure:"Number Not Found"`
-	ResponseStatus string   `mapstructure:"Response Status"`
-	Errors         []string `mapstructure:"Errors"`
-	NumberDeleted  int      `mapstructure:"Number Deleted"`
-	ResponseBody   string   `mapstructure:"Response Body"`
-}
-
-// ExtractBody will extract the body returned by the bulk extract request.
-func (dr DeleteResult) ExtractBody() (DeleteRespBody, error) {
-	var resp DeleteRespBody
-	err := mapstructure.Decode(dr.Body, &resp)
-	return resp, err
-}
diff --git a/rackspace/objectstorage/v1/bulk/urls.go b/rackspace/objectstorage/v1/bulk/urls.go
deleted file mode 100644
index 2e11203..0000000
--- a/rackspace/objectstorage/v1/bulk/urls.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package bulk
-
-import "github.com/rackspace/gophercloud"
-
-func deleteURL(c *gophercloud.ServiceClient) string {
-	return c.Endpoint + "?bulk-delete"
-}
-
-func extractURL(c *gophercloud.ServiceClient, ext string) string {
-	return c.Endpoint + "?extract-archive=" + ext
-}
diff --git a/rackspace/objectstorage/v1/bulk/urls_test.go b/rackspace/objectstorage/v1/bulk/urls_test.go
deleted file mode 100644
index 9169e52..0000000
--- a/rackspace/objectstorage/v1/bulk/urls_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package bulk
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestDeleteURL(t *testing.T) {
-	actual := deleteURL(endpointClient())
-	expected := endpoint + "?bulk-delete"
-	th.CheckEquals(t, expected, actual)
-}
-
-func TestExtractURL(t *testing.T) {
-	actual := extractURL(endpointClient(), "tar")
-	expected := endpoint + "?extract-archive=tar"
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/rackspace/objectstorage/v1/cdncontainers/delegate.go b/rackspace/objectstorage/v1/cdncontainers/delegate.go
deleted file mode 100644
index 89adb83..0000000
--- a/rackspace/objectstorage/v1/cdncontainers/delegate.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package cdncontainers
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// ExtractNames interprets a page of List results when just the container
-// names are requested.
-func ExtractNames(page pagination.Page) ([]string, error) {
-	return os.ExtractNames(page)
-}
-
-// ListOpts are options for listing Rackspace CDN containers.
-type ListOpts struct {
-	EndMarker string `q:"end_marker"`
-	Format    string `q:"format"`
-	Limit     int    `q:"limit"`
-	Marker    string `q:"marker"`
-}
-
-// ToContainerListParams formats a ListOpts into a query string and boolean
-// representing whether to list complete information for each container.
-func (opts ListOpts) ToContainerListParams() (bool, string, error) {
-	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return false, "", err
-	}
-	return false, q.String(), nil
-}
-
-// List is a function that retrieves containers associated with the account as
-// well as account metadata. It returns a pager which can be iterated with the
-// EachPage function.
-func List(c *gophercloud.ServiceClient, opts os.ListOptsBuilder) pagination.Pager {
-	return os.List(c, opts)
-}
diff --git a/rackspace/objectstorage/v1/cdncontainers/delegate_test.go b/rackspace/objectstorage/v1/cdncontainers/delegate_test.go
deleted file mode 100644
index 02c3c5e..0000000
--- a/rackspace/objectstorage/v1/cdncontainers/delegate_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package cdncontainers
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListCDNContainers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListContainerNamesSuccessfully(t)
-
-	count := 0
-	err := List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNames(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, os.ExpectedListNames, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestGetCDNContainer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGetContainerSuccessfully(t)
-
-	_, err := Get(fake.ServiceClient(), "testContainer").ExtractMetadata()
-	th.CheckNoErr(t, err)
-
-}
-
-func TestUpdateCDNContainer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleUpdateContainerSuccessfully(t)
-
-	options := &UpdateOpts{TTL: 3600}
-	res := Update(fake.ServiceClient(), "testContainer", options)
-	th.CheckNoErr(t, res.Err)
-
-}
diff --git a/rackspace/objectstorage/v1/cdncontainers/doc.go b/rackspace/objectstorage/v1/cdncontainers/doc.go
deleted file mode 100644
index 7b0930e..0000000
--- a/rackspace/objectstorage/v1/cdncontainers/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package cdncontainers provides information and interaction with the CDN
-// Container API resource for the Rackspace Cloud Files service.
-package cdncontainers
diff --git a/rackspace/objectstorage/v1/cdncontainers/requests.go b/rackspace/objectstorage/v1/cdncontainers/requests.go
deleted file mode 100644
index e85e17a..0000000
--- a/rackspace/objectstorage/v1/cdncontainers/requests.go
+++ /dev/null
@@ -1,161 +0,0 @@
-package cdncontainers
-
-import (
-	"strconv"
-
-	"github.com/rackspace/gophercloud"
-)
-
-// EnableOptsBuilder allows extensions to add additional parameters to the Enable
-// request.
-type EnableOptsBuilder interface {
-	ToCDNContainerEnableMap() (map[string]string, error)
-}
-
-// EnableOpts is a structure that holds options for enabling a CDN container.
-type EnableOpts struct {
-	// CDNEnabled indicates whether or not the container is CDN enabled. Set to
-	// `true` to enable the container. Note that changing this setting from true
-	// to false will disable the container in the CDN but only after the TTL has
-	// expired.
-	CDNEnabled bool `h:"X-Cdn-Enabled"`
-	// TTL is the time-to-live for the container (in seconds).
-	TTL int `h:"X-Ttl"`
-}
-
-// ToCDNContainerEnableMap formats an EnableOpts into a map of headers.
-func (opts EnableOpts) ToCDNContainerEnableMap() (map[string]string, error) {
-	h, err := gophercloud.BuildHeaders(opts)
-	if err != nil {
-		return nil, err
-	}
-	return h, nil
-}
-
-// Enable is a function that enables/disables a CDN container.
-func Enable(c *gophercloud.ServiceClient, containerName string, opts EnableOptsBuilder) EnableResult {
-	var res EnableResult
-	h := make(map[string]string)
-
-	if opts != nil {
-		headers, err := opts.ToCDNContainerEnableMap()
-		if err != nil {
-			res.Err = err
-			return res
-		}
-
-		for k, v := range headers {
-			h[k] = v
-		}
-	}
-
-	resp, err := c.Request("PUT", enableURL(c, containerName), gophercloud.RequestOpts{
-		MoreHeaders: h,
-		OkCodes:     []int{201, 202, 204},
-	})
-	if resp != nil {
-		res.Header = resp.Header
-	}
-	res.Err = err
-	return res
-}
-
-// Get is a function that retrieves the metadata of a container. To extract just
-// the custom metadata, pass the GetResult response to the ExtractMetadata
-// function.
-func Get(c *gophercloud.ServiceClient, containerName string) GetResult {
-	var res GetResult
-	resp, err := c.Request("HEAD", getURL(c, containerName), gophercloud.RequestOpts{
-		OkCodes: []int{200, 204},
-	})
-	if resp != nil {
-		res.Header = resp.Header
-	}
-	res.Err = err
-	return res
-}
-
-// State is the state of an option. It is a pointer to a boolean to enable checking for
-// a zero-value of nil instead of false, which is a valid option.
-type State *bool
-
-var (
-	iTrue  = true
-	iFalse = false
-
-	// Enabled is used for a true value for options in request bodies.
-	Enabled State = &iTrue
-	// Disabled is used for a false value for options in request bodies.
-	Disabled State = &iFalse
-)
-
-// UpdateOptsBuilder allows extensions to add additional parameters to the
-// Update request.
-type UpdateOptsBuilder interface {
-	ToContainerUpdateMap() (map[string]string, error)
-}
-
-// UpdateOpts is a structure that holds parameters for updating, creating, or
-// deleting a container's metadata.
-type UpdateOpts struct {
-	// Whether or not to CDN-enable a container. Prefer using XCDNEnabled, which
-	// is of type *bool underneath.
-	// TODO v2.0: change type to Enabled/Disabled (*bool)
-	CDNEnabled bool `h:"X-Cdn-Enabled"`
-	// Whether or not to enable log retention. Prefer using XLogRetention, which
-	// is of type *bool underneath.
-	// TODO v2.0: change type to Enabled/Disabled (*bool)
-	LogRetention  bool `h:"X-Log-Retention"`
-	XCDNEnabled   *bool
-	XLogRetention *bool
-	TTL           int `h:"X-Ttl"`
-}
-
-// ToContainerUpdateMap formats a CreateOpts into a map of headers.
-func (opts UpdateOpts) ToContainerUpdateMap() (map[string]string, error) {
-	h, err := gophercloud.BuildHeaders(opts)
-	if err != nil {
-		return nil, err
-	}
-	h["X-Cdn-Enabled"] = strconv.FormatBool(opts.CDNEnabled)
-	h["X-Log-Retention"] = strconv.FormatBool(opts.LogRetention)
-
-	if opts.XCDNEnabled != nil {
-		h["X-Cdn-Enabled"] = strconv.FormatBool(*opts.XCDNEnabled)
-	}
-
-	if opts.XLogRetention != nil {
-		h["X-Log-Retention"] = strconv.FormatBool(*opts.XLogRetention)
-	}
-
-	return h, nil
-}
-
-// Update is a function that creates, updates, or deletes a container's
-// metadata.
-func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsBuilder) UpdateResult {
-	var res UpdateResult
-	h := c.AuthenticatedHeaders()
-
-	if opts != nil {
-		headers, err := opts.ToContainerUpdateMap()
-		if err != nil {
-			res.Err = err
-			return res
-		}
-
-		for k, v := range headers {
-			h[k] = v
-		}
-	}
-
-	resp, err := c.Request("POST", updateURL(c, containerName), gophercloud.RequestOpts{
-		MoreHeaders: h,
-		OkCodes:     []int{202, 204},
-	})
-	if resp != nil {
-		res.Header = resp.Header
-	}
-	res.Err = err
-	return res
-}
diff --git a/rackspace/objectstorage/v1/cdncontainers/requests_test.go b/rackspace/objectstorage/v1/cdncontainers/requests_test.go
deleted file mode 100644
index 28b963d..0000000
--- a/rackspace/objectstorage/v1/cdncontainers/requests_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package cdncontainers
-
-import (
-	"net/http"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestEnableCDNContainer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Add("X-Ttl", "259200")
-		w.Header().Add("X-Cdn-Enabled", "True")
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	options := &EnableOpts{CDNEnabled: true, TTL: 259200}
-	actual := Enable(fake.ServiceClient(), "testContainer", options)
-	th.AssertNoErr(t, actual.Err)
-	th.CheckEquals(t, actual.Header["X-Ttl"][0], "259200")
-	th.CheckEquals(t, actual.Header["X-Cdn-Enabled"][0], "True")
-}
diff --git a/rackspace/objectstorage/v1/cdncontainers/results.go b/rackspace/objectstorage/v1/cdncontainers/results.go
deleted file mode 100644
index cb0ad30..0000000
--- a/rackspace/objectstorage/v1/cdncontainers/results.go
+++ /dev/null
@@ -1,149 +0,0 @@
-package cdncontainers
-
-import (
-	"strings"
-	"time"
-
-	"github.com/rackspace/gophercloud"
-)
-
-// EnableHeader represents the headers returned in the response from an Enable request.
-type EnableHeader struct {
-	CDNIosURI       string    `mapstructure:"X-Cdn-Ios-Uri"`
-	CDNSslURI       string    `mapstructure:"X-Cdn-Ssl-Uri"`
-	CDNStreamingURI string    `mapstructure:"X-Cdn-Streaming-Uri"`
-	CDNUri          string    `mapstructure:"X-Cdn-Uri"`
-	ContentLength   int       `mapstructure:"Content-Length"`
-	ContentType     string    `mapstructure:"Content-Type"`
-	Date            time.Time `mapstructure:"-"`
-	TransID         string    `mapstructure:"X-Trans-Id"`
-}
-
-// EnableResult represents the result of an Enable operation.
-type EnableResult struct {
-	gophercloud.HeaderResult
-}
-
-// Extract will return extract an EnableHeader from the response to an Enable
-// request. To obtain a map of headers, call the ExtractHeader method on the EnableResult.
-func (er EnableResult) Extract() (EnableHeader, error) {
-	var eh EnableHeader
-	if er.Err != nil {
-		return eh, er.Err
-	}
-
-	if err := gophercloud.DecodeHeader(er.Header, &eh); err != nil {
-		return eh, err
-	}
-
-	if date, ok := er.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, er.Header["Date"][0])
-		if err != nil {
-			return eh, err
-		}
-		eh.Date = t
-	}
-
-	return eh, nil
-}
-
-// GetHeader represents the headers returned in the response from a Get request.
-type GetHeader struct {
-	CDNEnabled      bool      `mapstructure:"X-Cdn-Enabled"`
-	CDNIosURI       string    `mapstructure:"X-Cdn-Ios-Uri"`
-	CDNSslURI       string    `mapstructure:"X-Cdn-Ssl-Uri"`
-	CDNStreamingURI string    `mapstructure:"X-Cdn-Streaming-Uri"`
-	CDNUri          string    `mapstructure:"X-Cdn-Uri"`
-	ContentLength   int       `mapstructure:"Content-Length"`
-	ContentType     string    `mapstructure:"Content-Type"`
-	Date            time.Time `mapstructure:"-"`
-	LogRetention    bool      `mapstructure:"X-Log-Retention"`
-	TransID         string    `mapstructure:"X-Trans-Id"`
-	TTL             int       `mapstructure:"X-Ttl"`
-}
-
-// GetResult represents the result of a Get operation.
-type GetResult struct {
-	gophercloud.HeaderResult
-}
-
-// Extract will return a struct of headers returned from a call to Get. To obtain
-// a map of headers, call the ExtractHeader method on the GetResult.
-func (gr GetResult) Extract() (GetHeader, error) {
-	var gh GetHeader
-	if gr.Err != nil {
-		return gh, gr.Err
-	}
-
-	if err := gophercloud.DecodeHeader(gr.Header, &gh); err != nil {
-		return gh, err
-	}
-
-	if date, ok := gr.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, gr.Header["Date"][0])
-		if err != nil {
-			return gh, err
-		}
-		gh.Date = t
-	}
-
-	return gh, nil
-}
-
-// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
-// and returns the custom metadata associated with the container.
-func (gr GetResult) ExtractMetadata() (map[string]string, error) {
-	if gr.Err != nil {
-		return nil, gr.Err
-	}
-	metadata := make(map[string]string)
-	for k, v := range gr.Header {
-		if strings.HasPrefix(k, "X-Container-Meta-") {
-			key := strings.TrimPrefix(k, "X-Container-Meta-")
-			metadata[key] = v[0]
-		}
-	}
-	return metadata, nil
-}
-
-// UpdateHeader represents the headers returned in the response from a Update request.
-type UpdateHeader struct {
-	CDNIosURI       string    `mapstructure:"X-Cdn-Ios-Uri"`
-	CDNSslURI       string    `mapstructure:"X-Cdn-Ssl-Uri"`
-	CDNStreamingURI string    `mapstructure:"X-Cdn-Streaming-Uri"`
-	CDNUri          string    `mapstructure:"X-Cdn-Uri"`
-	ContentLength   int       `mapstructure:"Content-Length"`
-	ContentType     string    `mapstructure:"Content-Type"`
-	Date            time.Time `mapstructure:"-"`
-	TransID         string    `mapstructure:"X-Trans-Id"`
-}
-
-// UpdateResult represents the result of an update operation. To extract the
-// the headers from the HTTP response, you can invoke the 'ExtractHeader'
-// method on the result struct.
-type UpdateResult struct {
-	gophercloud.HeaderResult
-}
-
-// Extract will return a struct of headers returned from a call to Update. To obtain
-// a map of headers, call the ExtractHeader method on the UpdateResult.
-func (ur UpdateResult) Extract() (UpdateHeader, error) {
-	var uh UpdateHeader
-	if ur.Err != nil {
-		return uh, ur.Err
-	}
-
-	if err := gophercloud.DecodeHeader(ur.Header, &uh); err != nil {
-		return uh, err
-	}
-
-	if date, ok := ur.Header["Date"]; ok && len(date) > 0 {
-		t, err := time.Parse(time.RFC1123, ur.Header["Date"][0])
-		if err != nil {
-			return uh, err
-		}
-		uh.Date = t
-	}
-
-	return uh, nil
-}
diff --git a/rackspace/objectstorage/v1/cdncontainers/urls.go b/rackspace/objectstorage/v1/cdncontainers/urls.go
deleted file mode 100644
index 541249a..0000000
--- a/rackspace/objectstorage/v1/cdncontainers/urls.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package cdncontainers
-
-import "github.com/rackspace/gophercloud"
-
-func enableURL(c *gophercloud.ServiceClient, containerName string) string {
-	return c.ServiceURL(containerName)
-}
-
-func getURL(c *gophercloud.ServiceClient, container string) string {
-	return c.ServiceURL(container)
-}
-
-func updateURL(c *gophercloud.ServiceClient, container string) string {
-	return getURL(c, container)
-}
diff --git a/rackspace/objectstorage/v1/cdncontainers/urls_test.go b/rackspace/objectstorage/v1/cdncontainers/urls_test.go
deleted file mode 100644
index aa5bfe6..0000000
--- a/rackspace/objectstorage/v1/cdncontainers/urls_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package cdncontainers
-
-import (
-	"testing"
-
-	"github.com/rackspace/gophercloud"
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-const endpoint = "http://localhost:57909/"
-
-func endpointClient() *gophercloud.ServiceClient {
-	return &gophercloud.ServiceClient{Endpoint: endpoint}
-}
-
-func TestEnableURL(t *testing.T) {
-	actual := enableURL(endpointClient(), "foo")
-	expected := endpoint + "foo"
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/rackspace/objectstorage/v1/cdnobjects/delegate.go b/rackspace/objectstorage/v1/cdnobjects/delegate.go
deleted file mode 100644
index e9d2ff1..0000000
--- a/rackspace/objectstorage/v1/cdnobjects/delegate.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package cdnobjects
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
-)
-
-// Delete is a function that deletes an object from the CDN.
-func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts os.DeleteOptsBuilder) os.DeleteResult {
-	return os.Delete(c, containerName, objectName, nil)
-}
diff --git a/rackspace/objectstorage/v1/cdnobjects/delegate_test.go b/rackspace/objectstorage/v1/cdnobjects/delegate_test.go
deleted file mode 100644
index b5e04a9..0000000
--- a/rackspace/objectstorage/v1/cdnobjects/delegate_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package cdnobjects
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestDeleteCDNObject(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleDeleteObjectSuccessfully(t)
-
-	res := Delete(fake.ServiceClient(), "testContainer", "testObject", nil)
-	th.AssertNoErr(t, res.Err)
-
-}
diff --git a/rackspace/objectstorage/v1/cdnobjects/doc.go b/rackspace/objectstorage/v1/cdnobjects/doc.go
deleted file mode 100644
index 90cd5c9..0000000
--- a/rackspace/objectstorage/v1/cdnobjects/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package cdnobjects provides information and interaction with the CDN
-// Object API resource for the Rackspace Cloud Files service.
-package cdnobjects
diff --git a/rackspace/objectstorage/v1/cdnobjects/request.go b/rackspace/objectstorage/v1/cdnobjects/request.go
deleted file mode 100644
index 540e0cd..0000000
--- a/rackspace/objectstorage/v1/cdnobjects/request.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package cdnobjects
-
-import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/rackspace/objectstorage/v1/cdncontainers"
-)
-
-// CDNURL returns the unique CDN URI for the given container and object.
-func CDNURL(c *gophercloud.ServiceClient, containerName, objectName string) (string, error) {
-	h, err := cdncontainers.Get(c, containerName).Extract()
-	if err != nil {
-		return "", err
-	}
-	return h.CDNUri + "/" + objectName, nil
-}
diff --git a/rackspace/objectstorage/v1/containers/delegate.go b/rackspace/objectstorage/v1/containers/delegate.go
deleted file mode 100644
index 77ed002..0000000
--- a/rackspace/objectstorage/v1/containers/delegate.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package containers
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// ExtractInfo interprets a page of List results when full container info
-// is requested.
-func ExtractInfo(page pagination.Page) ([]os.Container, error) {
-	return os.ExtractInfo(page)
-}
-
-// ExtractNames interprets a page of List results when just the container
-// names are requested.
-func ExtractNames(page pagination.Page) ([]string, error) {
-	return os.ExtractNames(page)
-}
-
-// List is a function that retrieves containers associated with the account as
-// well as account metadata. It returns a pager which can be iterated with the
-// EachPage function.
-func List(c *gophercloud.ServiceClient, opts os.ListOptsBuilder) pagination.Pager {
-	return os.List(c, opts)
-}
-
-// CreateOpts is a structure that holds parameters for creating a container.
-type CreateOpts struct {
-	Metadata         map[string]string
-	ContainerRead    string `h:"X-Container-Read"`
-	ContainerWrite   string `h:"X-Container-Write"`
-	VersionsLocation string `h:"X-Versions-Location"`
-}
-
-// ToContainerCreateMap formats a CreateOpts into a map of headers.
-func (opts CreateOpts) ToContainerCreateMap() (map[string]string, error) {
-	h, err := gophercloud.BuildHeaders(opts)
-	if err != nil {
-		return nil, err
-	}
-	for k, v := range opts.Metadata {
-		h["X-Container-Meta-"+k] = v
-	}
-	return h, nil
-}
-
-// Create is a function that creates a new container.
-func Create(c *gophercloud.ServiceClient, containerName string, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(c, containerName, opts)
-}
-
-// Delete is a function that deletes a container.
-func Delete(c *gophercloud.ServiceClient, containerName string) os.DeleteResult {
-	return os.Delete(c, containerName)
-}
-
-// UpdateOpts is a structure that holds parameters for updating or creating a
-// container's metadata.
-type UpdateOpts struct {
-	Metadata               map[string]string
-	ContainerRead          string `h:"X-Container-Read"`
-	ContainerWrite         string `h:"X-Container-Write"`
-	ContentType            string `h:"Content-Type"`
-	DetectContentType      bool   `h:"X-Detect-Content-Type"`
-	RemoveVersionsLocation string `h:"X-Remove-Versions-Location"`
-	VersionsLocation       string `h:"X-Versions-Location"`
-}
-
-// ToContainerUpdateMap formats a CreateOpts into a map of headers.
-func (opts UpdateOpts) ToContainerUpdateMap() (map[string]string, error) {
-	h, err := gophercloud.BuildHeaders(opts)
-	if err != nil {
-		return nil, err
-	}
-	for k, v := range opts.Metadata {
-		h["X-Container-Meta-"+k] = v
-	}
-	return h, nil
-}
-
-// Update is a function that creates, updates, or deletes a container's
-// metadata.
-func Update(c *gophercloud.ServiceClient, containerName string, opts os.UpdateOptsBuilder) os.UpdateResult {
-	return os.Update(c, containerName, opts)
-}
-
-// Get is a function that retrieves the metadata of a container. To extract just
-// the custom metadata, pass the GetResult response to the ExtractMetadata
-// function.
-func Get(c *gophercloud.ServiceClient, containerName string) os.GetResult {
-	return os.Get(c, containerName)
-}
diff --git a/rackspace/objectstorage/v1/containers/delegate_test.go b/rackspace/objectstorage/v1/containers/delegate_test.go
deleted file mode 100644
index 7ba4eb2..0000000
--- a/rackspace/objectstorage/v1/containers/delegate_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package containers
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListContainerInfo(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListContainerInfoSuccessfully(t)
-
-	count := 0
-	err := List(fake.ServiceClient(), &os.ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractInfo(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, os.ExpectedListInfo, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestListContainerNames(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListContainerNamesSuccessfully(t)
-
-	count := 0
-	err := List(fake.ServiceClient(), &os.ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNames(page)
-		if err != nil {
-			t.Errorf("Failed to extract container names: %v", err)
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, os.ExpectedListNames, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestCreateContainers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleCreateContainerSuccessfully(t)
-
-	options := os.CreateOpts{ContentType: "application/json", Metadata: map[string]string{"foo": "bar"}}
-	res := Create(fake.ServiceClient(), "testContainer", options)
-	th.CheckNoErr(t, res.Err)
-	th.CheckEquals(t, "bar", res.Header["X-Container-Meta-Foo"][0])
-
-}
-
-func TestDeleteContainers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleDeleteContainerSuccessfully(t)
-
-	res := Delete(fake.ServiceClient(), "testContainer")
-	th.CheckNoErr(t, res.Err)
-}
-
-func TestUpdateContainers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleUpdateContainerSuccessfully(t)
-
-	options := &os.UpdateOpts{Metadata: map[string]string{"foo": "bar"}}
-	res := Update(fake.ServiceClient(), "testContainer", options)
-	th.CheckNoErr(t, res.Err)
-}
-
-func TestGetContainers(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGetContainerSuccessfully(t)
-
-	_, err := Get(fake.ServiceClient(), "testContainer").ExtractMetadata()
-	th.CheckNoErr(t, err)
-}
diff --git a/rackspace/objectstorage/v1/containers/doc.go b/rackspace/objectstorage/v1/containers/doc.go
deleted file mode 100644
index d132a07..0000000
--- a/rackspace/objectstorage/v1/containers/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package containers provides information and interaction with the Container
-// API resource for the Rackspace Cloud Files service.
-package containers
diff --git a/rackspace/objectstorage/v1/objects/delegate.go b/rackspace/objectstorage/v1/objects/delegate.go
deleted file mode 100644
index 94c820b..0000000
--- a/rackspace/objectstorage/v1/objects/delegate.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package objects
-
-import (
-	"io"
-
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// ExtractInfo is a function that takes a page of objects and returns their full information.
-func ExtractInfo(page pagination.Page) ([]os.Object, error) {
-	return os.ExtractInfo(page)
-}
-
-// ExtractNames is a function that takes a page of objects and returns only their names.
-func ExtractNames(page pagination.Page) ([]string, error) {
-	return os.ExtractNames(page)
-}
-
-// List is a function that retrieves objects in the container as
-// well as container metadata. It returns a pager which can be iterated with the
-// EachPage function.
-func List(c *gophercloud.ServiceClient, containerName string, opts os.ListOptsBuilder) pagination.Pager {
-	return os.List(c, containerName, opts)
-}
-
-// Download is a function that retrieves the content and metadata for an object.
-// To extract just the content, pass the DownloadResult response to the
-// ExtractContent function.
-func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts os.DownloadOptsBuilder) os.DownloadResult {
-	return os.Download(c, containerName, objectName, opts)
-}
-
-// Create is a function that creates a new object or replaces an existing object.
-func Create(c *gophercloud.ServiceClient, containerName, objectName string, content io.ReadSeeker, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(c, containerName, objectName, content, opts)
-}
-
-// CopyOpts is a structure that holds parameters for copying one object to
-// another.
-type CopyOpts struct {
-	Metadata           map[string]string
-	ContentDisposition string `h:"Content-Disposition"`
-	ContentEncoding    string `h:"Content-Encoding"`
-	ContentLength      int    `h:"Content-Length"`
-	ContentType        string `h:"Content-Type"`
-	CopyFrom           string `h:"X-Copy_From"`
-	Destination        string `h:"Destination"`
-	DetectContentType  bool   `h:"X-Detect-Content-Type"`
-}
-
-// ToObjectCopyMap formats a CopyOpts into a map of headers.
-func (opts CopyOpts) ToObjectCopyMap() (map[string]string, error) {
-	h, err := gophercloud.BuildHeaders(opts)
-	if err != nil {
-		return nil, err
-	}
-	for k, v := range opts.Metadata {
-		h["X-Object-Meta-"+k] = v
-	}
-	// `Content-Length` is required and a value of "0" is acceptable, but calling `gophercloud.BuildHeaders`
-	// will remove the `Content-Length` header if it's set to 0 (or equivalently not set). This will add
-	// the header if it's not already set.
-	if _, ok := h["Content-Length"]; !ok {
-		h["Content-Length"] = "0"
-	}
-	return h, nil
-}
-
-// Copy is a function that copies one object to another.
-func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts os.CopyOptsBuilder) os.CopyResult {
-	return os.Copy(c, containerName, objectName, opts)
-}
-
-// Delete is a function that deletes an object.
-func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts os.DeleteOptsBuilder) os.DeleteResult {
-	return os.Delete(c, containerName, objectName, opts)
-}
-
-// Get is a function that retrieves the metadata of an object. To extract just the custom
-// metadata, pass the GetResult response to the ExtractMetadata function.
-func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts os.GetOptsBuilder) os.GetResult {
-	return os.Get(c, containerName, objectName, opts)
-}
-
-// Update is a function that creates, updates, or deletes an object's metadata.
-func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts os.UpdateOptsBuilder) os.UpdateResult {
-	return os.Update(c, containerName, objectName, opts)
-}
-
-func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName string, opts os.CreateTempURLOpts) (string, error) {
-	return os.CreateTempURL(c, containerName, objectName, opts)
-}
diff --git a/rackspace/objectstorage/v1/objects/delegate_test.go b/rackspace/objectstorage/v1/objects/delegate_test.go
deleted file mode 100644
index 21cd417..0000000
--- a/rackspace/objectstorage/v1/objects/delegate_test.go
+++ /dev/null
@@ -1,127 +0,0 @@
-package objects
-
-import (
-	"strings"
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestDownloadObject(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleDownloadObjectSuccessfully(t)
-
-	content, err := Download(fake.ServiceClient(), "testContainer", "testObject", nil).ExtractContent()
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, string(content), "Successful download with Gophercloud")
-}
-
-func TestListObjectsInfo(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListObjectsInfoSuccessfully(t)
-
-	count := 0
-	options := &os.ListOpts{Full: true}
-	err := List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractInfo(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, os.ExpectedListInfo, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestListObjectNames(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListObjectNamesSuccessfully(t)
-
-	count := 0
-	options := &os.ListOpts{Full: false}
-	err := List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNames(page)
-		if err != nil {
-			t.Errorf("Failed to extract container names: %v", err)
-			return false, err
-		}
-
-		th.CheckDeepEquals(t, os.ExpectedListNames, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestCreateObject(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	
-        content := "Did gyre and gimble in the wabe"
-        os.HandleCreateTextObjectSuccessfully(t, content)
-
-	options := &os.CreateOpts{ContentType: "text/plain"}
-	res := Create(fake.ServiceClient(), "testContainer", "testObject", strings.NewReader(content), options)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestCreateObjectWithoutContentType(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-        content := "The sky was the color of television, tuned to a dead channel."
-	os.HandleCreateTypelessObjectSuccessfully(t, content)
-
-	res := Create(fake.ServiceClient(), "testContainer", "testObject", strings.NewReader(content), &os.CreateOpts{})
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestCopyObject(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleCopyObjectSuccessfully(t)
-
-	options := &CopyOpts{Destination: "/newTestContainer/newTestObject"}
-	res := Copy(fake.ServiceClient(), "testContainer", "testObject", options)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestDeleteObject(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleDeleteObjectSuccessfully(t)
-
-	res := Delete(fake.ServiceClient(), "testContainer", "testObject", nil)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestUpdateObject(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleUpdateObjectSuccessfully(t)
-
-	options := &os.UpdateOpts{Metadata: map[string]string{"Gophercloud-Test": "objects"}}
-	res := Update(fake.ServiceClient(), "testContainer", "testObject", options)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestGetObject(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGetObjectSuccessfully(t)
-
-	expected := map[string]string{"Gophercloud-Test": "objects"}
-	actual, err := Get(fake.ServiceClient(), "testContainer", "testObject", nil).ExtractMetadata()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, expected, actual)
-}
diff --git a/rackspace/objectstorage/v1/objects/doc.go b/rackspace/objectstorage/v1/objects/doc.go
deleted file mode 100644
index 781984b..0000000
--- a/rackspace/objectstorage/v1/objects/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package objects provides information and interaction with the Object
-// API resource for the Rackspace Cloud Files service.
-package objects
diff --git a/rackspace/orchestration/v1/buildinfo/delegate.go b/rackspace/orchestration/v1/buildinfo/delegate.go
deleted file mode 100644
index c834e5c..0000000
--- a/rackspace/orchestration/v1/buildinfo/delegate.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package buildinfo
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/orchestration/v1/buildinfo"
-)
-
-// Get retreives build info data for the Heat deployment.
-func Get(c *gophercloud.ServiceClient) os.GetResult {
-	return os.Get(c)
-}
diff --git a/rackspace/orchestration/v1/buildinfo/delegate_test.go b/rackspace/orchestration/v1/buildinfo/delegate_test.go
deleted file mode 100644
index b25a690..0000000
--- a/rackspace/orchestration/v1/buildinfo/delegate_test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package buildinfo
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/orchestration/v1/buildinfo"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestGetTemplate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGetSuccessfully(t, os.GetOutput)
-
-	actual, err := Get(fake.ServiceClient()).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.GetExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/rackspace/orchestration/v1/buildinfo/doc.go b/rackspace/orchestration/v1/buildinfo/doc.go
deleted file mode 100644
index 183e8df..0000000
--- a/rackspace/orchestration/v1/buildinfo/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package buildinfo provides build information about heat deployments.
-package buildinfo
diff --git a/rackspace/orchestration/v1/stackevents/delegate.go b/rackspace/orchestration/v1/stackevents/delegate.go
deleted file mode 100644
index 08675de..0000000
--- a/rackspace/orchestration/v1/stackevents/delegate.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package stackevents
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Find retreives stack events for the given stack name.
-func Find(c *gophercloud.ServiceClient, stackName string) os.FindResult {
-	return os.Find(c, stackName)
-}
-
-// List makes a request against the API to list resources for the given stack.
-func List(c *gophercloud.ServiceClient, stackName, stackID string, opts os.ListOptsBuilder) pagination.Pager {
-	return os.List(c, stackName, stackID, opts)
-}
-
-// ListResourceEvents makes a request against the API to list resources for the given stack.
-func ListResourceEvents(c *gophercloud.ServiceClient, stackName, stackID, resourceName string, opts os.ListResourceEventsOptsBuilder) pagination.Pager {
-	return os.ListResourceEvents(c, stackName, stackID, resourceName, opts)
-}
-
-// Get retreives data for the given stack resource.
-func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName, eventID string) os.GetResult {
-	return os.Get(c, stackName, stackID, resourceName, eventID)
-}
diff --git a/rackspace/orchestration/v1/stackevents/delegate_test.go b/rackspace/orchestration/v1/stackevents/delegate_test.go
deleted file mode 100644
index e1c0bc8..0000000
--- a/rackspace/orchestration/v1/stackevents/delegate_test.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package stackevents
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestFindEvents(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleFindSuccessfully(t, os.FindOutput)
-
-	actual, err := Find(fake.ServiceClient(), "postman_stack").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.FindExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestList(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListSuccessfully(t, os.ListOutput)
-
-	count := 0
-	err := List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractEvents(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, os.ListExpected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestListResourceEvents(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListResourceEventsSuccessfully(t, os.ListResourceEventsOutput)
-
-	count := 0
-	err := ListResourceEvents(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractEvents(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, os.ListResourceEventsExpected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestGetEvent(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGetSuccessfully(t, os.GetOutput)
-
-	actual, err := Get(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", "93940999-7d40-44ae-8de4-19624e7b8d18").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.GetExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/rackspace/orchestration/v1/stackevents/doc.go b/rackspace/orchestration/v1/stackevents/doc.go
deleted file mode 100644
index dfd6ef6..0000000
--- a/rackspace/orchestration/v1/stackevents/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package stackevents provides operations for finding, listing, and retrieving
-// stack events.
-package stackevents
diff --git a/rackspace/orchestration/v1/stackresources/delegate.go b/rackspace/orchestration/v1/stackresources/delegate.go
deleted file mode 100644
index cb7be28..0000000
--- a/rackspace/orchestration/v1/stackresources/delegate.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package stackresources
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Find retreives stack resources for the given stack name.
-func Find(c *gophercloud.ServiceClient, stackName string) os.FindResult {
-	return os.Find(c, stackName)
-}
-
-// List makes a request against the API to list resources for the given stack.
-func List(c *gophercloud.ServiceClient, stackName, stackID string, opts os.ListOptsBuilder) pagination.Pager {
-	return os.List(c, stackName, stackID, opts)
-}
-
-// Get retreives data for the given stack resource.
-func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) os.GetResult {
-	return os.Get(c, stackName, stackID, resourceName)
-}
-
-// Metadata retreives the metadata for the given stack resource.
-func Metadata(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) os.MetadataResult {
-	return os.Metadata(c, stackName, stackID, resourceName)
-}
-
-// ListTypes makes a request against the API to list resource types.
-func ListTypes(c *gophercloud.ServiceClient) pagination.Pager {
-	return os.ListTypes(c)
-}
-
-// Schema retreives the schema for the given resource type.
-func Schema(c *gophercloud.ServiceClient, resourceType string) os.SchemaResult {
-	return os.Schema(c, resourceType)
-}
-
-// Template retreives the template representation for the given resource type.
-func Template(c *gophercloud.ServiceClient, resourceType string) os.TemplateResult {
-	return os.Template(c, resourceType)
-}
diff --git a/rackspace/orchestration/v1/stackresources/delegate_test.go b/rackspace/orchestration/v1/stackresources/delegate_test.go
deleted file mode 100644
index 116e44c..0000000
--- a/rackspace/orchestration/v1/stackresources/delegate_test.go
+++ /dev/null
@@ -1,108 +0,0 @@
-package stackresources
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestFindResources(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleFindSuccessfully(t, os.FindOutput)
-
-	actual, err := Find(fake.ServiceClient(), "hello_world").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.FindExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestListResources(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListSuccessfully(t, os.ListOutput)
-
-	count := 0
-	err := List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractResources(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, os.ListExpected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestGetResource(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGetSuccessfully(t, os.GetOutput)
-
-	actual, err := Get(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.GetExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestResourceMetadata(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleMetadataSuccessfully(t, os.MetadataOutput)
-
-	actual, err := Metadata(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.MetadataExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestListResourceTypes(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListTypesSuccessfully(t, os.ListTypesOutput)
-
-	count := 0
-	err := ListTypes(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractResourceTypes(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, os.ListTypesExpected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, 1, count)
-}
-
-func TestGetResourceSchema(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGetSchemaSuccessfully(t, os.GetSchemaOutput)
-
-	actual, err := Schema(fake.ServiceClient(), "OS::Heat::AResourceName").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.GetSchemaExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestGetResourceTemplate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGetTemplateSuccessfully(t, os.GetTemplateOutput)
-
-	actual, err := Template(fake.ServiceClient(), "OS::Heat::AResourceName").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.GetTemplateExpected
-	th.AssertDeepEquals(t, expected, string(actual))
-}
diff --git a/rackspace/orchestration/v1/stackresources/doc.go b/rackspace/orchestration/v1/stackresources/doc.go
deleted file mode 100644
index e4f8b08..0000000
--- a/rackspace/orchestration/v1/stackresources/doc.go
+++ /dev/null
@@ -1,5 +0,0 @@
-// Package stackresources provides operations for working with stack resources.
-// A resource is a template artifact that represents some component of your
-// desired architecture (a Cloud Server, a group of scaled Cloud Servers, a load
-// balancer, some configuration management system, and so forth).
-package stackresources
diff --git a/rackspace/orchestration/v1/stacks/delegate.go b/rackspace/orchestration/v1/stacks/delegate.go
deleted file mode 100644
index f7e387f..0000000
--- a/rackspace/orchestration/v1/stacks/delegate.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package stacks
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Create accepts an os.CreateOpts struct and creates a new stack using the values
-// provided.
-func Create(c *gophercloud.ServiceClient, opts os.CreateOptsBuilder) os.CreateResult {
-	return os.Create(c, opts)
-}
-
-// Adopt accepts an os.AdoptOpts struct and creates a new stack from existing stack
-// resources using the values provided.
-func Adopt(c *gophercloud.ServiceClient, opts os.AdoptOptsBuilder) os.AdoptResult {
-	return os.Adopt(c, opts)
-}
-
-// List accepts an os.ListOpts struct and lists stacks based on the options provided.
-func List(c *gophercloud.ServiceClient, opts os.ListOptsBuilder) pagination.Pager {
-	return os.List(c, opts)
-}
-
-// Get retreives a stack based on the stack name and stack ID.
-func Get(c *gophercloud.ServiceClient, stackName, stackID string) os.GetResult {
-	return os.Get(c, stackName, stackID)
-}
-
-// Update accepts an os.UpdateOpts struct and updates a stack based on the options provided.
-func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts os.UpdateOptsBuilder) os.UpdateResult {
-	return os.Update(c, stackName, stackID, opts)
-}
-
-// Delete deletes a stack based on the stack name and stack ID provided.
-func Delete(c *gophercloud.ServiceClient, stackName, stackID string) os.DeleteResult {
-	return os.Delete(c, stackName, stackID)
-}
-
-// Preview provides a preview of a stack based on the options provided.
-func Preview(c *gophercloud.ServiceClient, opts os.PreviewOptsBuilder) os.PreviewResult {
-	return os.Preview(c, opts)
-}
-
-// Abandon abandons a stack, keeping the resources available.
-func Abandon(c *gophercloud.ServiceClient, stackName, stackID string) os.AbandonResult {
-	return os.Abandon(c, stackName, stackID)
-}
diff --git a/rackspace/orchestration/v1/stacks/delegate_test.go b/rackspace/orchestration/v1/stacks/delegate_test.go
deleted file mode 100644
index 553ae94..0000000
--- a/rackspace/orchestration/v1/stacks/delegate_test.go
+++ /dev/null
@@ -1,870 +0,0 @@
-package stacks
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestCreateStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleCreateSuccessfully(t, CreateOutput)
-
-	createOpts := os.CreateOpts{
-		Name:    "stackcreated",
-		Timeout: 60,
-		Template: `{
-      "outputs": {
-        "db_host": {
-          "value": {
-            "get_attr": [
-            "db",
-            "hostname"
-            ]
-          }
-        }
-      },
-      "heat_template_version": "2014-10-16",
-      "description": "HEAT template for creating a Cloud Database.\n",
-      "parameters": {
-        "db_name": {
-          "default": "wordpress",
-          "type": "string",
-          "description": "the name for the database",
-          "constraints": [
-          {
-            "length": {
-              "max": 64,
-              "min": 1
-            },
-            "description": "must be between 1 and 64 characters"
-          },
-          {
-            "allowed_pattern": "[a-zA-Z][a-zA-Z0-9]*",
-            "description": "must begin with a letter and contain only alphanumeric characters."
-          }
-          ]
-        },
-        "db_instance_name": {
-          "default": "Cloud_DB",
-          "type": "string",
-          "description": "the database instance name"
-        },
-        "db_username": {
-          "default": "admin",
-          "hidden": true,
-          "type": "string",
-          "description": "database admin account username",
-          "constraints": [
-          {
-            "length": {
-              "max": 16,
-              "min": 1
-                },
-              "description": "must be between 1 and 16 characters"
-            },
-            {
-              "allowed_pattern": "[a-zA-Z][a-zA-Z0-9]*",
-              "description": "must begin with a letter and contain only alphanumeric characters."
-            }
-          ]
-          },
-          "db_volume_size": {
-            "default": 30,
-            "type": "number",
-            "description": "database volume size (in GB)",
-            "constraints": [
-            {
-              "range": {
-                "max": 1024,
-                "min": 1
-              },
-              "description": "must be between 1 and 1024 GB"
-            }
-            ]
-          },
-          "db_flavor": {
-            "default": "1GB Instance",
-            "type": "string",
-            "description": "database instance size",
-            "constraints": [
-            {
-              "description": "must be a valid cloud database flavor",
-              "allowed_values": [
-              "1GB Instance",
-              "2GB Instance",
-              "4GB Instance",
-              "8GB Instance",
-              "16GB Instance"
-              ]
-            }
-            ]
-          },
-        "db_password": {
-          "default": "admin",
-          "hidden": true,
-          "type": "string",
-          "description": "database admin account password",
-          "constraints": [
-          {
-            "length": {
-              "max": 41,
-              "min": 1
-            },
-            "description": "must be between 1 and 14 characters"
-          },
-          {
-            "allowed_pattern": "[a-zA-Z0-9]*",
-            "description": "must contain only alphanumeric characters."
-          }
-          ]
-        }
-      },
-      "resources": {
-        "db": {
-          "type": "OS::Trove::Instance",
-          "properties": {
-            "flavor": {
-              "get_param": "db_flavor"
-            },
-            "size": {
-              "get_param": "db_volume_size"
-            },
-            "users": [
-            {
-              "password": {
-                "get_param": "db_password"
-              },
-              "name": {
-                "get_param": "db_username"
-              },
-              "databases": [
-              {
-                "get_param": "db_name"
-              }
-              ]
-            }
-            ],
-            "name": {
-              "get_param": "db_instance_name"
-            },
-            "databases": [
-            {
-              "name": {
-                "get_param": "db_name"
-              }
-            }
-            ]
-          }
-        }
-      }
-    }`,
-		DisableRollback: os.Disable,
-	}
-	actual, err := Create(fake.ServiceClient(), createOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := CreateExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestCreateStackNewTemplateFormat(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleCreateSuccessfully(t, CreateOutput)
-
-	createOpts := os.CreateOpts{
-		Name:            "stackcreated",
-		Timeout:         60,
-		TemplateOpts:    new(os.Template),
-		DisableRollback: os.Disable,
-	}
-	createOpts.TemplateOpts.Bin = []byte(`{
-		    "outputs": {
-		        "db_host": {
-		            "value": {
-		                "get_attr": [
-		                    "db",
-		                    "hostname"
-		                ]
-		            }
-		        }
-		    },
-		    "heat_template_version": "2014-10-16",
-		    "description": "HEAT template for creating a Cloud Database.\n",
-		    "parameters": {
-		        "db_name": {
-		            "default": "wordpress",
-		            "type": "string",
-		            "description": "the name for the database",
-		            "constraints": [
-		                {
-		                    "length": {
-		                        "max": 64,
-		                        "min": 1
-		                    },
-		                    "description": "must be between 1 and 64 characters"
-		                },
-		                {
-		                    "allowed_pattern": "[a-zA-Z][a-zA-Z0-9]*",
-		                    "description": "must begin with a letter and contain only alphanumeric characters."
-		                }
-		            ]
-		        },
-		        "db_instance_name": {
-		            "default": "Cloud_DB",
-		            "type": "string",
-		            "description": "the database instance name"
-		        },
-		        "db_username": {
-		            "default": "admin",
-		            "hidden": true,
-		            "type": "string",
-		            "description": "database admin account username",
-		            "constraints": [
-		                {
-		                    "length": {
-		                        "max": 16,
-		                        "min": 1
-		                    },
-		                    "description": "must be between 1 and 16 characters"
-		                },
-		                {
-		                    "allowed_pattern": "[a-zA-Z][a-zA-Z0-9]*",
-		                    "description": "must begin with a letter and contain only alphanumeric characters."
-		                }
-		            ]
-		        },
-		        "db_volume_size": {
-		            "default": 30,
-		            "type": "number",
-		            "description": "database volume size (in GB)",
-		            "constraints": [
-		                {
-		                    "range": {
-		                        "max": 1024,
-		                        "min": 1
-		                    },
-		                    "description": "must be between 1 and 1024 GB"
-		                }
-		            ]
-		        },
-		        "db_flavor": {
-		            "default": "1GB Instance",
-		            "type": "string",
-		            "description": "database instance size",
-		            "constraints": [
-		                {
-		                    "description": "must be a valid cloud database flavor",
-		                    "allowed_values": [
-		                        "1GB Instance",
-		                        "2GB Instance",
-		                        "4GB Instance",
-		                        "8GB Instance",
-		                        "16GB Instance"
-		                    ]
-		                }
-		            ]
-		        },
-		        "db_password": {
-		            "default": "admin",
-		            "hidden": true,
-		            "type": "string",
-		            "description": "database admin account password",
-		            "constraints": [
-		                {
-		                    "length": {
-		                        "max": 41,
-		                        "min": 1
-		                    },
-		                    "description": "must be between 1 and 14 characters"
-		                },
-		                {
-		                    "allowed_pattern": "[a-zA-Z0-9]*",
-		                    "description": "must contain only alphanumeric characters."
-		                }
-		            ]
-		        }
-		    },
-		    "resources": {
-		        "db": {
-		            "type": "OS::Trove::Instance",
-		            "properties": {
-		                "flavor": {
-		                    "get_param": "db_flavor"
-		                },
-		                "size": {
-		                    "get_param": "db_volume_size"
-		                },
-		                "users": [
-		                    {
-		                        "password": {
-		                            "get_param": "db_password"
-		                        },
-		                        "name": {
-		                            "get_param": "db_username"
-		                        },
-		                        "databases": [
-		                            {
-		                                "get_param": "db_name"
-		                            }
-		                        ]
-		                    }
-		                ],
-		                "name": {
-		                    "get_param": "db_instance_name"
-		                },
-		                "databases": [
-		                    {
-		                        "name": {
-		                            "get_param": "db_name"
-		                        }
-		                    }
-		                ]
-		            }
-		        }
-		    }
-		}`)
-	actual, err := Create(fake.ServiceClient(), createOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := CreateExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestAdoptStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleCreateSuccessfully(t, CreateOutput)
-
-	adoptOpts := os.AdoptOpts{
-		AdoptStackData: `{\"environment\":{\"parameters\":{}},    \"status\":\"COMPLETE\",\"name\": \"trovestack\",\n  \"template\": {\n    \"outputs\": {\n      \"db_host\": {\n        \"value\": {\n          \"get_attr\": [\n            \"db\",\n            \"hostname\"\n          ]\n        }\n      }\n    },\n    \"heat_template_version\": \"2014-10-16\",\n    \"description\": \"HEAT template for creating a Cloud Database.\\n\",\n    \"parameters\": {\n      \"db_instance_name\": {\n        \"default\": \"Cloud_DB\",\n        \"type\": \"string\",\n        \"description\": \"the database instance name\"\n      },\n      \"db_flavor\": {\n        \"default\": \"1GB Instance\",\n        \"type\": \"string\",\n        \"description\": \"database instance size\",\n        \"constraints\": [\n          {\n            \"description\": \"must be a valid cloud database flavor\",\n            \"allowed_values\": [\n              \"1GB Instance\",\n              \"2GB Instance\",\n              \"4GB Instance\",\n              \"8GB Instance\",\n              \"16GB Instance\"\n            ]\n          }\n        ]\n      },\n      \"db_password\": {\n        \"default\": \"admin\",\n        \"hidden\": true,\n        \"type\": \"string\",\n        \"description\": \"database admin account password\",\n        \"constraints\": [\n          {\n            \"length\": {\n              \"max\": 41,\n              \"min\": 1\n            },\n            \"description\": \"must be between 1 and 14 characters\"\n          },\n          {\n            \"allowed_pattern\": \"[a-zA-Z0-9]*\",\n            \"description\": \"must contain only alphanumeric characters.\"\n          }\n        ]\n      },\n      \"db_name\": {\n        \"default\": \"wordpress\",\n        \"type\": \"string\",\n        \"description\": \"the name for the database\",\n        \"constraints\": [\n          {\n            \"length\": {\n              \"max\": 64,\n              \"min\": 1\n            },\n            \"description\": \"must be between 1 and 64 characters\"\n          },\n          {\n            \"allowed_pattern\": \"[a-zA-Z][a-zA-Z0-9]*\",\n            \"description\": \"must begin with a letter and contain only alphanumeric characters.\"\n          }\n        ]\n      },\n      \"db_username\": {\n        \"default\": \"admin\",\n        \"hidden\": true,\n        \"type\": \"string\",\n        \"description\": \"database admin account username\",\n        \"constraints\": [\n          {\n            \"length\": {\n              \"max\": 16,\n              \"min\": 1\n            },\n            \"description\": \"must be between 1 and 16 characters\"\n          },\n          {\n            \"allowed_pattern\": \"[a-zA-Z][a-zA-Z0-9]*\",\n            \"description\": \"must begin with a letter and contain only alphanumeric characters.\"\n          }\n        ]\n      },\n      \"db_volume_size\": {\n        \"default\": 30,\n        \"type\": \"number\",\n        \"description\": \"database volume size (in GB)\",\n        \"constraints\": [\n          {\n            \"range\": {\n              \"max\": 1024,\n              \"min\": 1\n            },\n            \"description\": \"must be between 1 and 1024 GB\"\n          }\n        ]\n      }\n    },\n    \"resources\": {\n      \"db\": {\n        \"type\": \"OS::Trove::Instance\",\n        \"properties\": {\n          \"flavor\": {\n            \"get_param\": \"db_flavor\"\n          },\n          \"databases\": [\n            {\n              \"name\": {\n                \"get_param\": \"db_name\"\n              }\n            }\n          ],\n          \"users\": [\n            {\n              \"password\": {\n                \"get_param\": \"db_password\"\n              },\n              \"name\": {\n                \"get_param\": \"db_username\"\n              },\n              \"databases\": [\n                {\n                  \"get_param\": \"db_name\"\n                }\n              ]\n            }\n          ],\n          \"name\": {\n            \"get_param\": \"db_instance_name\"\n          },\n          \"size\": {\n            \"get_param\": \"db_volume_size\"\n          }\n        }\n      }\n    }\n  },\n  \"action\": \"CREATE\",\n  \"id\": \"exxxxd-7xx5-4xxb-bxx2-cxxxxxx5\",\n  \"resources\": {\n    \"db\": {\n      \"status\": \"COMPLETE\",\n      \"name\": \"db\",\n      \"resource_data\": {},\n      \"resource_id\": \"exxxx2-9xx0-4xxxb-bxx2-dxxxxxx4\",\n      \"action\": \"CREATE\",\n      \"type\": \"OS::Trove::Instance\",\n      \"metadata\": {}\n    }\n  }\n},`,
-		Name:           "stackadopted",
-		Timeout:        60,
-		Template: `{
-      "outputs": {
-        "db_host": {
-          "value": {
-            "get_attr": [
-            "db",
-            "hostname"
-            ]
-          }
-        }
-      },
-      "heat_template_version": "2014-10-16",
-      "description": "HEAT template for creating a Cloud Database.\n",
-      "parameters": {
-        "db_name": {
-          "default": "wordpress",
-          "type": "string",
-          "description": "the name for the database",
-          "constraints": [
-          {
-            "length": {
-              "max": 64,
-              "min": 1
-            },
-            "description": "must be between 1 and 64 characters"
-          },
-          {
-            "allowed_pattern": "[a-zA-Z][a-zA-Z0-9]*",
-            "description": "must begin with a letter and contain only alphanumeric characters."
-          }
-          ]
-        },
-        "db_instance_name": {
-          "default": "Cloud_DB",
-          "type": "string",
-          "description": "the database instance name"
-        },
-        "db_username": {
-          "default": "admin",
-          "hidden": true,
-          "type": "string",
-          "description": "database admin account username",
-          "constraints": [
-          {
-            "length": {
-              "max": 16,
-              "min": 1
-            },
-            "description": "must be between 1 and 16 characters"
-          },
-          {
-            "allowed_pattern": "[a-zA-Z][a-zA-Z0-9]*",
-            "description": "must begin with a letter and contain only alphanumeric characters."
-          }
-          ]
-        },
-        "db_volume_size": {
-          "default": 30,
-          "type": "number",
-          "description": "database volume size (in GB)",
-          "constraints": [
-          {
-            "range": {
-              "max": 1024,
-              "min": 1
-            },
-            "description": "must be between 1 and 1024 GB"
-          }
-          ]
-        },
-        "db_flavor": {
-          "default": "1GB Instance",
-          "type": "string",
-          "description": "database instance size",
-          "constraints": [
-          {
-            "description": "must be a valid cloud database flavor",
-            "allowed_values": [
-            "1GB Instance",
-            "2GB Instance",
-            "4GB Instance",
-            "8GB Instance",
-            "16GB Instance"
-            ]
-          }
-          ]
-        },
-        "db_password": {
-          "default": "admin",
-          "hidden": true,
-          "type": "string",
-          "description": "database admin account password",
-          "constraints": [
-          {
-            "length": {
-              "max": 41,
-              "min": 1
-            },
-            "description": "must be between 1 and 14 characters"
-          },
-          {
-            "allowed_pattern": "[a-zA-Z0-9]*",
-            "description": "must contain only alphanumeric characters."
-          }
-          ]
-        }
-      },
-      "resources": {
-        "db": {
-          "type": "OS::Trove::Instance",
-          "properties": {
-            "flavor": {
-              "get_param": "db_flavor"
-            },
-            "size": {
-              "get_param": "db_volume_size"
-            },
-            "users": [
-            {
-              "password": {
-                "get_param": "db_password"
-              },
-              "name": {
-                "get_param": "db_username"
-              },
-              "databases": [
-              {
-                "get_param": "db_name"
-              }
-              ]
-            }
-            ],
-            "name": {
-              "get_param": "db_instance_name"
-            },
-            "databases": [
-            {
-              "name": {
-                "get_param": "db_name"
-              }
-            }
-            ]
-          }
-        }
-      }
-    }`,
-		DisableRollback: os.Disable,
-	}
-	actual, err := Adopt(fake.ServiceClient(), adoptOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := CreateExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestAdoptStackNewTemplateFormat(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleCreateSuccessfully(t, CreateOutput)
-	template := new(os.Template)
-	template.Bin = []byte(`{
-  "outputs": {
-	"db_host": {
-	  "value": {
-		"get_attr": [
-		"db",
-		"hostname"
-		]
-	  }
-	}
-  },
-  "heat_template_version": "2014-10-16",
-  "description": "HEAT template for creating a Cloud Database.\n",
-  "parameters": {
-	"db_name": {
-	  "default": "wordpress",
-	  "type": "string",
-	  "description": "the name for the database",
-	  "constraints": [
-	  {
-		"length": {
-		  "max": 64,
-		  "min": 1
-		},
-		"description": "must be between 1 and 64 characters"
-	  },
-	  {
-		"allowed_pattern": "[a-zA-Z][a-zA-Z0-9]*",
-		"description": "must begin with a letter and contain only alphanumeric characters."
-	  }
-	  ]
-	},
-	"db_instance_name": {
-	  "default": "Cloud_DB",
-	  "type": "string",
-	  "description": "the database instance name"
-	},
-	"db_username": {
-	  "default": "admin",
-	  "hidden": true,
-	  "type": "string",
-	  "description": "database admin account username",
-	  "constraints": [
-	  {
-		"length": {
-		  "max": 16,
-		  "min": 1
-		},
-		"description": "must be between 1 and 16 characters"
-	  },
-	  {
-		"allowed_pattern": "[a-zA-Z][a-zA-Z0-9]*",
-		"description": "must begin with a letter and contain only alphanumeric characters."
-	  }
-	  ]
-	},
-	"db_volume_size": {
-	  "default": 30,
-	  "type": "number",
-	  "description": "database volume size (in GB)",
-	  "constraints": [
-	  {
-		"range": {
-		  "max": 1024,
-		  "min": 1
-		},
-		"description": "must be between 1 and 1024 GB"
-	  }
-	  ]
-	},
-	"db_flavor": {
-	  "default": "1GB Instance",
-	  "type": "string",
-	  "description": "database instance size",
-	  "constraints": [
-	  {
-		"description": "must be a valid cloud database flavor",
-		"allowed_values": [
-		"1GB Instance",
-		"2GB Instance",
-		"4GB Instance",
-		"8GB Instance",
-		"16GB Instance"
-		]
-	  }
-	  ]
-	},
-	"db_password": {
-	  "default": "admin",
-	  "hidden": true,
-	  "type": "string",
-	  "description": "database admin account password",
-	  "constraints": [
-	  {
-		"length": {
-		  "max": 41,
-		  "min": 1
-		},
-		"description": "must be between 1 and 14 characters"
-	  },
-	  {
-		"allowed_pattern": "[a-zA-Z0-9]*",
-		"description": "must contain only alphanumeric characters."
-	  }
-	  ]
-	}
-  },
-  "resources": {
-	"db": {
-	  "type": "OS::Trove::Instance",
-	  "properties": {
-		"flavor": {
-		  "get_param": "db_flavor"
-		},
-		"size": {
-		  "get_param": "db_volume_size"
-		},
-		"users": [
-		{
-		  "password": {
-			"get_param": "db_password"
-		  },
-		  "name": {
-			"get_param": "db_username"
-		  },
-		  "databases": [
-		  {
-			"get_param": "db_name"
-		  }
-		  ]
-		}
-		],
-		"name": {
-		  "get_param": "db_instance_name"
-		},
-		"databases": [
-		{
-		  "name": {
-			"get_param": "db_name"
-		  }
-		}
-		]
-	  }
-	}
-  }
-}`)
-
-	adoptOpts := os.AdoptOpts{
-		AdoptStackData:  `{\"environment\":{\"parameters\":{}},    \"status\":\"COMPLETE\",\"name\": \"trovestack\",\n  \"template\": {\n    \"outputs\": {\n      \"db_host\": {\n        \"value\": {\n          \"get_attr\": [\n            \"db\",\n            \"hostname\"\n          ]\n        }\n      }\n    },\n    \"heat_template_version\": \"2014-10-16\",\n    \"description\": \"HEAT template for creating a Cloud Database.\\n\",\n    \"parameters\": {\n      \"db_instance_name\": {\n        \"default\": \"Cloud_DB\",\n        \"type\": \"string\",\n        \"description\": \"the database instance name\"\n      },\n      \"db_flavor\": {\n        \"default\": \"1GB Instance\",\n        \"type\": \"string\",\n        \"description\": \"database instance size\",\n        \"constraints\": [\n          {\n            \"description\": \"must be a valid cloud database flavor\",\n            \"allowed_values\": [\n              \"1GB Instance\",\n              \"2GB Instance\",\n              \"4GB Instance\",\n              \"8GB Instance\",\n              \"16GB Instance\"\n            ]\n          }\n        ]\n      },\n      \"db_password\": {\n        \"default\": \"admin\",\n        \"hidden\": true,\n        \"type\": \"string\",\n        \"description\": \"database admin account password\",\n        \"constraints\": [\n          {\n            \"length\": {\n              \"max\": 41,\n              \"min\": 1\n            },\n            \"description\": \"must be between 1 and 14 characters\"\n          },\n          {\n            \"allowed_pattern\": \"[a-zA-Z0-9]*\",\n            \"description\": \"must contain only alphanumeric characters.\"\n          }\n        ]\n      },\n      \"db_name\": {\n        \"default\": \"wordpress\",\n        \"type\": \"string\",\n        \"description\": \"the name for the database\",\n        \"constraints\": [\n          {\n            \"length\": {\n              \"max\": 64,\n              \"min\": 1\n            },\n            \"description\": \"must be between 1 and 64 characters\"\n          },\n          {\n            \"allowed_pattern\": \"[a-zA-Z][a-zA-Z0-9]*\",\n            \"description\": \"must begin with a letter and contain only alphanumeric characters.\"\n          }\n        ]\n      },\n      \"db_username\": {\n        \"default\": \"admin\",\n        \"hidden\": true,\n        \"type\": \"string\",\n        \"description\": \"database admin account username\",\n        \"constraints\": [\n          {\n            \"length\": {\n              \"max\": 16,\n              \"min\": 1\n            },\n            \"description\": \"must be between 1 and 16 characters\"\n          },\n          {\n            \"allowed_pattern\": \"[a-zA-Z][a-zA-Z0-9]*\",\n            \"description\": \"must begin with a letter and contain only alphanumeric characters.\"\n          }\n        ]\n      },\n      \"db_volume_size\": {\n        \"default\": 30,\n        \"type\": \"number\",\n        \"description\": \"database volume size (in GB)\",\n        \"constraints\": [\n          {\n            \"range\": {\n              \"max\": 1024,\n              \"min\": 1\n            },\n            \"description\": \"must be between 1 and 1024 GB\"\n          }\n        ]\n      }\n    },\n    \"resources\": {\n      \"db\": {\n        \"type\": \"OS::Trove::Instance\",\n        \"properties\": {\n          \"flavor\": {\n            \"get_param\": \"db_flavor\"\n          },\n          \"databases\": [\n            {\n              \"name\": {\n                \"get_param\": \"db_name\"\n              }\n            }\n          ],\n          \"users\": [\n            {\n              \"password\": {\n                \"get_param\": \"db_password\"\n              },\n              \"name\": {\n                \"get_param\": \"db_username\"\n              },\n              \"databases\": [\n                {\n                  \"get_param\": \"db_name\"\n                }\n              ]\n            }\n          ],\n          \"name\": {\n            \"get_param\": \"db_instance_name\"\n          },\n          \"size\": {\n            \"get_param\": \"db_volume_size\"\n          }\n        }\n      }\n    }\n  },\n  \"action\": \"CREATE\",\n  \"id\": \"exxxxd-7xx5-4xxb-bxx2-cxxxxxx5\",\n  \"resources\": {\n    \"db\": {\n      \"status\": \"COMPLETE\",\n      \"name\": \"db\",\n      \"resource_data\": {},\n      \"resource_id\": \"exxxx2-9xx0-4xxxb-bxx2-dxxxxxx4\",\n      \"action\": \"CREATE\",\n      \"type\": \"OS::Trove::Instance\",\n      \"metadata\": {}\n    }\n  }\n},`,
-		Name:            "stackadopted",
-		Timeout:         60,
-		TemplateOpts:    template,
-		DisableRollback: os.Disable,
-	}
-	actual, err := Adopt(fake.ServiceClient(), adoptOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := CreateExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestListStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleListSuccessfully(t, os.FullListOutput)
-
-	count := 0
-	err := List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := os.ExtractStacks(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, os.ListExpected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestUpdateStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleUpdateSuccessfully(t)
-
-	updateOpts := os.UpdateOpts{
-		Template: `
-    {
-      "heat_template_version": "2013-05-23",
-      "description": "Simple template to test heat commands",
-      "parameters": {
-        "flavor": {
-          "default": "m1.tiny",
-          "type": "string"
-        }
-      },
-      "resources": {
-        "hello_world": {
-          "type":"OS::Nova::Server",
-          "properties": {
-            "key_name": "heat_key",
-            "flavor": {
-              "get_param": "flavor"
-            },
-            "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-            "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-          }
-        }
-      }
-    }`,
-	}
-	err := Update(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestUpdateStackNewTemplateFormat(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleUpdateSuccessfully(t)
-
-	updateOpts := os.UpdateOpts{
-		TemplateOpts: new(os.Template),
-	}
-	updateOpts.TemplateOpts.Bin = []byte(`
-		{
-			"stack_name": "postman_stack",
-			"template": {
-				"heat_template_version": "2013-05-23",
-				"description": "Simple template to test heat commands",
-				"parameters": {
-					"flavor": {
-						"default": "m1.tiny",
-						"type": "string"
-					}
-				},
-				"resources": {
-					"hello_world": {
-						"type": "OS::Nova::Server",
-						"properties": {
-							"key_name": "heat_key",
-							"flavor": {
-								"get_param": "flavor"
-							},
-							"image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-							"user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-						}
-					}
-				}
-			}
-		}`)
-	err := Update(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDeleteStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleDeleteSuccessfully(t)
-
-	err := Delete(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada").ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestPreviewStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandlePreviewSuccessfully(t, os.GetOutput)
-
-	previewOpts := os.PreviewOpts{
-		Name:    "stackcreated",
-		Timeout: 60,
-		Template: `
-    {
-      "stack_name": "postman_stack",
-      "template": {
-        "heat_template_version": "2013-05-23",
-        "description": "Simple template to test heat commands",
-        "parameters": {
-          "flavor": {
-            "default": "m1.tiny",
-            "type": "string"
-          }
-        },
-        "resources": {
-          "hello_world": {
-            "type":"OS::Nova::Server",
-            "properties": {
-              "key_name": "heat_key",
-              "flavor": {
-                "get_param": "flavor"
-              },
-              "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-              "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-            }
-          }
-        }
-      }
-    }`,
-		DisableRollback: os.Disable,
-	}
-	actual, err := Preview(fake.ServiceClient(), previewOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.PreviewExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestPreviewStackNewTemplateFormat(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandlePreviewSuccessfully(t, os.GetOutput)
-
-	previewOpts := os.PreviewOpts{
-		Name:            "stackcreated",
-		Timeout:         60,
-		TemplateOpts:    new(os.Template),
-		DisableRollback: os.Disable,
-	}
-	previewOpts.TemplateOpts.Bin = []byte(`
-		{
-		    "stack_name": "postman_stack",
-		    "template": {
-		        "heat_template_version": "2013-05-23",
-		        "description": "Simple template to test heat commands",
-		        "parameters": {
-		            "flavor": {
-		                "default": "m1.tiny",
-		                "type": "string"
-		            }
-		        },
-		        "resources": {
-		            "hello_world": {
-		                "type": "OS::Nova::Server",
-		                "properties": {
-		                    "key_name": "heat_key",
-		                    "flavor": {
-		                        "get_param": "flavor"
-		                    },
-		                    "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-		                    "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
-		                }
-		            }
-		        }
-		    }
-		}`)
-	actual, err := Preview(fake.ServiceClient(), previewOpts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.PreviewExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestAbandonStack(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleAbandonSuccessfully(t, os.AbandonOutput)
-
-	actual, err := Abandon(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c8").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.AbandonExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/rackspace/orchestration/v1/stacks/doc.go b/rackspace/orchestration/v1/stacks/doc.go
deleted file mode 100644
index 19231b5..0000000
--- a/rackspace/orchestration/v1/stacks/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Package stacks provides operation for working with Heat stacks. A stack is a
-// group of resources (servers, load balancers, databases, and so forth)
-// combined to fulfill a useful purpose. Based on a template, Heat orchestration
-// engine creates an instantiated set of resources (a stack) to run the
-// application framework or component specified (in the template). A stack is a
-// running instance of a template. The result of creating a stack is a deployment
-// of the application framework or component.
-package stacks
diff --git a/rackspace/orchestration/v1/stacks/fixtures.go b/rackspace/orchestration/v1/stacks/fixtures.go
deleted file mode 100644
index c79c2a6..0000000
--- a/rackspace/orchestration/v1/stacks/fixtures.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// +build fixtures
-
-package stacks
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
-)
-
-// CreateExpected represents the expected object from a Create request.
-var CreateExpected = &os.CreatedStack{
-	ID: "b663e18a-4767-4cdf-9db5-9c8cc13cc38a",
-	Links: []gophercloud.Link{
-		gophercloud.Link{
-			Href: "https://ord.orchestration.api.rackspacecloud.com/v1/864477/stacks/stackcreated/b663e18a-4767-4cdf-9db5-9c8cc13cc38a",
-			Rel:  "self",
-		},
-	},
-}
-
-// CreateOutput represents the response body from a Create request.
-const CreateOutput = `
-{
-  "stack": {
-    "id": "b663e18a-4767-4cdf-9db5-9c8cc13cc38a",
-    "links": [
-    {
-      "href": "https://ord.orchestration.api.rackspacecloud.com/v1/864477/stacks/stackcreated/b663e18a-4767-4cdf-9db5-9c8cc13cc38a",
-      "rel": "self"
-    }
-    ]
-  }
-}
-`
diff --git a/rackspace/orchestration/v1/stacktemplates/delegate.go b/rackspace/orchestration/v1/stacktemplates/delegate.go
deleted file mode 100644
index 3b5d46e..0000000
--- a/rackspace/orchestration/v1/stacktemplates/delegate.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package stacktemplates
-
-import (
-	"github.com/rackspace/gophercloud"
-	os "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacktemplates"
-)
-
-// Get retreives data for the given stack template.
-func Get(c *gophercloud.ServiceClient, stackName, stackID string) os.GetResult {
-	return os.Get(c, stackName, stackID)
-}
-
-// Validate validates the given stack template.
-func Validate(c *gophercloud.ServiceClient, opts os.ValidateOptsBuilder) os.ValidateResult {
-	return os.Validate(c, opts)
-}
diff --git a/rackspace/orchestration/v1/stacktemplates/delegate_test.go b/rackspace/orchestration/v1/stacktemplates/delegate_test.go
deleted file mode 100644
index d4d0f8f..0000000
--- a/rackspace/orchestration/v1/stacktemplates/delegate_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package stacktemplates
-
-import (
-	"testing"
-
-	os "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacktemplates"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestGetTemplate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleGetSuccessfully(t, os.GetOutput)
-
-	actual, err := Get(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.GetExpected
-	th.AssertDeepEquals(t, expected, string(actual))
-}
-
-func TestValidateTemplate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	os.HandleValidateSuccessfully(t, os.ValidateOutput)
-
-	opts := os.ValidateOpts{
-		Template: `{
-			"Description": "Simple template to test heat commands",
-			"Parameters": {
-				"flavor": {
-					"Default": "m1.tiny",
-					"Type": "String",
-					"NoEcho": "false",
-					"Description": "",
-					"Label": "flavor"
-				}
-			}
-		}`,
-	}
-	actual, err := Validate(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-
-	expected := os.ValidateExpected
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/rackspace/orchestration/v1/stacktemplates/doc.go b/rackspace/orchestration/v1/stacktemplates/doc.go
deleted file mode 100644
index 5af0bd6..0000000
--- a/rackspace/orchestration/v1/stacktemplates/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Package stacktemplates provides operations for working with Heat templates.
-// A Cloud Orchestration template is a portable file, written in a user-readable
-// language, that describes how a set of resources should be assembled and what
-// software should be installed in order to produce a working stack. The template
-// specifies what resources should be used, what attributes can be set, and other
-// parameters that are critical to the successful, repeatable automation of a
-// specific application stack.
-package stacktemplates
diff --git a/rackspace/rackconnect/v3/cloudnetworks/requests.go b/rackspace/rackconnect/v3/cloudnetworks/requests.go
deleted file mode 100644
index 5884303..0000000
--- a/rackspace/rackconnect/v3/cloudnetworks/requests.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package cloudnetworks
-
-import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns all cloud networks that are associated with RackConnect. The ID
-// returned for each network is the same as the ID returned by the networks package.
-func List(c *gophercloud.ServiceClient) pagination.Pager {
-	url := listURL(c)
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return CloudNetworkPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(c, url, createPage)
-}
-
-// Get retrieves a specific cloud network (that is associated with RackConnect)
-// based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
-}
diff --git a/rackspace/rackconnect/v3/cloudnetworks/requests_test.go b/rackspace/rackconnect/v3/cloudnetworks/requests_test.go
deleted file mode 100644
index 10d15dd..0000000
--- a/rackspace/rackconnect/v3/cloudnetworks/requests_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package cloudnetworks
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListCloudNetworks(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/cloud_networks", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		fmt.Fprintf(w, `[{
-      "cidr": "192.168.100.0/24",
-      "created": "2014-05-25T01:23:42Z",
-      "id": "07426958-1ebf-4c38-b032-d456820ca21a",
-      "name": "RC-CLOUD",
-      "updated": "2014-05-25T02:28:44Z"
-    }]`)
-	})
-
-	expected := []CloudNetwork{
-		CloudNetwork{
-			CIDR:      "192.168.100.0/24",
-			CreatedAt: time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC),
-			ID:        "07426958-1ebf-4c38-b032-d456820ca21a",
-			Name:      "RC-CLOUD",
-			UpdatedAt: time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC),
-		},
-	}
-
-	count := 0
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractCloudNetworks(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestGetCloudNetwork(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/cloud_networks/07426958-1ebf-4c38-b032-d456820ca21a", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `{
-      "cidr": "192.168.100.0/24",
-      "created": "2014-05-25T01:23:42Z",
-      "id": "07426958-1ebf-4c38-b032-d456820ca21a",
-      "name": "RC-CLOUD",
-      "updated": "2014-05-25T02:28:44Z"
-    }`)
-	})
-
-	expected := &CloudNetwork{
-		CIDR:      "192.168.100.0/24",
-		CreatedAt: time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC),
-		ID:        "07426958-1ebf-4c38-b032-d456820ca21a",
-		Name:      "RC-CLOUD",
-		UpdatedAt: time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC),
-	}
-
-	actual, err := Get(fake.ServiceClient(), "07426958-1ebf-4c38-b032-d456820ca21a").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertDeepEquals(t, expected, actual)
-}
diff --git a/rackspace/rackconnect/v3/cloudnetworks/results.go b/rackspace/rackconnect/v3/cloudnetworks/results.go
deleted file mode 100644
index f554a0d..0000000
--- a/rackspace/rackconnect/v3/cloudnetworks/results.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package cloudnetworks
-
-import (
-	"fmt"
-	"reflect"
-	"time"
-
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// CloudNetwork represents a network associated with a RackConnect configuration.
-type CloudNetwork struct {
-	// Specifies the ID of the newtork.
-	ID string `mapstructure:"id"`
-	// Specifies the user-provided name of the network.
-	Name string `mapstructure:"name"`
-	// Specifies the IP range for this network.
-	CIDR string `mapstructure:"cidr"`
-	// Specifies the time the network was created.
-	CreatedAt time.Time `mapstructure:"-"`
-	// Specifies the time the network was last updated.
-	UpdatedAt time.Time `mapstructure:"-"`
-}
-
-// CloudNetworkPage is the page returned by a pager when traversing over a
-// collection of CloudNetworks.
-type CloudNetworkPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty returns true if a CloudNetworkPage contains no CloudNetworks.
-func (r CloudNetworkPage) IsEmpty() (bool, error) {
-	cns, err := ExtractCloudNetworks(r)
-	if err != nil {
-		return true, err
-	}
-	return len(cns) == 0, nil
-}
-
-// ExtractCloudNetworks extracts and returns CloudNetworks. It is used while iterating over
-// a cloudnetworks.List call.
-func ExtractCloudNetworks(page pagination.Page) ([]CloudNetwork, error) {
-	var res []CloudNetwork
-	casted := page.(CloudNetworkPage).Body
-	err := mapstructure.Decode(casted, &res)
-
-	var rawNets []interface{}
-	switch casted.(type) {
-	case interface{}:
-		rawNets = casted.([]interface{})
-	default:
-		return res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i := range rawNets {
-		thisNet := (rawNets[i]).(map[string]interface{})
-
-		if t, ok := thisNet["created"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return res, err
-			}
-			res[i].CreatedAt = creationTime
-		}
-
-		if t, ok := thisNet["updated"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return res, err
-			}
-			res[i].UpdatedAt = updatedTime
-		}
-	}
-
-	return res, err
-}
-
-// GetResult represents the result of a Get operation.
-type GetResult struct {
-	gophercloud.Result
-}
-
-// Extract is a function that extracts a CloudNetwork from a GetResult.
-func (r GetResult) Extract() (*CloudNetwork, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-	var res CloudNetwork
-
-	err := mapstructure.Decode(r.Body, &res)
-
-	b := r.Body.(map[string]interface{})
-
-	if date, ok := b["created"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.CreatedAt = t
-	}
-
-	if date, ok := b["updated"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.UpdatedAt = t
-	}
-
-	return &res, err
-}
diff --git a/rackspace/rackconnect/v3/cloudnetworks/urls.go b/rackspace/rackconnect/v3/cloudnetworks/urls.go
deleted file mode 100644
index bd6b098..0000000
--- a/rackspace/rackconnect/v3/cloudnetworks/urls.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package cloudnetworks
-
-import "github.com/rackspace/gophercloud"
-
-func listURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL("cloud_networks")
-}
-
-func getURL(c *gophercloud.ServiceClient, id string) string {
-	return c.ServiceURL("cloud_networks", id)
-}
diff --git a/rackspace/rackconnect/v3/doc.go b/rackspace/rackconnect/v3/doc.go
deleted file mode 100644
index 3a8279e..0000000
--- a/rackspace/rackconnect/v3/doc.go
+++ /dev/null
@@ -1,4 +0,0 @@
-// Package rackconnect allows Rackspace cloud accounts to leverage version 3 of
-// RackConnect, Rackspace's hybrid connectivity solution connecting dedicated
-// and cloud servers.
-package rackconnect
diff --git a/rackspace/rackconnect/v3/lbpools/doc.go b/rackspace/rackconnect/v3/lbpools/doc.go
deleted file mode 100644
index f4319b8..0000000
--- a/rackspace/rackconnect/v3/lbpools/doc.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Package lbpools provides access to load balancer pools associated with a
-// RackConnect configuration. Load Balancer Pools must be configured in advance
-// by your Network Security team to be eligible for use with RackConnect.
-// If you do not see a pool that you expect to see, contact your Support team
-// for further assistance. The Load Balancer Pool id returned by these calls is
-// automatically generated by the RackConnect automation and will remain constant
-// unless the Load Balancer Pool is renamed on your hardware load balancer.
-// All Load Balancer Pools will currently return a status of ACTIVE. Future
-// features may introduce additional statuses.
-// Node status values are ADDING, ACTIVE, REMOVING, ADD_FAILED, and REMOVE_FAILED.
-// The cloud_servers node count will only include Cloud Servers from the specified
-// cloud account. Any dedicated servers or cloud servers from another cloud account
-// on the same RackConnect Configuration will be counted as external nodes.
-package lbpools
diff --git a/rackspace/rackconnect/v3/lbpools/requests.go b/rackspace/rackconnect/v3/lbpools/requests.go
deleted file mode 100644
index c300c56..0000000
--- a/rackspace/rackconnect/v3/lbpools/requests.go
+++ /dev/null
@@ -1,146 +0,0 @@
-package lbpools
-
-import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns all load balancer pools that are associated with RackConnect.
-func List(c *gophercloud.ServiceClient) pagination.Pager {
-	url := listURL(c)
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return PoolPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(c, url, createPage)
-}
-
-// Get retrieves a specific load balancer pool (that is associated with RackConnect)
-// based on its unique ID.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
-}
-
-// ListNodes returns all load balancer pool nodes that are associated with RackConnect
-// for the given LB pool ID.
-func ListNodes(c *gophercloud.ServiceClient, id string) pagination.Pager {
-	url := listNodesURL(c, id)
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return NodePage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(c, url, createPage)
-}
-
-// CreateNode adds the cloud server with the given serverID to the load balancer
-// pool with the given poolID.
-func CreateNode(c *gophercloud.ServiceClient, poolID, serverID string) CreateNodeResult {
-	var res CreateNodeResult
-	reqBody := map[string]interface{}{
-		"cloud_server": map[string]string{
-			"id": serverID,
-		},
-	}
-	_, res.Err = c.Post(createNodeURL(c, poolID), reqBody, &res.Body, nil)
-	return res
-}
-
-// ListNodesDetails returns all load balancer pool nodes that are associated with RackConnect
-// for the given LB pool ID with all their details.
-func ListNodesDetails(c *gophercloud.ServiceClient, id string) pagination.Pager {
-	url := listNodesDetailsURL(c, id)
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return NodeDetailsPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(c, url, createPage)
-}
-
-// GetNode retrieves a specific LB pool node (that is associated with RackConnect)
-// based on its unique ID and the LB pool's unique ID.
-func GetNode(c *gophercloud.ServiceClient, poolID, nodeID string) GetNodeResult {
-	var res GetNodeResult
-	_, res.Err = c.Get(nodeURL(c, poolID, nodeID), &res.Body, nil)
-	return res
-}
-
-// DeleteNode removes the node with the given nodeID from the LB pool with the
-// given poolID.
-func DeleteNode(c *gophercloud.ServiceClient, poolID, nodeID string) DeleteNodeResult {
-	var res DeleteNodeResult
-	_, res.Err = c.Delete(deleteNodeURL(c, poolID, nodeID), nil)
-	return res
-}
-
-// GetNodeDetails retrieves a specific LB pool node's details based on its unique
-// ID and the LB pool's unique ID.
-func GetNodeDetails(c *gophercloud.ServiceClient, poolID, nodeID string) GetNodeDetailsResult {
-	var res GetNodeDetailsResult
-	_, res.Err = c.Get(nodeDetailsURL(c, poolID, nodeID), &res.Body, nil)
-	return res
-}
-
-// NodeOpts are options for bulk adding/deleting nodes to LB pools.
-type NodeOpts struct {
-	ServerID string
-	PoolID   string
-}
-
-// NodesOpts are a slice of NodeOpts, passed as options for bulk operations.
-type NodesOpts []NodeOpts
-
-// ToLBPoolCreateNodesMap serializes a NodesOpts into a map to send in the request.
-func (o NodesOpts) ToLBPoolCreateNodesMap() ([]map[string]interface{}, error) {
-	m := make([]map[string]interface{}, len(o))
-	for i := range o {
-		m[i] = map[string]interface{}{
-			"cloud_server": map[string]string{
-				"id": o[i].ServerID,
-			},
-			"load_balancer_pool": map[string]string{
-				"id": o[i].PoolID,
-			},
-		}
-	}
-	return m, nil
-}
-
-// CreateNodes adds the cloud servers with the given serverIDs to the corresponding
-// load balancer pools with the given poolIDs.
-func CreateNodes(c *gophercloud.ServiceClient, opts NodesOpts) CreateNodesResult {
-	var res CreateNodesResult
-	reqBody, err := opts.ToLBPoolCreateNodesMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Post(createNodesURL(c), reqBody, &res.Body, nil)
-	return res
-}
-
-// DeleteNodes removes the cloud servers with the given serverIDs to the corresponding
-// load balancer pools with the given poolIDs.
-func DeleteNodes(c *gophercloud.ServiceClient, opts NodesOpts) DeleteNodesResult {
-	var res DeleteNodesResult
-	reqBody, err := opts.ToLBPoolCreateNodesMap()
-	if err != nil {
-		res.Err = err
-		return res
-	}
-
-	_, res.Err = c.Request("DELETE", createNodesURL(c), gophercloud.RequestOpts{
-		JSONBody: &reqBody,
-		OkCodes:  []int{204},
-	})
-	return res
-}
-
-// ListNodesDetailsForServer is similar to ListNodesDetails but only returns nodes
-// for the given serverID.
-func ListNodesDetailsForServer(c *gophercloud.ServiceClient, serverID string) pagination.Pager {
-	url := listNodesForServerURL(c, serverID)
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return NodeDetailsForServerPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(c, url, createPage)
-}
diff --git a/rackspace/rackconnect/v3/lbpools/requests_test.go b/rackspace/rackconnect/v3/lbpools/requests_test.go
deleted file mode 100644
index 48ebcec..0000000
--- a/rackspace/rackconnect/v3/lbpools/requests_test.go
+++ /dev/null
@@ -1,876 +0,0 @@
-package lbpools
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListPools(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/load_balancer_pools", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		fmt.Fprintf(w, `[
-      {
-        "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-        "name": "RCv3Test",
-        "node_counts": {
-          "cloud_servers": 3,
-          "external": 4,
-          "total": 7
-        },
-        "port": 80,
-        "status": "ACTIVE",
-        "status_detail": null,
-        "virtual_ip": "203.0.113.5"
-      },
-      {
-        "id": "33021100-4abf-4836-9080-465a6d87ab68",
-        "name": "RCv3Test2",
-        "node_counts": {
-          "cloud_servers": 1,
-          "external": 0,
-          "total": 1
-        },
-        "port": 80,
-        "status": "ACTIVE",
-        "status_detail": null,
-        "virtual_ip": "203.0.113.7"
-      },
-      {
-        "id": "b644350a-301b-47b5-a411-c6e0f933c347",
-        "name": "RCv3Test3",
-        "node_counts": {
-          "cloud_servers": 2,
-          "external": 3,
-          "total": 5
-        },
-        "port": 443,
-        "status": "ACTIVE",
-        "status_detail": null,
-        "virtual_ip": "203.0.113.15"
-      }
-    ]`)
-	})
-
-	expected := []Pool{
-		Pool{
-			ID:   "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-			Name: "RCv3Test",
-			NodeCounts: struct {
-				CloudServers int `mapstructure:"cloud_servers"`
-				External     int `mapstructure:"external"`
-				Total        int `mapstructure:"total"`
-			}{
-				CloudServers: 3,
-				External:     4,
-				Total:        7,
-			},
-			Port:      80,
-			Status:    "ACTIVE",
-			VirtualIP: "203.0.113.5",
-		},
-		Pool{
-			ID:   "33021100-4abf-4836-9080-465a6d87ab68",
-			Name: "RCv3Test2",
-			NodeCounts: struct {
-				CloudServers int `mapstructure:"cloud_servers"`
-				External     int `mapstructure:"external"`
-				Total        int `mapstructure:"total"`
-			}{
-				CloudServers: 1,
-				External:     0,
-				Total:        1,
-			},
-			Port:      80,
-			Status:    "ACTIVE",
-			VirtualIP: "203.0.113.7",
-		},
-		Pool{
-			ID:   "b644350a-301b-47b5-a411-c6e0f933c347",
-			Name: "RCv3Test3",
-			NodeCounts: struct {
-				CloudServers int `mapstructure:"cloud_servers"`
-				External     int `mapstructure:"external"`
-				Total        int `mapstructure:"total"`
-			}{
-				CloudServers: 2,
-				External:     3,
-				Total:        5,
-			},
-			Port:      443,
-			Status:    "ACTIVE",
-			VirtualIP: "203.0.113.15",
-		},
-	}
-
-	count := 0
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractPools(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestGetLBPool(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `{
-      "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-      "name": "RCv3Test",
-      "node_counts": {
-        "cloud_servers": 3,
-        "external": 4,
-        "total": 7
-      },
-      "port": 80,
-      "status": "ACTIVE",
-      "status_detail": null,
-      "virtual_ip": "203.0.113.5"
-    }`)
-	})
-
-	expected := &Pool{
-		ID:   "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-		Name: "RCv3Test",
-		NodeCounts: struct {
-			CloudServers int `mapstructure:"cloud_servers"`
-			External     int `mapstructure:"external"`
-			Total        int `mapstructure:"total"`
-		}{
-			CloudServers: 3,
-			External:     4,
-			Total:        7,
-		},
-		Port:      80,
-		Status:    "ACTIVE",
-		VirtualIP: "203.0.113.5",
-	}
-
-	actual, err := Get(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestListNodes(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		fmt.Fprintf(w, `[
-      {
-        "created": "2014-05-30T03:23:42Z",
-        "cloud_server": {
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2"
-        },
-        "id": "1860451d-fb89-45b8-b54e-151afceb50e5",
-        "load_balancer_pool": {
-          "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2"
-        },
-        "status": "ACTIVE",
-        "updated": "2014-05-30T03:24:18Z"
-      },
-      {
-        "created": "2014-05-31T08:23:12Z",
-        "cloud_server": {
-          "id": "f28b870f-a063-498a-8b12-7025e5b1caa6"
-        },
-        "id": "b70481dd-7edf-4dbb-a44b-41cc7679d4fb",
-        "load_balancer_pool": {
-          "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2"
-        },
-        "status": "ADDING",
-        "updated": "2014-05-31T08:23:26Z"
-      },
-      {
-        "created": "2014-05-31T08:23:18Z",
-        "cloud_server": {
-          "id": "a3d3a6b3-e4e4-496f-9a3d-5c987163e458"
-        },
-        "id": "ced9ddc8-6fae-4e72-9457-16ead52b5515",
-        "load_balancer_pool": {
-          "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2"
-        },
-        "status": "ADD_FAILED",
-        "status_detail": "Unable to communicate with network device",
-        "updated": "2014-05-31T08:24:36Z"
-      }
-    ]`)
-	})
-
-	expected := []Node{
-		Node{
-			CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC),
-			CloudServer: struct {
-				ID string `mapstructure:"id"`
-			}{
-				ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-			},
-			ID: "1860451d-fb89-45b8-b54e-151afceb50e5",
-			LoadBalancerPool: struct {
-				ID string `mapstructure:"id"`
-			}{
-				ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-			},
-			Status:    "ACTIVE",
-			UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC),
-		},
-		Node{
-			CreatedAt: time.Date(2014, 5, 31, 8, 23, 12, 0, time.UTC),
-			CloudServer: struct {
-				ID string `mapstructure:"id"`
-			}{
-				ID: "f28b870f-a063-498a-8b12-7025e5b1caa6",
-			},
-			ID: "b70481dd-7edf-4dbb-a44b-41cc7679d4fb",
-			LoadBalancerPool: struct {
-				ID string `mapstructure:"id"`
-			}{
-				ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-			},
-			Status:    "ADDING",
-			UpdatedAt: time.Date(2014, 5, 31, 8, 23, 26, 0, time.UTC),
-		},
-		Node{
-			CreatedAt: time.Date(2014, 5, 31, 8, 23, 18, 0, time.UTC),
-			CloudServer: struct {
-				ID string `mapstructure:"id"`
-			}{
-				ID: "a3d3a6b3-e4e4-496f-9a3d-5c987163e458",
-			},
-			ID: "ced9ddc8-6fae-4e72-9457-16ead52b5515",
-			LoadBalancerPool: struct {
-				ID string `mapstructure:"id"`
-			}{
-				ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-			},
-			Status:       "ADD_FAILED",
-			StatusDetail: "Unable to communicate with network device",
-			UpdatedAt:    time.Date(2014, 5, 31, 8, 24, 36, 0, time.UTC),
-		},
-	}
-
-	count := 0
-	err := ListNodes(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2").EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNodes(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestCreateNode(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-      {
-        "cloud_server": {
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2"
-        }
-      }
-    `)
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, `
-      {
-        "created": "2014-05-30T03:23:42Z",
-        "cloud_server": {
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2"
-        },
-        "id": "1860451d-fb89-45b8-b54e-151afceb50e5",
-        "load_balancer_pool": {
-          "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2"
-        },
-        "status": "ACTIVE",
-        "status_detail": null,
-        "updated": "2014-05-30T03:24:18Z"
-      }
-    `)
-	})
-
-	expected := &Node{
-		CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC),
-		CloudServer: struct {
-			ID string `mapstructure:"id"`
-		}{
-			ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-		},
-		ID: "1860451d-fb89-45b8-b54e-151afceb50e5",
-		LoadBalancerPool: struct {
-			ID string `mapstructure:"id"`
-		}{
-			ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-		},
-		Status:    "ACTIVE",
-		UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC),
-	}
-
-	actual, err := CreateNode(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", "d95ae0c4-6ab8-4873-b82f-f8433840cff2").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestListNodesDetails(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes/details", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		fmt.Fprintf(w, `
-      [
-      {
-        "created": "2014-05-30T03:23:42Z",
-        "cloud_server": {
-          "cloud_network": {
-            "cidr": "192.168.100.0/24",
-            "created": "2014-05-25T01:23:42Z",
-            "id": "07426958-1ebf-4c38-b032-d456820ca21a",
-            "name": "RC-CLOUD",
-            "private_ip_v4": "192.168.100.5",
-            "updated": "2014-05-25T02:28:44Z"
-          },
-          "created": "2014-05-30T02:18:42Z",
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-          "name": "RCv3TestServer1",
-          "updated": "2014-05-30T02:19:18Z"
-        },
-        "id": "1860451d-fb89-45b8-b54e-151afceb50e5",
-        "load_balancer_pool": {
-          "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-          "name": "RCv3Test",
-          "node_counts": {
-            "cloud_servers": 3,
-            "external": 4,
-            "total": 7
-          },
-          "port": 80,
-          "status": "ACTIVE",
-          "status_detail": null,
-          "virtual_ip": "203.0.113.5"
-        },
-        "status": "ACTIVE",
-        "status_detail": null,
-        "updated": "2014-05-30T03:24:18Z"
-      }
-      ]
-    `)
-	})
-
-	expected := []NodeDetails{
-		NodeDetails{
-			CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC),
-			CloudServer: struct {
-				ID           string `mapstructure:"id"`
-				Name         string `mapstructure:"name"`
-				CloudNetwork struct {
-					ID          string    `mapstructure:"id"`
-					Name        string    `mapstructure:"name"`
-					PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-					CIDR        string    `mapstructure:"cidr"`
-					CreatedAt   time.Time `mapstructure:"-"`
-					UpdatedAt   time.Time `mapstructure:"-"`
-				} `mapstructure:"cloud_network"`
-				CreatedAt time.Time `mapstructure:"-"`
-				UpdatedAt time.Time `mapstructure:"-"`
-			}{
-				ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-				CloudNetwork: struct {
-					ID          string    `mapstructure:"id"`
-					Name        string    `mapstructure:"name"`
-					PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-					CIDR        string    `mapstructure:"cidr"`
-					CreatedAt   time.Time `mapstructure:"-"`
-					UpdatedAt   time.Time `mapstructure:"-"`
-				}{
-					ID:          "07426958-1ebf-4c38-b032-d456820ca21a",
-					CIDR:        "192.168.100.0/24",
-					CreatedAt:   time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC),
-					Name:        "RC-CLOUD",
-					PrivateIPv4: "192.168.100.5",
-					UpdatedAt:   time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC),
-				},
-				CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC),
-				Name:      "RCv3TestServer1",
-				UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC),
-			},
-			ID: "1860451d-fb89-45b8-b54e-151afceb50e5",
-			LoadBalancerPool: Pool{
-				ID:   "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-				Name: "RCv3Test",
-				NodeCounts: struct {
-					CloudServers int `mapstructure:"cloud_servers"`
-					External     int `mapstructure:"external"`
-					Total        int `mapstructure:"total"`
-				}{
-					CloudServers: 3,
-					External:     4,
-					Total:        7,
-				},
-				Port:      80,
-				Status:    "ACTIVE",
-				VirtualIP: "203.0.113.5",
-			},
-			Status:    "ACTIVE",
-			UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC),
-		},
-	}
-	count := 0
-	err := ListNodesDetails(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2").EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNodesDetails(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestGetNode(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes/1860451d-fb89-45b8-b54e-151afceb50e5", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-      {
-        "created": "2014-05-30T03:23:42Z",
-        "cloud_server": {
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2"
-        },
-        "id": "1860451d-fb89-45b8-b54e-151afceb50e5",
-        "load_balancer_pool": {
-          "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2"
-        },
-        "status": "ACTIVE",
-        "status_detail": null,
-        "updated": "2014-05-30T03:24:18Z"
-      }
-    `)
-	})
-
-	expected := &Node{
-		CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC),
-		CloudServer: struct {
-			ID string `mapstructure:"id"`
-		}{
-			ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-		},
-		ID: "1860451d-fb89-45b8-b54e-151afceb50e5",
-		LoadBalancerPool: struct {
-			ID string `mapstructure:"id"`
-		}{
-			ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-		},
-		Status:    "ACTIVE",
-		UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC),
-	}
-
-	actual, err := GetNode(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", "1860451d-fb89-45b8-b54e-151afceb50e5").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestDeleteNode(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes/1860451d-fb89-45b8-b54e-151afceb50e5", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	err := DeleteNode(client.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", "1860451d-fb89-45b8-b54e-151afceb50e5").ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestGetNodeDetails(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes/d95ae0c4-6ab8-4873-b82f-f8433840cff2/details", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		fmt.Fprintf(w, `
-      {
-        "created": "2014-05-30T03:23:42Z",
-        "cloud_server": {
-          "cloud_network": {
-            "cidr": "192.168.100.0/24",
-            "created": "2014-05-25T01:23:42Z",
-            "id": "07426958-1ebf-4c38-b032-d456820ca21a",
-            "name": "RC-CLOUD",
-            "private_ip_v4": "192.168.100.5",
-            "updated": "2014-05-25T02:28:44Z"
-          },
-          "created": "2014-05-30T02:18:42Z",
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-          "name": "RCv3TestServer1",
-          "updated": "2014-05-30T02:19:18Z"
-        },
-        "id": "1860451d-fb89-45b8-b54e-151afceb50e5",
-        "load_balancer_pool": {
-          "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-          "name": "RCv3Test",
-          "node_counts": {
-            "cloud_servers": 3,
-            "external": 4,
-            "total": 7
-          },
-          "port": 80,
-          "status": "ACTIVE",
-          "status_detail": null,
-          "virtual_ip": "203.0.113.5"
-        },
-        "status": "ACTIVE",
-        "status_detail": null,
-        "updated": "2014-05-30T03:24:18Z"
-      }
-    `)
-	})
-
-	expected := &NodeDetails{
-		CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC),
-		CloudServer: struct {
-			ID           string `mapstructure:"id"`
-			Name         string `mapstructure:"name"`
-			CloudNetwork struct {
-				ID          string    `mapstructure:"id"`
-				Name        string    `mapstructure:"name"`
-				PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-				CIDR        string    `mapstructure:"cidr"`
-				CreatedAt   time.Time `mapstructure:"-"`
-				UpdatedAt   time.Time `mapstructure:"-"`
-			} `mapstructure:"cloud_network"`
-			CreatedAt time.Time `mapstructure:"-"`
-			UpdatedAt time.Time `mapstructure:"-"`
-		}{
-			ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-			CloudNetwork: struct {
-				ID          string    `mapstructure:"id"`
-				Name        string    `mapstructure:"name"`
-				PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-				CIDR        string    `mapstructure:"cidr"`
-				CreatedAt   time.Time `mapstructure:"-"`
-				UpdatedAt   time.Time `mapstructure:"-"`
-			}{
-				ID:          "07426958-1ebf-4c38-b032-d456820ca21a",
-				CIDR:        "192.168.100.0/24",
-				CreatedAt:   time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC),
-				Name:        "RC-CLOUD",
-				PrivateIPv4: "192.168.100.5",
-				UpdatedAt:   time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC),
-			},
-			CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC),
-			Name:      "RCv3TestServer1",
-			UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC),
-		},
-		ID: "1860451d-fb89-45b8-b54e-151afceb50e5",
-		LoadBalancerPool: Pool{
-			ID:   "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-			Name: "RCv3Test",
-			NodeCounts: struct {
-				CloudServers int `mapstructure:"cloud_servers"`
-				External     int `mapstructure:"external"`
-				Total        int `mapstructure:"total"`
-			}{
-				CloudServers: 3,
-				External:     4,
-				Total:        7,
-			},
-			Port:      80,
-			Status:    "ACTIVE",
-			VirtualIP: "203.0.113.5",
-		},
-		Status:    "ACTIVE",
-		UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC),
-	}
-
-	actual, err := GetNodeDetails(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", "d95ae0c4-6ab8-4873-b82f-f8433840cff2").Extract()
-	th.AssertNoErr(t, err)
-	th.CheckDeepEquals(t, expected, actual)
-}
-
-func TestCreateNodes(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/load_balancer_pools/nodes", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-      [
-      {
-        "cloud_server": {
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2"
-        },
-        "load_balancer_pool": {
-          "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2"
-        }
-      },
-      {
-        "cloud_server": {
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2"
-        },
-        "load_balancer_pool": {
-          "id": "33021100-4abf-4836-9080-465a6d87ab68"
-      }
-    }
-    ]
-  `)
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, `
-      [
-      {
-        "created": "2014-05-30T03:23:42Z",
-        "cloud_server": {
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2"
-        },
-        "id": "1860451d-fb89-45b8-b54e-151afceb50e5",
-        "load_balancer_pool": {
-          "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2"
-        },
-        "status": "ADDING",
-        "status_detail": null,
-        "updated": null
-      },
-      {
-        "created": "2014-05-31T08:23:12Z",
-        "cloud_server": {
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2"
-        },
-        "id": "b70481dd-7edf-4dbb-a44b-41cc7679d4fb",
-        "load_balancer_pool": {
-          "id": "33021100-4abf-4836-9080-465a6d87ab68"
-        },
-        "status": "ADDING",
-        "status_detail": null,
-        "updated": null
-      }
-      ]
-    `)
-	})
-
-	expected := []Node{
-		Node{
-			CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC),
-			CloudServer: struct {
-				ID string `mapstructure:"id"`
-			}{
-				ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-			},
-			ID: "1860451d-fb89-45b8-b54e-151afceb50e5",
-			LoadBalancerPool: struct {
-				ID string `mapstructure:"id"`
-			}{
-				ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-			},
-			Status: "ADDING",
-		},
-		Node{
-			CreatedAt: time.Date(2014, 5, 31, 8, 23, 12, 0, time.UTC),
-			CloudServer: struct {
-				ID string `mapstructure:"id"`
-			}{
-				ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-			},
-			ID: "b70481dd-7edf-4dbb-a44b-41cc7679d4fb",
-			LoadBalancerPool: struct {
-				ID string `mapstructure:"id"`
-			}{
-				ID: "33021100-4abf-4836-9080-465a6d87ab68",
-			},
-			Status: "ADDING",
-		},
-	}
-
-	opts := NodesOpts{
-		NodeOpts{
-			ServerID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-			PoolID:   "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-		},
-		NodeOpts{
-			ServerID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-			PoolID:   "33021100-4abf-4836-9080-465a6d87ab68",
-		},
-	}
-	actual, err := CreateNodes(fake.ServiceClient(), opts).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestDeleteNodes(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/load_balancer_pools/nodes", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-      [
-      {
-        "cloud_server": {
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2"
-        },
-        "load_balancer_pool": {
-          "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2"
-        }
-      },
-      {
-        "cloud_server": {
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2"
-        },
-        "load_balancer_pool": {
-          "id": "33021100-4abf-4836-9080-465a6d87ab68"
-        }
-      }
-      ]
-    `)
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	opts := NodesOpts{
-		NodeOpts{
-			ServerID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-			PoolID:   "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-		},
-		NodeOpts{
-			ServerID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-			PoolID:   "33021100-4abf-4836-9080-465a6d87ab68",
-		},
-	}
-	err := DeleteNodes(client.ServiceClient(), opts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestListNodesForServerDetails(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/load_balancer_pools/nodes/details", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		fmt.Fprintf(w, `
-      [
-      {
-        "created": "2014-05-30T03:23:42Z",
-        "id": "1860451d-fb89-45b8-b54e-151afceb50e5",
-        "load_balancer_pool": {
-          "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-          "name": "RCv3Test",
-          "node_counts": {
-            "cloud_servers": 3,
-            "external": 4,
-            "total": 7
-          },
-          "port": 80,
-          "status": "ACTIVE",
-          "status_detail": null,
-          "virtual_ip": "203.0.113.5"
-        },
-        "status": "ACTIVE",
-        "status_detail": null,
-        "updated": "2014-05-30T03:24:18Z"
-      }
-      ]
-    `)
-	})
-
-	expected := []NodeDetailsForServer{
-		NodeDetailsForServer{
-			CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC),
-			ID:        "1860451d-fb89-45b8-b54e-151afceb50e5",
-			LoadBalancerPool: Pool{
-				ID:   "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2",
-				Name: "RCv3Test",
-				NodeCounts: struct {
-					CloudServers int `mapstructure:"cloud_servers"`
-					External     int `mapstructure:"external"`
-					Total        int `mapstructure:"total"`
-				}{
-					CloudServers: 3,
-					External:     4,
-					Total:        7,
-				},
-				Port:      80,
-				Status:    "ACTIVE",
-				VirtualIP: "203.0.113.5",
-			},
-			Status:    "ACTIVE",
-			UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC),
-		},
-	}
-	count := 0
-	err := ListNodesDetailsForServer(fake.ServiceClient(), "07426958-1ebf-4c38-b032-d456820ca21a").EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractNodesDetailsForServer(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
diff --git a/rackspace/rackconnect/v3/lbpools/results.go b/rackspace/rackconnect/v3/lbpools/results.go
deleted file mode 100644
index e5e914b..0000000
--- a/rackspace/rackconnect/v3/lbpools/results.go
+++ /dev/null
@@ -1,505 +0,0 @@
-package lbpools
-
-import (
-	"fmt"
-	"reflect"
-	"time"
-
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Pool represents a load balancer pool associated with a RackConnect configuration.
-type Pool struct {
-	// The unique ID of the load balancer pool.
-	ID string `mapstructure:"id"`
-	// The name of the load balancer pool.
-	Name string `mapstructure:"name"`
-	// The node counts associated witht the load balancer pool.
-	NodeCounts struct {
-		// The number of nodes associated with this LB pool for this account.
-		CloudServers int `mapstructure:"cloud_servers"`
-		// The number of nodes associated with this LB pool from other accounts.
-		External int `mapstructure:"external"`
-		// The total number of nodes associated with this LB pool.
-		Total int `mapstructure:"total"`
-	} `mapstructure:"node_counts"`
-	// The port of the LB pool
-	Port int `mapstructure:"port"`
-	// The status of the LB pool
-	Status string `mapstructure:"status"`
-	// The details of the status of the LB pool
-	StatusDetail string `mapstructure:"status_detail"`
-	// The virtual IP of the LB pool
-	VirtualIP string `mapstructure:"virtual_ip"`
-}
-
-// PoolPage is the page returned by a pager when traversing over a
-// collection of Pools.
-type PoolPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty returns true if a PoolPage contains no Pools.
-func (r PoolPage) IsEmpty() (bool, error) {
-	cns, err := ExtractPools(r)
-	if err != nil {
-		return true, err
-	}
-	return len(cns) == 0, nil
-}
-
-// ExtractPools extracts and returns Pools. It is used while iterating over
-// an lbpools.List call.
-func ExtractPools(page pagination.Page) ([]Pool, error) {
-	var res []Pool
-	err := mapstructure.Decode(page.(PoolPage).Body, &res)
-	return res, err
-}
-
-// GetResult represents the result of a Get operation.
-type GetResult struct {
-	gophercloud.Result
-}
-
-// Extract is a function that extracts an LBPool from a GetResult.
-func (r GetResult) Extract() (*Pool, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-	var res Pool
-	err := mapstructure.Decode(r.Body, &res)
-	return &res, err
-}
-
-// Node represents a load balancer pool node associated with a RackConnect configuration.
-type Node struct {
-	// The unique ID of the LB node.
-	ID string `mapstructure:"id"`
-	// The cloud server (node) of the load balancer pool.
-	CloudServer struct {
-		// The cloud server ID.
-		ID string `mapstructure:"id"`
-	} `mapstructure:"cloud_server"`
-	// The load balancer pool.
-	LoadBalancerPool struct {
-		// The LB pool ID.
-		ID string `mapstructure:"id"`
-	} `mapstructure:"load_balancer_pool"`
-	// The status of the LB pool.
-	Status string `mapstructure:"status"`
-	// The details of the status of the LB pool.
-	StatusDetail string `mapstructure:"status_detail"`
-	// The time the LB node was created.
-	CreatedAt time.Time `mapstructure:"-"`
-	// The time the LB node was last updated.
-	UpdatedAt time.Time `mapstructure:"-"`
-}
-
-// NodePage is the page returned by a pager when traversing over a
-// collection of Nodes.
-type NodePage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty returns true if a NodePage contains no Nodes.
-func (r NodePage) IsEmpty() (bool, error) {
-	n, err := ExtractNodes(r)
-	if err != nil {
-		return true, err
-	}
-	return len(n) == 0, nil
-}
-
-// ExtractNodes extracts and returns a slice of Nodes. It is used while iterating over
-// an lbpools.ListNodes call.
-func ExtractNodes(page pagination.Page) ([]Node, error) {
-	var res []Node
-	casted := page.(NodePage).Body
-	err := mapstructure.Decode(casted, &res)
-
-	var rawNodes []interface{}
-	switch casted.(type) {
-	case interface{}:
-		rawNodes = casted.([]interface{})
-	default:
-		return res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i := range rawNodes {
-		thisNode := (rawNodes[i]).(map[string]interface{})
-
-		if t, ok := thisNode["created"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return res, err
-			}
-			res[i].CreatedAt = creationTime
-		}
-
-		if t, ok := thisNode["updated"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return res, err
-			}
-			res[i].UpdatedAt = updatedTime
-		}
-	}
-
-	return res, err
-}
-
-// NodeResult represents a result that can be extracted as a Node.
-type NodeResult struct {
-	gophercloud.Result
-}
-
-// CreateNodeResult represents the result of an CreateNode operation.
-type CreateNodeResult struct {
-	NodeResult
-}
-
-// GetNodeResult represents the result of an GetNode operation.
-type GetNodeResult struct {
-	NodeResult
-}
-
-// Extract is a function that extracts a Node from a NodeResult.
-func (r NodeResult) Extract() (*Node, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-	var res Node
-	err := mapstructure.Decode(r.Body, &res)
-
-	b := r.Body.(map[string]interface{})
-
-	if date, ok := b["created"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.CreatedAt = t
-	}
-
-	if date, ok := b["updated"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.UpdatedAt = t
-	}
-
-	return &res, err
-}
-
-// NodeDetails represents a load balancer pool node associated with a RackConnect configuration
-// with all its details.
-type NodeDetails struct {
-	// The unique ID of the LB node.
-	ID string `mapstructure:"id"`
-	// The cloud server (node) of the load balancer pool.
-	CloudServer struct {
-		// The cloud server ID.
-		ID string `mapstructure:"id"`
-		// The name of the server.
-		Name string `mapstructure:"name"`
-		// The cloud network for the cloud server.
-		CloudNetwork struct {
-			// The network ID.
-			ID string `mapstructure:"id"`
-			// The network name.
-			Name string `mapstructure:"name"`
-			// The network's private IPv4 address.
-			PrivateIPv4 string `mapstructure:"private_ip_v4"`
-			// The IP range for the network.
-			CIDR string `mapstructure:"cidr"`
-			// The datetime the network was created.
-			CreatedAt time.Time `mapstructure:"-"`
-			// The last datetime the network was updated.
-			UpdatedAt time.Time `mapstructure:"-"`
-		} `mapstructure:"cloud_network"`
-		// The datetime the server was created.
-		CreatedAt time.Time `mapstructure:"-"`
-		// The datetime the server was last updated.
-		UpdatedAt time.Time `mapstructure:"-"`
-	} `mapstructure:"cloud_server"`
-	// The load balancer pool.
-	LoadBalancerPool Pool `mapstructure:"load_balancer_pool"`
-	// The status of the LB pool.
-	Status string `mapstructure:"status"`
-	// The details of the status of the LB pool.
-	StatusDetail string `mapstructure:"status_detail"`
-	// The time the LB node was created.
-	CreatedAt time.Time `mapstructure:"-"`
-	// The time the LB node was last updated.
-	UpdatedAt time.Time `mapstructure:"-"`
-}
-
-// NodeDetailsPage is the page returned by a pager when traversing over a
-// collection of NodeDetails.
-type NodeDetailsPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty returns true if a NodeDetailsPage contains no NodeDetails.
-func (r NodeDetailsPage) IsEmpty() (bool, error) {
-	n, err := ExtractNodesDetails(r)
-	if err != nil {
-		return true, err
-	}
-	return len(n) == 0, nil
-}
-
-// ExtractNodesDetails extracts and returns a slice of NodeDetails. It is used while iterating over
-// an lbpools.ListNodesDetails call.
-func ExtractNodesDetails(page pagination.Page) ([]NodeDetails, error) {
-	var res []NodeDetails
-	casted := page.(NodeDetailsPage).Body
-	err := mapstructure.Decode(casted, &res)
-
-	var rawNodesDetails []interface{}
-	switch casted.(type) {
-	case interface{}:
-		rawNodesDetails = casted.([]interface{})
-	default:
-		return res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i := range rawNodesDetails {
-		thisNodeDetails := (rawNodesDetails[i]).(map[string]interface{})
-
-		if t, ok := thisNodeDetails["created"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return res, err
-			}
-			res[i].CreatedAt = creationTime
-		}
-
-		if t, ok := thisNodeDetails["updated"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return res, err
-			}
-			res[i].UpdatedAt = updatedTime
-		}
-
-		if cs, ok := thisNodeDetails["cloud_server"].(map[string]interface{}); ok {
-			if t, ok := cs["created"].(string); ok && t != "" {
-				creationTime, err := time.Parse(time.RFC3339, t)
-				if err != nil {
-					return res, err
-				}
-				res[i].CloudServer.CreatedAt = creationTime
-			}
-			if t, ok := cs["updated"].(string); ok && t != "" {
-				updatedTime, err := time.Parse(time.RFC3339, t)
-				if err != nil {
-					return res, err
-				}
-				res[i].CloudServer.UpdatedAt = updatedTime
-			}
-			if cn, ok := cs["cloud_network"].(map[string]interface{}); ok {
-				if t, ok := cn["created"].(string); ok && t != "" {
-					creationTime, err := time.Parse(time.RFC3339, t)
-					if err != nil {
-						return res, err
-					}
-					res[i].CloudServer.CloudNetwork.CreatedAt = creationTime
-				}
-				if t, ok := cn["updated"].(string); ok && t != "" {
-					updatedTime, err := time.Parse(time.RFC3339, t)
-					if err != nil {
-						return res, err
-					}
-					res[i].CloudServer.CloudNetwork.UpdatedAt = updatedTime
-				}
-			}
-		}
-	}
-
-	return res, err
-}
-
-// GetNodeDetailsResult represents the result of an NodeDetails operation.
-type GetNodeDetailsResult struct {
-	gophercloud.Result
-}
-
-// Extract is a function that extracts a NodeDetails from a NodeDetailsResult.
-func (r GetNodeDetailsResult) Extract() (*NodeDetails, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-	var res NodeDetails
-	err := mapstructure.Decode(r.Body, &res)
-
-	b := r.Body.(map[string]interface{})
-
-	if date, ok := b["created"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.CreatedAt = t
-	}
-
-	if date, ok := b["updated"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.UpdatedAt = t
-	}
-
-	if cs, ok := b["cloud_server"].(map[string]interface{}); ok {
-		if t, ok := cs["created"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return &res, err
-			}
-			res.CloudServer.CreatedAt = creationTime
-		}
-		if t, ok := cs["updated"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return &res, err
-			}
-			res.CloudServer.UpdatedAt = updatedTime
-		}
-		if cn, ok := cs["cloud_network"].(map[string]interface{}); ok {
-			if t, ok := cn["created"].(string); ok && t != "" {
-				creationTime, err := time.Parse(time.RFC3339, t)
-				if err != nil {
-					return &res, err
-				}
-				res.CloudServer.CloudNetwork.CreatedAt = creationTime
-			}
-			if t, ok := cn["updated"].(string); ok && t != "" {
-				updatedTime, err := time.Parse(time.RFC3339, t)
-				if err != nil {
-					return &res, err
-				}
-				res.CloudServer.CloudNetwork.UpdatedAt = updatedTime
-			}
-		}
-	}
-
-	return &res, err
-}
-
-// DeleteNodeResult represents the result of a DeleteNode operation.
-type DeleteNodeResult struct {
-	gophercloud.ErrResult
-}
-
-// CreateNodesResult represents the result of a CreateNodes operation.
-type CreateNodesResult struct {
-	gophercloud.Result
-}
-
-// Extract is a function that extracts a slice of Nodes from a CreateNodesResult.
-func (r CreateNodesResult) Extract() ([]Node, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-	var res []Node
-	err := mapstructure.Decode(r.Body, &res)
-
-	b := r.Body.([]interface{})
-	for i := range b {
-		if date, ok := b[i].(map[string]interface{})["created"]; ok && date != nil {
-			t, err := time.Parse(time.RFC3339, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			res[i].CreatedAt = t
-		}
-		if date, ok := b[i].(map[string]interface{})["updated"]; ok && date != nil {
-			t, err := time.Parse(time.RFC3339, date.(string))
-			if err != nil {
-				return nil, err
-			}
-			res[i].UpdatedAt = t
-		}
-	}
-
-	return res, err
-}
-
-// DeleteNodesResult represents the result of a DeleteNodes operation.
-type DeleteNodesResult struct {
-	gophercloud.ErrResult
-}
-
-// NodeDetailsForServer represents a load balancer pool node associated with a RackConnect configuration
-// with all its details for a particular server.
-type NodeDetailsForServer struct {
-	// The unique ID of the LB node.
-	ID string `mapstructure:"id"`
-	// The load balancer pool.
-	LoadBalancerPool Pool `mapstructure:"load_balancer_pool"`
-	// The status of the LB pool.
-	Status string `mapstructure:"status"`
-	// The details of the status of the LB pool.
-	StatusDetail string `mapstructure:"status_detail"`
-	// The time the LB node was created.
-	CreatedAt time.Time `mapstructure:"-"`
-	// The time the LB node was last updated.
-	UpdatedAt time.Time `mapstructure:"-"`
-}
-
-// NodeDetailsForServerPage is the page returned by a pager when traversing over a
-// collection of NodeDetailsForServer.
-type NodeDetailsForServerPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty returns true if a NodeDetailsForServerPage contains no NodeDetailsForServer.
-func (r NodeDetailsForServerPage) IsEmpty() (bool, error) {
-	n, err := ExtractNodesDetailsForServer(r)
-	if err != nil {
-		return true, err
-	}
-	return len(n) == 0, nil
-}
-
-// ExtractNodesDetailsForServer extracts and returns a slice of NodeDetailsForServer. It is used while iterating over
-// an lbpools.ListNodesDetailsForServer call.
-func ExtractNodesDetailsForServer(page pagination.Page) ([]NodeDetailsForServer, error) {
-	var res []NodeDetailsForServer
-	casted := page.(NodeDetailsForServerPage).Body
-	err := mapstructure.Decode(casted, &res)
-
-	var rawNodesDetails []interface{}
-	switch casted.(type) {
-	case interface{}:
-		rawNodesDetails = casted.([]interface{})
-	default:
-		return res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i := range rawNodesDetails {
-		thisNodeDetails := (rawNodesDetails[i]).(map[string]interface{})
-
-		if t, ok := thisNodeDetails["created"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return res, err
-			}
-			res[i].CreatedAt = creationTime
-		}
-
-		if t, ok := thisNodeDetails["updated"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return res, err
-			}
-			res[i].UpdatedAt = updatedTime
-		}
-	}
-
-	return res, err
-}
diff --git a/rackspace/rackconnect/v3/lbpools/urls.go b/rackspace/rackconnect/v3/lbpools/urls.go
deleted file mode 100644
index c238239..0000000
--- a/rackspace/rackconnect/v3/lbpools/urls.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package lbpools
-
-import "github.com/rackspace/gophercloud"
-
-var root = "load_balancer_pools"
-
-func listURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL(root)
-}
-
-func getURL(c *gophercloud.ServiceClient, id string) string {
-	return c.ServiceURL(root, id)
-}
-
-func listNodesURL(c *gophercloud.ServiceClient, id string) string {
-	return c.ServiceURL(root, id, "nodes")
-}
-
-func createNodeURL(c *gophercloud.ServiceClient, id string) string {
-	return listNodesURL(c, id)
-}
-
-func listNodesDetailsURL(c *gophercloud.ServiceClient, id string) string {
-	return c.ServiceURL(root, id, "nodes", "details")
-}
-
-func nodeURL(c *gophercloud.ServiceClient, poolID, nodeID string) string {
-	return c.ServiceURL(root, poolID, "nodes", nodeID)
-}
-
-func deleteNodeURL(c *gophercloud.ServiceClient, poolID, nodeID string) string {
-	return nodeURL(c, poolID, nodeID)
-}
-
-func nodeDetailsURL(c *gophercloud.ServiceClient, poolID, nodeID string) string {
-	return c.ServiceURL(root, poolID, "nodes", nodeID, "details")
-}
-
-func createNodesURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL(root, "nodes")
-}
-
-func deleteNodesURL(c *gophercloud.ServiceClient) string {
-	return createNodesURL(c)
-}
-
-func listNodesForServerURL(c *gophercloud.ServiceClient, serverID string) string {
-	return c.ServiceURL(root, "nodes", "details?cloud_server_id="+serverID)
-}
diff --git a/rackspace/rackconnect/v3/publicips/requests.go b/rackspace/rackconnect/v3/publicips/requests.go
deleted file mode 100644
index 1164260..0000000
--- a/rackspace/rackconnect/v3/publicips/requests.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package publicips
-
-import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// List returns all public IPs.
-func List(c *gophercloud.ServiceClient) pagination.Pager {
-	url := listURL(c)
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return PublicIPPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(c, url, createPage)
-}
-
-// Create adds a public IP to the server with the given serverID.
-func Create(c *gophercloud.ServiceClient, serverID string) CreateResult {
-	var res CreateResult
-	reqBody := map[string]interface{}{
-		"cloud_server": map[string]string{
-			"id": serverID,
-		},
-	}
-	_, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil)
-	return res
-}
-
-// ListForServer returns all public IPs for the server with the given serverID.
-func ListForServer(c *gophercloud.ServiceClient, serverID string) pagination.Pager {
-	url := listForServerURL(c, serverID)
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return PublicIPPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(c, url, createPage)
-}
-
-// Get retrieves the public IP with the given id.
-func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
-}
-
-// Delete removes the public IP with the given id.
-func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = c.Delete(deleteURL(c, id), nil)
-	return res
-}
diff --git a/rackspace/rackconnect/v3/publicips/requests_test.go b/rackspace/rackconnect/v3/publicips/requests_test.go
deleted file mode 100644
index 61da2b0..0000000
--- a/rackspace/rackconnect/v3/publicips/requests_test.go
+++ /dev/null
@@ -1,378 +0,0 @@
-package publicips
-
-import (
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	"github.com/rackspace/gophercloud/pagination"
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
-	fake "github.com/rackspace/gophercloud/testhelper/client"
-)
-
-func TestListIPs(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/public_ips", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		fmt.Fprintf(w, `[
-      {
-        "created": "2014-05-30T03:23:42Z",
-        "cloud_server": {
-          "cloud_network": {
-            "cidr": "192.168.100.0/24",
-            "created": "2014-05-25T01:23:42Z",
-            "id": "07426958-1ebf-4c38-b032-d456820ca21a",
-            "name": "RC-CLOUD",
-            "private_ip_v4": "192.168.100.5",
-            "updated": "2014-05-25T02:28:44Z"
-          },
-          "created": "2014-05-30T02:18:42Z",
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-          "name": "RCv3TestServer1",
-          "updated": "2014-05-30T02:19:18Z"
-        },
-        "id": "2d0f586b-37a7-4ae0-adac-2743d5feb450",
-        "public_ip_v4": "203.0.113.110",
-        "status": "ACTIVE",
-        "status_detail": null,
-        "updated": "2014-05-30T03:24:18Z"
-      }
-    ]`)
-	})
-
-	expected := []PublicIP{
-		PublicIP{
-			ID:         "2d0f586b-37a7-4ae0-adac-2743d5feb450",
-			PublicIPv4: "203.0.113.110",
-			CreatedAt:  time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC),
-			CloudServer: struct {
-				ID           string `mapstructure:"id"`
-				Name         string `mapstructure:"name"`
-				CloudNetwork struct {
-					ID          string    `mapstructure:"id"`
-					Name        string    `mapstructure:"name"`
-					PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-					CIDR        string    `mapstructure:"cidr"`
-					CreatedAt   time.Time `mapstructure:"-"`
-					UpdatedAt   time.Time `mapstructure:"-"`
-				} `mapstructure:"cloud_network"`
-				CreatedAt time.Time `mapstructure:"-"`
-				UpdatedAt time.Time `mapstructure:"-"`
-			}{
-				ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-				CloudNetwork: struct {
-					ID          string    `mapstructure:"id"`
-					Name        string    `mapstructure:"name"`
-					PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-					CIDR        string    `mapstructure:"cidr"`
-					CreatedAt   time.Time `mapstructure:"-"`
-					UpdatedAt   time.Time `mapstructure:"-"`
-				}{
-					ID:          "07426958-1ebf-4c38-b032-d456820ca21a",
-					CIDR:        "192.168.100.0/24",
-					CreatedAt:   time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC),
-					Name:        "RC-CLOUD",
-					PrivateIPv4: "192.168.100.5",
-					UpdatedAt:   time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC),
-				},
-				CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC),
-				Name:      "RCv3TestServer1",
-				UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC),
-			},
-			Status:    "ACTIVE",
-			UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC),
-		},
-	}
-
-	count := 0
-	err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractPublicIPs(page)
-		th.AssertNoErr(t, err)
-
-		th.CheckDeepEquals(t, expected, actual)
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
-
-func TestCreateIP(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/public_ips", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-      {
-        "cloud_server": {
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2"
-        }
-      }
-    `)
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(w, `
-      {
-        "created": "2014-05-30T03:23:42Z",
-        "cloud_server": {
-          "cloud_network": {
-            "cidr": "192.168.100.0/24",
-            "created": "2014-05-25T01:23:42Z",
-            "id": "07426958-1ebf-4c38-b032-d456820ca21a",
-            "name": "RC-CLOUD",
-            "private_ip_v4": "192.168.100.5",
-            "updated": "2014-05-25T02:28:44Z"
-          },
-          "created": "2014-05-30T02:18:42Z",
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-          "name": "RCv3TestServer1",
-          "updated": "2014-05-30T02:19:18Z"
-        },
-        "id": "2d0f586b-37a7-4ae0-adac-2743d5feb450",
-        "status": "ADDING"
-      }`)
-	})
-
-	expected := &PublicIP{
-		CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC),
-		CloudServer: struct {
-			ID           string `mapstructure:"id"`
-			Name         string `mapstructure:"name"`
-			CloudNetwork struct {
-				ID          string    `mapstructure:"id"`
-				Name        string    `mapstructure:"name"`
-				PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-				CIDR        string    `mapstructure:"cidr"`
-				CreatedAt   time.Time `mapstructure:"-"`
-				UpdatedAt   time.Time `mapstructure:"-"`
-			} `mapstructure:"cloud_network"`
-			CreatedAt time.Time `mapstructure:"-"`
-			UpdatedAt time.Time `mapstructure:"-"`
-		}{
-			ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-			CloudNetwork: struct {
-				ID          string    `mapstructure:"id"`
-				Name        string    `mapstructure:"name"`
-				PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-				CIDR        string    `mapstructure:"cidr"`
-				CreatedAt   time.Time `mapstructure:"-"`
-				UpdatedAt   time.Time `mapstructure:"-"`
-			}{
-				ID:          "07426958-1ebf-4c38-b032-d456820ca21a",
-				CIDR:        "192.168.100.0/24",
-				CreatedAt:   time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC),
-				Name:        "RC-CLOUD",
-				PrivateIPv4: "192.168.100.5",
-				UpdatedAt:   time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC),
-			},
-			CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC),
-			Name:      "RCv3TestServer1",
-			UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC),
-		},
-		ID:     "2d0f586b-37a7-4ae0-adac-2743d5feb450",
-		Status: "ADDING",
-	}
-
-	actual, err := Create(fake.ServiceClient(), "d95ae0c4-6ab8-4873-b82f-f8433840cff2").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestGetIP(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/public_ips/2d0f586b-37a7-4ae0-adac-2743d5feb450", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		fmt.Fprintf(w, `
-      {
-        "created": "2014-05-30T03:23:42Z",
-        "cloud_server": {
-          "cloud_network": {
-            "cidr": "192.168.100.0/24",
-            "created": "2014-05-25T01:23:42Z",
-            "id": "07426958-1ebf-4c38-b032-d456820ca21a",
-            "name": "RC-CLOUD",
-            "private_ip_v4": "192.168.100.5",
-            "updated": "2014-05-25T02:28:44Z"
-          },
-          "created": "2014-05-30T02:18:42Z",
-          "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-          "name": "RCv3TestServer1",
-          "updated": "2014-05-30T02:19:18Z"
-        },
-        "id": "2d0f586b-37a7-4ae0-adac-2743d5feb450",
-        "public_ip_v4": "203.0.113.110",
-        "status": "ACTIVE",
-        "status_detail": null,
-        "updated": "2014-05-30T03:24:18Z"
-      }`)
-	})
-
-	expected := &PublicIP{
-		CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC),
-		CloudServer: struct {
-			ID           string `mapstructure:"id"`
-			Name         string `mapstructure:"name"`
-			CloudNetwork struct {
-				ID          string    `mapstructure:"id"`
-				Name        string    `mapstructure:"name"`
-				PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-				CIDR        string    `mapstructure:"cidr"`
-				CreatedAt   time.Time `mapstructure:"-"`
-				UpdatedAt   time.Time `mapstructure:"-"`
-			} `mapstructure:"cloud_network"`
-			CreatedAt time.Time `mapstructure:"-"`
-			UpdatedAt time.Time `mapstructure:"-"`
-		}{
-			ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-			CloudNetwork: struct {
-				ID          string    `mapstructure:"id"`
-				Name        string    `mapstructure:"name"`
-				PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-				CIDR        string    `mapstructure:"cidr"`
-				CreatedAt   time.Time `mapstructure:"-"`
-				UpdatedAt   time.Time `mapstructure:"-"`
-			}{
-				ID:          "07426958-1ebf-4c38-b032-d456820ca21a",
-				CIDR:        "192.168.100.0/24",
-				CreatedAt:   time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC),
-				Name:        "RC-CLOUD",
-				PrivateIPv4: "192.168.100.5",
-				UpdatedAt:   time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC),
-			},
-			CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC),
-			Name:      "RCv3TestServer1",
-			UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC),
-		},
-		ID:         "2d0f586b-37a7-4ae0-adac-2743d5feb450",
-		Status:     "ACTIVE",
-		PublicIPv4: "203.0.113.110",
-		UpdatedAt:  time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC),
-	}
-
-	actual, err := Get(fake.ServiceClient(), "2d0f586b-37a7-4ae0-adac-2743d5feb450").Extract()
-	th.AssertNoErr(t, err)
-	th.AssertDeepEquals(t, expected, actual)
-}
-
-func TestDeleteIP(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/public_ips/2d0f586b-37a7-4ae0-adac-2743d5feb450", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	err := Delete(client.ServiceClient(), "2d0f586b-37a7-4ae0-adac-2743d5feb450").ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestListForServer(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	th.Mux.HandleFunc("/public_ips", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Accept", "application/json")
-
-		w.Header().Set("Content-Type", "application/json")
-		fmt.Fprintf(w, `
-    [
-    {
-      "created": "2014-05-30T03:23:42Z",
-      "cloud_server": {
-        "cloud_network": {
-          "cidr": "192.168.100.0/24",
-          "created": "2014-05-25T01:23:42Z",
-          "id": "07426958-1ebf-4c38-b032-d456820ca21a",
-          "name": "RC-CLOUD",
-          "private_ip_v4": "192.168.100.5",
-          "updated": "2014-05-25T02:28:44Z"
-        },
-        "created": "2014-05-30T02:18:42Z",
-        "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-        "name": "RCv3TestServer1",
-        "updated": "2014-05-30T02:19:18Z"
-      },
-      "id": "2d0f586b-37a7-4ae0-adac-2743d5feb450",
-      "public_ip_v4": "203.0.113.110",
-      "status": "ACTIVE",
-      "updated": "2014-05-30T03:24:18Z"
-    }
-    ]`)
-	})
-
-	expected := []PublicIP{
-		PublicIP{
-			CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC),
-			CloudServer: struct {
-				ID           string `mapstructure:"id"`
-				Name         string `mapstructure:"name"`
-				CloudNetwork struct {
-					ID          string    `mapstructure:"id"`
-					Name        string    `mapstructure:"name"`
-					PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-					CIDR        string    `mapstructure:"cidr"`
-					CreatedAt   time.Time `mapstructure:"-"`
-					UpdatedAt   time.Time `mapstructure:"-"`
-				} `mapstructure:"cloud_network"`
-				CreatedAt time.Time `mapstructure:"-"`
-				UpdatedAt time.Time `mapstructure:"-"`
-			}{
-				ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2",
-				CloudNetwork: struct {
-					ID          string    `mapstructure:"id"`
-					Name        string    `mapstructure:"name"`
-					PrivateIPv4 string    `mapstructure:"private_ip_v4"`
-					CIDR        string    `mapstructure:"cidr"`
-					CreatedAt   time.Time `mapstructure:"-"`
-					UpdatedAt   time.Time `mapstructure:"-"`
-				}{
-					ID:          "07426958-1ebf-4c38-b032-d456820ca21a",
-					CIDR:        "192.168.100.0/24",
-					CreatedAt:   time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC),
-					Name:        "RC-CLOUD",
-					PrivateIPv4: "192.168.100.5",
-					UpdatedAt:   time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC),
-				},
-				CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC),
-				Name:      "RCv3TestServer1",
-				UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC),
-			},
-			ID:         "2d0f586b-37a7-4ae0-adac-2743d5feb450",
-			Status:     "ACTIVE",
-			PublicIPv4: "203.0.113.110",
-			UpdatedAt:  time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC),
-		},
-	}
-	count := 0
-	err := ListForServer(fake.ServiceClient(), "d95ae0c4-6ab8-4873-b82f-f8433840cff2").EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		actual, err := ExtractPublicIPs(page)
-		th.AssertNoErr(t, err)
-		th.CheckDeepEquals(t, expected, actual)
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, count, 1)
-}
diff --git a/rackspace/rackconnect/v3/publicips/results.go b/rackspace/rackconnect/v3/publicips/results.go
deleted file mode 100644
index 132cf77..0000000
--- a/rackspace/rackconnect/v3/publicips/results.go
+++ /dev/null
@@ -1,221 +0,0 @@
-package publicips
-
-import (
-	"fmt"
-	"reflect"
-	"time"
-
-	"github.com/mitchellh/mapstructure"
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// PublicIP represents a public IP address.
-type PublicIP struct {
-	// The unique ID of the public IP.
-	ID string `mapstructure:"id"`
-	// The IPv4 address of the public IP.
-	PublicIPv4 string `mapstructure:"public_ip_v4"`
-	// The cloud server (node) of the public IP.
-	CloudServer struct {
-		// The cloud server ID.
-		ID string `mapstructure:"id"`
-		// The name of the server.
-		Name string `mapstructure:"name"`
-		// The cloud network for the cloud server.
-		CloudNetwork struct {
-			// The network ID.
-			ID string `mapstructure:"id"`
-			// The network name.
-			Name string `mapstructure:"name"`
-			// The network's private IPv4 address.
-			PrivateIPv4 string `mapstructure:"private_ip_v4"`
-			// The IP range for the network.
-			CIDR string `mapstructure:"cidr"`
-			// The datetime the network was created.
-			CreatedAt time.Time `mapstructure:"-"`
-			// The last datetime the network was updated.
-			UpdatedAt time.Time `mapstructure:"-"`
-		} `mapstructure:"cloud_network"`
-		// The datetime the server was created.
-		CreatedAt time.Time `mapstructure:"-"`
-		// The datetime the server was last updated.
-		UpdatedAt time.Time `mapstructure:"-"`
-	} `mapstructure:"cloud_server"`
-	// The status of the public IP.
-	Status string `mapstructure:"status"`
-	// The details of the status of the public IP.
-	StatusDetail string `mapstructure:"status_detail"`
-	// The time the public IP was created.
-	CreatedAt time.Time `mapstructure:"-"`
-	// The time the public IP was last updated.
-	UpdatedAt time.Time `mapstructure:"-"`
-}
-
-// PublicIPPage is the page returned by a pager when traversing over a
-// collection of PublicIPs.
-type PublicIPPage struct {
-	pagination.SinglePageBase
-}
-
-// IsEmpty returns true if a PublicIPPage contains no PublicIPs.
-func (r PublicIPPage) IsEmpty() (bool, error) {
-	n, err := ExtractPublicIPs(r)
-	if err != nil {
-		return true, err
-	}
-	return len(n) == 0, nil
-}
-
-// ExtractPublicIPs extracts and returns a slice of PublicIPs. It is used while iterating over
-// a publicips.List call.
-func ExtractPublicIPs(page pagination.Page) ([]PublicIP, error) {
-	var res []PublicIP
-	casted := page.(PublicIPPage).Body
-	err := mapstructure.Decode(casted, &res)
-
-	var rawNodesDetails []interface{}
-	switch casted.(type) {
-	case interface{}:
-		rawNodesDetails = casted.([]interface{})
-	default:
-		return res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
-	}
-
-	for i := range rawNodesDetails {
-		thisNodeDetails := (rawNodesDetails[i]).(map[string]interface{})
-
-		if t, ok := thisNodeDetails["created"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return res, err
-			}
-			res[i].CreatedAt = creationTime
-		}
-
-		if t, ok := thisNodeDetails["updated"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return res, err
-			}
-			res[i].UpdatedAt = updatedTime
-		}
-
-		if cs, ok := thisNodeDetails["cloud_server"].(map[string]interface{}); ok {
-			if t, ok := cs["created"].(string); ok && t != "" {
-				creationTime, err := time.Parse(time.RFC3339, t)
-				if err != nil {
-					return res, err
-				}
-				res[i].CloudServer.CreatedAt = creationTime
-			}
-			if t, ok := cs["updated"].(string); ok && t != "" {
-				updatedTime, err := time.Parse(time.RFC3339, t)
-				if err != nil {
-					return res, err
-				}
-				res[i].CloudServer.UpdatedAt = updatedTime
-			}
-			if cn, ok := cs["cloud_network"].(map[string]interface{}); ok {
-				if t, ok := cn["created"].(string); ok && t != "" {
-					creationTime, err := time.Parse(time.RFC3339, t)
-					if err != nil {
-						return res, err
-					}
-					res[i].CloudServer.CloudNetwork.CreatedAt = creationTime
-				}
-				if t, ok := cn["updated"].(string); ok && t != "" {
-					updatedTime, err := time.Parse(time.RFC3339, t)
-					if err != nil {
-						return res, err
-					}
-					res[i].CloudServer.CloudNetwork.UpdatedAt = updatedTime
-				}
-			}
-		}
-	}
-
-	return res, err
-}
-
-// PublicIPResult represents a result that can be extracted into a PublicIP.
-type PublicIPResult struct {
-	gophercloud.Result
-}
-
-// CreateResult represents the result of a Create operation.
-type CreateResult struct {
-	PublicIPResult
-}
-
-// GetResult represents the result of a Get operation.
-type GetResult struct {
-	PublicIPResult
-}
-
-// Extract is a function that extracts a PublicIP from a PublicIPResult.
-func (r PublicIPResult) Extract() (*PublicIP, error) {
-	if r.Err != nil {
-		return nil, r.Err
-	}
-	var res PublicIP
-	err := mapstructure.Decode(r.Body, &res)
-
-	b := r.Body.(map[string]interface{})
-
-	if date, ok := b["created"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.CreatedAt = t
-	}
-
-	if date, ok := b["updated"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
-		if err != nil {
-			return nil, err
-		}
-		res.UpdatedAt = t
-	}
-
-	if cs, ok := b["cloud_server"].(map[string]interface{}); ok {
-		if t, ok := cs["created"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return &res, err
-			}
-			res.CloudServer.CreatedAt = creationTime
-		}
-		if t, ok := cs["updated"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
-			if err != nil {
-				return &res, err
-			}
-			res.CloudServer.UpdatedAt = updatedTime
-		}
-		if cn, ok := cs["cloud_network"].(map[string]interface{}); ok {
-			if t, ok := cn["created"].(string); ok && t != "" {
-				creationTime, err := time.Parse(time.RFC3339, t)
-				if err != nil {
-					return &res, err
-				}
-				res.CloudServer.CloudNetwork.CreatedAt = creationTime
-			}
-			if t, ok := cn["updated"].(string); ok && t != "" {
-				updatedTime, err := time.Parse(time.RFC3339, t)
-				if err != nil {
-					return &res, err
-				}
-				res.CloudServer.CloudNetwork.UpdatedAt = updatedTime
-			}
-		}
-	}
-
-	return &res, err
-}
-
-// DeleteResult represents the result of a Delete operation.
-type DeleteResult struct {
-	gophercloud.ErrResult
-}
diff --git a/rackspace/rackconnect/v3/publicips/urls.go b/rackspace/rackconnect/v3/publicips/urls.go
deleted file mode 100644
index 6f310be..0000000
--- a/rackspace/rackconnect/v3/publicips/urls.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package publicips
-
-import "github.com/rackspace/gophercloud"
-
-var root = "public_ips"
-
-func listURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL(root)
-}
-
-func createURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL(root)
-}
-
-func listForServerURL(c *gophercloud.ServiceClient, serverID string) string {
-	return c.ServiceURL(root + "?cloud_server_id=" + serverID)
-}
-
-func getURL(c *gophercloud.ServiceClient, id string) string {
-	return c.ServiceURL(root, id)
-}
-
-func deleteURL(c *gophercloud.ServiceClient, id string) string {
-	return getURL(c, id)
-}
diff --git a/results.go b/results.go
index 27fd1b6..8cca421 100644
--- a/results.go
+++ b/results.go
@@ -1,11 +1,12 @@
 package gophercloud
 
 import (
+	"bytes"
 	"encoding/json"
+	"io"
 	"net/http"
-	"reflect"
-
-	"github.com/mitchellh/mapstructure"
+	"strconv"
+	"time"
 )
 
 /*
@@ -35,6 +36,30 @@
 	Err error
 }
 
+// ExtractInto allows users to provide an object into which `Extract` will extract
+// the `Result.Body`. This would be useful for OpenStack providers that have
+// different fields in the response object than OpenStack proper.
+func (r Result) ExtractInto(to interface{}) error {
+	if r.Err != nil {
+		return r.Err
+	}
+
+	if reader, ok := r.Body.(io.Reader); ok {
+		if readCloser, ok := reader.(io.Closer); ok {
+			defer readCloser.Close()
+		}
+		return json.NewDecoder(reader).Decode(to)
+	}
+
+	b, err := json.Marshal(r.Body)
+	if err != nil {
+		return err
+	}
+	err = json.Unmarshal(b, to)
+
+	return err
+}
+
 // PrettyPrintJSON creates a string containing the full response body as
 // pretty-printed JSON. It's useful for capturing test fixtures and for
 // debugging extraction bugs. If you include its output in an issue related to
@@ -81,40 +106,124 @@
 // ExtractHeader will return the http.Header and error from the HeaderResult.
 //
 //   header, err := objects.Create(client, "my_container", objects.CreateOpts{}).ExtractHeader()
-func (hr HeaderResult) ExtractHeader() (http.Header, error) {
-	return hr.Header, hr.Err
-}
-
-// DecodeHeader is a function that decodes a header (usually of type map[string]interface{}) to
-// another type (usually a struct). This function is used by the objectstorage package to give
-// users access to response headers without having to query a map. A DecodeHookFunction is used,
-// because OpenStack-based clients return header values as arrays (Go slices).
-func DecodeHeader(from, to interface{}) error {
-	config := &mapstructure.DecoderConfig{
-		DecodeHook: func(from, to reflect.Kind, data interface{}) (interface{}, error) {
-			if from == reflect.Slice {
-				return data.([]string)[0], nil
-			}
-			return data, nil
-		},
-		Result:           to,
-		WeaklyTypedInput: true,
+func (r HeaderResult) ExtractInto(to interface{}) error {
+	if r.Err != nil {
+		return r.Err
 	}
-	decoder, err := mapstructure.NewDecoder(config)
+
+	tmpHeaderMap := map[string]string{}
+	for k, v := range r.Header {
+		if len(v) > 0 {
+			tmpHeaderMap[k] = v[0]
+		}
+	}
+
+	b, err := json.Marshal(tmpHeaderMap)
 	if err != nil {
 		return err
 	}
-	if err := decoder.Decode(from); err != nil {
-		return err
-	}
-	return nil
+	err = json.Unmarshal(b, to)
+
+	return err
 }
 
 // RFC3339Milli describes a common time format used by some API responses.
 const RFC3339Milli = "2006-01-02T15:04:05.999999Z"
 
-// Time format used in cloud orchestration
-const STACK_TIME_FMT = "2006-01-02T15:04:05"
+type JSONRFC3339Milli time.Time
+
+func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error {
+	b := bytes.NewBuffer(data)
+	dec := json.NewDecoder(b)
+	var s string
+	if err := dec.Decode(&s); err != nil {
+		return err
+	}
+	t, err := time.Parse(RFC3339Milli, s)
+	if err != nil {
+		return err
+	}
+	*jt = JSONRFC3339Milli(t)
+	return nil
+}
+
+const RFC3339MilliNoZ = "2006-01-02T15:04:05.999999"
+
+type JSONRFC3339MilliNoZ time.Time
+
+func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error {
+	var s string
+	if err := json.Unmarshal(data, &s); err != nil {
+		return err
+	}
+	if s == "" {
+		return nil
+	}
+	t, err := time.Parse(RFC3339MilliNoZ, s)
+	if err != nil {
+		return err
+	}
+	*jt = JSONRFC3339MilliNoZ(t)
+	return nil
+}
+
+type JSONRFC1123 time.Time
+
+func (jt *JSONRFC1123) UnmarshalJSON(data []byte) error {
+	var s string
+	if err := json.Unmarshal(data, &s); err != nil {
+		return err
+	}
+	if s == "" {
+		return nil
+	}
+	t, err := time.Parse(time.RFC1123, s)
+	if err != nil {
+		return err
+	}
+	*jt = JSONRFC1123(t)
+	return nil
+}
+
+type JSONUnix time.Time
+
+func (jt *JSONUnix) UnmarshalJSON(data []byte) error {
+	var s string
+	if err := json.Unmarshal(data, &s); err != nil {
+		return err
+	}
+	if s == "" {
+		return nil
+	}
+	unix, err := strconv.ParseInt(s, 10, 64)
+	if err != nil {
+		return err
+	}
+	t = time.Unix(unix, 0)
+	*jt = JSONUnix(t)
+	return nil
+}
+
+// RFC3339NoZ is the time format used in Heat (Orchestration).
+const RFC3339NoZ = "2006-01-02T15:04:05"
+
+type JSONRFC3339NoZ time.Time
+
+func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error {
+	var s string
+	if err := json.Unmarshal(data, &s); err != nil {
+		return err
+	}
+	if s == "" {
+		return nil
+	}
+	t, err := time.Parse(RFC3339NoZ, s)
+	if err != nil {
+		return err
+	}
+	*jt = JSONRFC3339NoZ(t)
+	return nil
+}
 
 /*
 Link is an internal type to be used in packages of collection resources that are
@@ -125,15 +234,15 @@
 Rel field set to "next".
 */
 type Link struct {
-	Href string `mapstructure:"href"`
-	Rel  string `mapstructure:"rel"`
+	Href string `json:"href"`
+	Rel  string `json:"rel"`
 }
 
 /*
 ExtractNextURL is an internal function useful for packages of collection
 resources that are paginated in a certain way.
 
-It attempts attempts to extract the "next" URL from slice of Link structs, or
+It attempts to extract the "next" URL from slice of Link structs, or
 "" if no such URL is present.
 */
 func ExtractNextURL(links []Link) (string, error) {
diff --git a/script/acceptancetest b/script/acceptancetest
index f9c89f4..9debd48 100755
--- a/script/acceptancetest
+++ b/script/acceptancetest
@@ -2,4 +2,4 @@
 #
 # Run the acceptance tests.
 
-exec go test -p=1 -tags 'acceptance fixtures' github.com/rackspace/gophercloud/acceptance/... $@
+exec go test -p=1 github.com/gophercloud/gophercloud/acceptance/... $@
diff --git a/script/bootstrap b/script/bootstrap
index 6bae6e8..78a195d 100755
--- a/script/bootstrap
+++ b/script/bootstrap
@@ -8,8 +8,8 @@
 mkdir -p $GOPATH
 
 # Download gophercloud into that environment
-go get github.com/rackspace/gophercloud
-cd $GOPATH/src/github.com/rackspace/gophercloud
+go get github.com/gophercloud/gophercloud
+cd $GOPATH/src/github.com/gophercloud/gophercloud
 git checkout master
 
 # Write out the env.sh convenience file.
@@ -17,10 +17,9 @@
 cat <<EOF >env.sh
 #!/bin/bash
 export GOPATH=$(pwd)
-export GOPHERCLOUD=$GOPATH/src/github.com/rackspace/gophercloud
+export GOPHERCLOUD=$GOPATH/src/github.com/gophercloud/gophercloud
 EOF
 chmod a+x env.sh
 
 # Make changes immediately available as a convenience.
 . ./env.sh
-
diff --git a/script/unittest b/script/unittest
index d3440a9..2c65d06 100755
--- a/script/unittest
+++ b/script/unittest
@@ -2,4 +2,4 @@
 #
 # Run the unit tests.
 
-exec go test -tags fixtures ./... $@
+exec go test ./testing ./.../testing $@
diff --git a/service_client.go b/service_client.go
index 3490da0..7484c67 100644
--- a/service_client.go
+++ b/service_client.go
@@ -1,6 +1,10 @@
 package gophercloud
 
-import "strings"
+import (
+	"io"
+	"net/http"
+	"strings"
+)
 
 // ServiceClient stores details required to interact with a specific service API implemented by a provider.
 // Generally, you'll acquire these by calling the appropriate `New` method on a ProviderClient.
@@ -16,6 +20,8 @@
 	// the API version and, like Endpoint, MUST end with a / if set. If not set, the Endpoint is used
 	// as-is, instead.
 	ResourceBase string
+
+	Microversion string
 }
 
 // ResourceBaseURL returns the base URL of any resources used by this service. It MUST end with a /.
@@ -30,3 +36,106 @@
 func (client *ServiceClient) ServiceURL(parts ...string) string {
 	return client.ResourceBaseURL() + strings.Join(parts, "/")
 }
+
+// Get calls `Request` with the "GET" HTTP verb.
+func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
+	if opts == nil {
+		opts = &RequestOpts{}
+	}
+	if JSONResponse != nil {
+		opts.JSONResponse = JSONResponse
+	}
+
+	if opts.MoreHeaders == nil {
+		opts.MoreHeaders = make(map[string]string)
+	}
+	opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
+
+	return client.Request("GET", url, opts)
+}
+
+// Post calls `Request` with the "POST" HTTP verb.
+func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
+	if opts == nil {
+		opts = &RequestOpts{}
+	}
+
+	if v, ok := (JSONBody).(io.Reader); ok {
+		opts.RawBody = v
+	} else if JSONBody != nil {
+		opts.JSONBody = JSONBody
+	}
+
+	if JSONResponse != nil {
+		opts.JSONResponse = JSONResponse
+	}
+
+	if opts.MoreHeaders == nil {
+		opts.MoreHeaders = make(map[string]string)
+	}
+	opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
+
+	return client.Request("POST", url, opts)
+}
+
+// Put calls `Request` with the "PUT" HTTP verb.
+func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
+	if opts == nil {
+		opts = &RequestOpts{}
+	}
+
+	if v, ok := (JSONBody).(io.Reader); ok {
+		opts.RawBody = v
+	} else if JSONBody != nil {
+		opts.JSONBody = JSONBody
+	}
+
+	if JSONResponse != nil {
+		opts.JSONResponse = JSONResponse
+	}
+
+	if opts.MoreHeaders == nil {
+		opts.MoreHeaders = make(map[string]string)
+	}
+	opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
+
+	return client.Request("PUT", url, opts)
+}
+
+// Patch calls `Request` with the "PATCH" HTTP verb.
+func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
+	if opts == nil {
+		opts = &RequestOpts{}
+	}
+
+	if v, ok := (JSONBody).(io.Reader); ok {
+		opts.RawBody = v
+	} else if JSONBody != nil {
+		opts.JSONBody = JSONBody
+	}
+
+	if JSONResponse != nil {
+		opts.JSONResponse = JSONResponse
+	}
+
+	if opts.MoreHeaders == nil {
+		opts.MoreHeaders = make(map[string]string)
+	}
+	opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
+
+	return client.Request("PATCH", url, opts)
+}
+
+// Delete calls `Request` with the "DELETE" HTTP verb.
+func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) {
+	if opts == nil {
+		opts = &RequestOpts{}
+	}
+
+	if opts.MoreHeaders == nil {
+		opts.MoreHeaders = make(map[string]string)
+	}
+	opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
+
+	return client.Request("DELETE", url, opts)
+}
diff --git a/service_client_test.go b/service_client_test.go
deleted file mode 100644
index 84beb3f..0000000
--- a/service_client_test.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package gophercloud
-
-import (
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestServiceURL(t *testing.T) {
-	c := &ServiceClient{Endpoint: "http://123.45.67.8/"}
-	expected := "http://123.45.67.8/more/parts/here"
-	actual := c.ServiceURL("more", "parts", "here")
-	th.CheckEquals(t, expected, actual)
-}
diff --git a/testhelper/client/fake.go b/testhelper/client/fake.go
index 5b69b05..3d81cc9 100644
--- a/testhelper/client/fake.go
+++ b/testhelper/client/fake.go
@@ -1,8 +1,8 @@
 package client
 
 import (
-	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/testhelper"
 )
 
 // Fake token to use.
diff --git a/testhelper/fixture/helper.go b/testhelper/fixture/helper.go
index d54355d..fe98c86 100644
--- a/testhelper/fixture/helper.go
+++ b/testhelper/fixture/helper.go
@@ -5,8 +5,8 @@
 	"net/http"
 	"testing"
 
-	th "github.com/rackspace/gophercloud/testhelper"
-	"github.com/rackspace/gophercloud/testhelper/client"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
 )
 
 func SetupHandler(t *testing.T, url, method, requestBody, responseBody string, status int) {
diff --git a/testing/endpoint_search_test.go b/testing/endpoint_search_test.go
new file mode 100644
index 0000000..22476cb
--- /dev/null
+++ b/testing/endpoint_search_test.go
@@ -0,0 +1,20 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestApplyDefaultsToEndpointOpts(t *testing.T) {
+	eo := gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic}
+	eo.ApplyDefaults("compute")
+	expected := gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic, Type: "compute"}
+	th.CheckDeepEquals(t, expected, eo)
+
+	eo = gophercloud.EndpointOpts{Type: "compute"}
+	eo.ApplyDefaults("object-store")
+	expected = gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic, Type: "compute"}
+	th.CheckDeepEquals(t, expected, eo)
+}
diff --git a/testing/params_test.go b/testing/params_test.go
new file mode 100644
index 0000000..90f3fad
--- /dev/null
+++ b/testing/params_test.go
@@ -0,0 +1,250 @@
+package testing
+
+import (
+	"net/url"
+	"reflect"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestMaybeString(t *testing.T) {
+	testString := ""
+	var expected *string
+	actual := gophercloud.MaybeString(testString)
+	th.CheckDeepEquals(t, expected, actual)
+
+	testString = "carol"
+	expected = &testString
+	actual = gophercloud.MaybeString(testString)
+	th.CheckDeepEquals(t, expected, actual)
+}
+
+func TestMaybeInt(t *testing.T) {
+	testInt := 0
+	var expected *int
+	actual := gophercloud.MaybeInt(testInt)
+	th.CheckDeepEquals(t, expected, actual)
+
+	testInt = 4
+	expected = &testInt
+	actual = gophercloud.MaybeInt(testInt)
+	th.CheckDeepEquals(t, expected, actual)
+}
+
+func TestBuildQueryString(t *testing.T) {
+	type testVar string
+	opts := struct {
+		J  int       `q:"j"`
+		R  string    `q:"r,required"`
+		C  bool      `q:"c"`
+		S  []string  `q:"s"`
+		TS []testVar `q:"ts"`
+		TI []int     `q:"ti"`
+	}{
+		J:  2,
+		R:  "red",
+		C:  true,
+		S:  []string{"one", "two", "three"},
+		TS: []testVar{"a", "b"},
+		TI: []int{1, 2},
+	}
+	expected := &url.URL{RawQuery: "c=true&j=2&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"}
+	actual, err := gophercloud.BuildQueryString(&opts)
+	if err != nil {
+		t.Errorf("Error building query string: %v", err)
+	}
+	th.CheckDeepEquals(t, expected, actual)
+
+	opts = struct {
+		J  int       `q:"j"`
+		R  string    `q:"r,required"`
+		C  bool      `q:"c"`
+		S  []string  `q:"s"`
+		TS []testVar `q:"ts"`
+		TI []int     `q:"ti"`
+	}{
+		J: 2,
+		C: true,
+	}
+	_, err = gophercloud.BuildQueryString(&opts)
+	if err == nil {
+		t.Errorf("Expected error: 'Required field not set'")
+	}
+	th.CheckDeepEquals(t, expected, actual)
+
+	_, err = gophercloud.BuildQueryString(map[string]interface{}{"Number": 4})
+	if err == nil {
+		t.Errorf("Expected error: 'Options type is not a struct'")
+	}
+}
+
+func TestBuildHeaders(t *testing.T) {
+	testStruct := struct {
+		Accept string `h:"Accept"`
+		Num    int    `h:"Number,required"`
+		Style  bool   `h:"Style"`
+	}{
+		Accept: "application/json",
+		Num:    4,
+		Style:  true,
+	}
+	expected := map[string]string{"Accept": "application/json", "Number": "4", "Style": "true"}
+	actual, err := gophercloud.BuildHeaders(&testStruct)
+	th.CheckNoErr(t, err)
+	th.CheckDeepEquals(t, expected, actual)
+
+	testStruct.Num = 0
+	_, err = gophercloud.BuildHeaders(&testStruct)
+	if err == nil {
+		t.Errorf("Expected error: 'Required header not set'")
+	}
+
+	_, err = gophercloud.BuildHeaders(map[string]interface{}{"Number": 4})
+	if err == nil {
+		t.Errorf("Expected error: 'Options type is not a struct'")
+	}
+}
+
+func TestQueriesAreEscaped(t *testing.T) {
+	type foo struct {
+		Name  string `q:"something"`
+		Shape string `q:"else"`
+	}
+
+	expected := &url.URL{RawQuery: "else=Triangl+e&something=blah%2B%3F%21%21foo"}
+
+	actual, err := gophercloud.BuildQueryString(foo{Name: "blah+?!!foo", Shape: "Triangl e"})
+	th.AssertNoErr(t, err)
+
+	th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestBuildRequestBody(t *testing.T) {
+	type PasswordCredentials struct {
+		Username string `json:"username" required:"true"`
+		Password string `json:"password" required:"true"`
+	}
+
+	type TokenCredentials struct {
+		ID string `json:"id,omitempty" required:"true"`
+	}
+
+	type orFields struct {
+		Filler int `json:"filler,omitempty"`
+		F1     int `json:"f1,omitempty" or:"F2"`
+		F2     int `json:"f2,omitempty" or:"F1"`
+	}
+
+	// AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder
+	// interface.
+	type AuthOptions struct {
+		PasswordCredentials `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
+
+		// The TenantID and TenantName fields are optional for the Identity V2 API.
+		// Some providers allow you to specify a TenantName instead of the TenantId.
+		// Some require both. Your provider's authentication policies will determine
+		// how these fields influence authentication.
+		TenantID   string `json:"tenantId,omitempty"`
+		TenantName string `json:"tenantName,omitempty"`
+
+		// TokenCredentials allows users to authenticate (possibly as another user) with an
+		// authentication token ID.
+		TokenCredentials `json:"token,omitempty" xor:"PasswordCredentials"`
+
+		OrFields orFields `json:"or_fields,omitempty"`
+	}
+
+	var successCases = []struct {
+		opts     AuthOptions
+		expected map[string]interface{}
+	}{
+		{
+			AuthOptions{
+				PasswordCredentials: PasswordCredentials{
+					Username: "me",
+					Password: "swordfish",
+				},
+			},
+			map[string]interface{}{
+				"auth": map[string]interface{}{
+					"passwordCredentials": map[string]interface{}{
+						"password": "swordfish",
+						"username": "me",
+					},
+				},
+			},
+		},
+		{
+			AuthOptions{
+				TokenCredentials: TokenCredentials{
+					ID: "1234567",
+				},
+			},
+			map[string]interface{}{
+				"auth": map[string]interface{}{
+					"token": map[string]interface{}{
+						"id": "1234567",
+					},
+				},
+			},
+		},
+	}
+
+	for _, successCase := range successCases {
+		actual, err := gophercloud.BuildRequestBody(successCase.opts, "auth")
+		th.AssertNoErr(t, err)
+		th.AssertDeepEquals(t, successCase.expected, actual)
+	}
+
+	var failCases = []struct {
+		opts     AuthOptions
+		expected error
+	}{
+		{
+			AuthOptions{
+				TenantID:   "987654321",
+				TenantName: "me",
+			},
+			gophercloud.ErrMissingInput{},
+		},
+		{
+			AuthOptions{
+				TokenCredentials: TokenCredentials{
+					ID: "1234567",
+				},
+				PasswordCredentials: PasswordCredentials{
+					Username: "me",
+					Password: "swordfish",
+				},
+			},
+			gophercloud.ErrMissingInput{},
+		},
+		{
+			AuthOptions{
+				PasswordCredentials: PasswordCredentials{
+					Password: "swordfish",
+				},
+			},
+			gophercloud.ErrMissingInput{},
+		},
+		{
+			AuthOptions{
+				PasswordCredentials: PasswordCredentials{
+					Username: "me",
+					Password: "swordfish",
+				},
+				OrFields: orFields{
+					Filler: 2,
+				},
+			},
+			gophercloud.ErrMissingInput{},
+		},
+	}
+
+	for _, failCase := range failCases {
+		_, err := gophercloud.BuildRequestBody(failCase.opts, "auth")
+		th.AssertDeepEquals(t, reflect.TypeOf(failCase.expected), reflect.TypeOf(err))
+	}
+}
diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go
new file mode 100644
index 0000000..7c0e84e
--- /dev/null
+++ b/testing/provider_client_test.go
@@ -0,0 +1,36 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestAuthenticatedHeaders(t *testing.T) {
+	p := &gophercloud.ProviderClient{
+		TokenID: "1234",
+	}
+	expected := map[string]string{"X-Auth-Token": "1234"}
+	actual := p.AuthenticatedHeaders()
+	th.CheckDeepEquals(t, expected, actual)
+}
+
+func TestUserAgent(t *testing.T) {
+	p := &gophercloud.ProviderClient{}
+
+	p.UserAgent.Prepend("custom-user-agent/2.4.0")
+	expected := "custom-user-agent/2.4.0 gophercloud/2.0.0"
+	actual := p.UserAgent.Join()
+	th.CheckEquals(t, expected, actual)
+
+	p.UserAgent.Prepend("another-custom-user-agent/0.3.0", "a-third-ua/5.9.0")
+	expected = "another-custom-user-agent/0.3.0 a-third-ua/5.9.0 custom-user-agent/2.4.0 gophercloud/2.0.0"
+	actual = p.UserAgent.Join()
+	th.CheckEquals(t, expected, actual)
+
+	p.UserAgent = gophercloud.UserAgent{}
+	expected = "gophercloud/2.0.0"
+	actual = p.UserAgent.Join()
+	th.CheckEquals(t, expected, actual)
+}
diff --git a/testing/service_client_test.go b/testing/service_client_test.go
new file mode 100644
index 0000000..904b303
--- /dev/null
+++ b/testing/service_client_test.go
@@ -0,0 +1,15 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestServiceURL(t *testing.T) {
+	c := &gophercloud.ServiceClient{Endpoint: "http://123.45.67.8/"}
+	expected := "http://123.45.67.8/more/parts/here"
+	actual := c.ServiceURL("more", "parts", "here")
+	th.CheckEquals(t, expected, actual)
+}
diff --git a/testing/util_test.go b/testing/util_test.go
new file mode 100644
index 0000000..5985bc3
--- /dev/null
+++ b/testing/util_test.go
@@ -0,0 +1,86 @@
+package testing
+
+import (
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestWaitFor(t *testing.T) {
+	err := gophercloud.WaitFor(5, func() (bool, error) {
+		return true, nil
+	})
+	th.CheckNoErr(t, err)
+}
+
+func TestNormalizeURL(t *testing.T) {
+	urls := []string{
+		"NoSlashAtEnd",
+		"SlashAtEnd/",
+	}
+	expected := []string{
+		"NoSlashAtEnd/",
+		"SlashAtEnd/",
+	}
+	for i := 0; i < len(expected); i++ {
+		th.CheckEquals(t, expected[i], gophercloud.NormalizeURL(urls[i]))
+	}
+
+}
+
+func TestNormalizePathURL(t *testing.T) {
+	baseDir, _ := os.Getwd()
+
+	rawPath := "template.yaml"
+	basePath, _ := filepath.Abs(".")
+	result, _ := gophercloud.NormalizePathURL(basePath, rawPath)
+	expected := strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "template.yaml"}, "/")
+	th.CheckEquals(t, expected, result)
+
+	rawPath = "http://www.google.com"
+	basePath, _ = filepath.Abs(".")
+	result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+	expected = "http://www.google.com"
+	th.CheckEquals(t, expected, result)
+
+	rawPath = "very/nested/file.yaml"
+	basePath, _ = filepath.Abs(".")
+	result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+	expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "very/nested/file.yaml"}, "/")
+	th.CheckEquals(t, expected, result)
+
+	rawPath = "very/nested/file.yaml"
+	basePath = "http://www.google.com"
+	result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+	expected = "http://www.google.com/very/nested/file.yaml"
+	th.CheckEquals(t, expected, result)
+
+	rawPath = "very/nested/file.yaml/"
+	basePath = "http://www.google.com/"
+	result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+	expected = "http://www.google.com/very/nested/file.yaml"
+	th.CheckEquals(t, expected, result)
+
+	rawPath = "very/nested/file.yaml"
+	basePath = "http://www.google.com/even/more"
+	result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+	expected = "http://www.google.com/even/more/very/nested/file.yaml"
+	th.CheckEquals(t, expected, result)
+
+	rawPath = "very/nested/file.yaml"
+	basePath = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more"}, "/")
+	result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+	expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more/very/nested/file.yaml"}, "/")
+	th.CheckEquals(t, expected, result)
+
+	rawPath = "very/nested/file.yaml/"
+	basePath = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more"}, "/")
+	result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+	expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more/very/nested/file.yaml"}, "/")
+	th.CheckEquals(t, expected, result)
+
+}
diff --git a/util_test.go b/util_test.go
deleted file mode 100644
index dcec77f..0000000
--- a/util_test.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package gophercloud
-
-import (
-	"os"
-	"path/filepath"
-	"strings"
-	"testing"
-
-	th "github.com/rackspace/gophercloud/testhelper"
-)
-
-func TestWaitFor(t *testing.T) {
-	err := WaitFor(5, func() (bool, error) {
-		return true, nil
-	})
-	th.CheckNoErr(t, err)
-}
-
-func TestNormalizeURL(t *testing.T) {
-	urls := []string{
-		"NoSlashAtEnd",
-		"SlashAtEnd/",
-	}
-	expected := []string{
-		"NoSlashAtEnd/",
-		"SlashAtEnd/",
-	}
-	for i := 0; i < len(expected); i++ {
-		th.CheckEquals(t, expected[i], NormalizeURL(urls[i]))
-	}
-
-}
-
-func TestNormalizePathURL(t *testing.T) {
-	baseDir, _ := os.Getwd()
-
-	rawPath := "template.yaml"
-	basePath, _ := filepath.Abs(".")
-	result, _ := NormalizePathURL(basePath, rawPath)
-	expected := strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "template.yaml"}, "/")
-	th.CheckEquals(t, expected, result)
-
-	rawPath = "http://www.google.com"
-	basePath, _ = filepath.Abs(".")
-	result, _ = NormalizePathURL(basePath, rawPath)
-	expected = "http://www.google.com"
-	th.CheckEquals(t, expected, result)
-
-	rawPath = "very/nested/file.yaml"
-	basePath, _ = filepath.Abs(".")
-	result, _ = NormalizePathURL(basePath, rawPath)
-	expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "very/nested/file.yaml"}, "/")
-	th.CheckEquals(t, expected, result)
-
-	rawPath = "very/nested/file.yaml"
-	basePath = "http://www.google.com"
-	result, _ = NormalizePathURL(basePath, rawPath)
-	expected = "http://www.google.com/very/nested/file.yaml"
-	th.CheckEquals(t, expected, result)
-
-	rawPath = "very/nested/file.yaml/"
-	basePath = "http://www.google.com/"
-	result, _ = NormalizePathURL(basePath, rawPath)
-	expected = "http://www.google.com/very/nested/file.yaml"
-	th.CheckEquals(t, expected, result)
-
-	rawPath = "very/nested/file.yaml"
-	basePath = "http://www.google.com/even/more"
-	result, _ = NormalizePathURL(basePath, rawPath)
-	expected = "http://www.google.com/even/more/very/nested/file.yaml"
-	th.CheckEquals(t, expected, result)
-
-	rawPath = "very/nested/file.yaml"
-	basePath = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more"}, "/")
-	result, _ = NormalizePathURL(basePath, rawPath)
-	expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more/very/nested/file.yaml"}, "/")
-	th.CheckEquals(t, expected, result)
-
-	rawPath = "very/nested/file.yaml/"
-	basePath = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more"}, "/")
-	result, _ = NormalizePathURL(basePath, rawPath)
-	expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more/very/nested/file.yaml"}, "/")
-	th.CheckEquals(t, expected, result)
-
-}