Merge pull request #491 from rgbkrk/find-node-by-ip-and-port

Method for finding a node by IP and Port
diff --git a/.travis.yml b/.travis.yml
index 0882a56..325b90a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,11 +2,10 @@
 install:
   - go get -v -tags 'fixtures acceptance' ./...
 go:
-  - 1.1
   - 1.2
   - 1.3
   - 1.4
-  - tip
+  - 1.5
 script: script/cibuild
 after_success:
   - go get golang.org/x/tools/cmd/cover
diff --git a/README.md b/README.md
index 19e90e0..0a0da59 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Gophercloud: the OpenStack SDK for Go
+# Gophercloud: an OpenStack SDK for Go
 [![Build Status](https://travis-ci.org/rackspace/gophercloud.svg?branch=master)](https://travis-ci.org/rackspace/gophercloud)
 
 Gophercloud is a flexible SDK that allows you to consume and work with OpenStack
diff --git a/acceptance/openstack/compute/v2/floatingip_test.go b/acceptance/openstack/compute/v2/floatingip_test.go
index ab7554b..de6efc9 100644
--- a/acceptance/openstack/compute/v2/floatingip_test.go
+++ b/acceptance/openstack/compute/v2/floatingip_test.go
@@ -49,7 +49,9 @@
 	return fip, err
 }
 
-func associateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
+func associateFloatingIPDeprecated(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
+	// This form works, but is considered deprecated.
+	// See associateFloatingIP or associateFloatingIPFixed
 	err := floatingip.Associate(client, serverId, fip.IP).ExtractErr()
 	th.AssertNoErr(t, err)
 	t.Logf("Associated floating IP %v from instance %v", fip.IP, serverId)
@@ -63,6 +65,63 @@
 	t.Logf("Floating IP %v is associated with Fixed IP %v", fip.IP, floatingIp.FixedIP)
 }
 
+func associateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
+	associateOpts := floatingip.AssociateOpts{
+		ServerID:   serverId,
+		FloatingIP: fip.IP,
+	}
+
+	err := floatingip.AssociateInstance(client, associateOpts).ExtractErr()
+	th.AssertNoErr(t, err)
+	t.Logf("Associated floating IP %v from instance %v", fip.IP, serverId)
+	defer func() {
+		err = floatingip.DisassociateInstance(client, associateOpts).ExtractErr()
+		th.AssertNoErr(t, err)
+		t.Logf("Disassociated floating IP %v from instance %v", fip.IP, serverId)
+	}()
+	floatingIp, err := floatingip.Get(client, fip.ID).Extract()
+	th.AssertNoErr(t, err)
+	t.Logf("Floating IP %v is associated with Fixed IP %v", fip.IP, floatingIp.FixedIP)
+}
+
+func associateFloatingIPFixed(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
+
+	network := os.Getenv("OS_NETWORK_NAME")
+	server, err := servers.Get(client, serverId).Extract()
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+
+	var fixedIP string
+	for _, networkAddresses := range server.Addresses[network].([]interface{}) {
+		address := networkAddresses.(map[string]interface{})
+		if address["OS-EXT-IPS:type"] == "fixed" {
+			if address["version"].(float64) == 4 {
+				fixedIP = address["addr"].(string)
+			}
+		}
+	}
+
+	associateOpts := floatingip.AssociateOpts{
+		ServerID:   serverId,
+		FloatingIP: fip.IP,
+		FixedIP:    fixedIP,
+	}
+
+	err = floatingip.AssociateInstance(client, associateOpts).ExtractErr()
+	th.AssertNoErr(t, err)
+	t.Logf("Associated floating IP %v from instance %v with Fixed IP %v", fip.IP, serverId, fixedIP)
+	defer func() {
+		err = floatingip.DisassociateInstance(client, associateOpts).ExtractErr()
+		th.AssertNoErr(t, err)
+		t.Logf("Disassociated floating IP %v from instance %v with Fixed IP %v", fip.IP, serverId, fixedIP)
+	}()
+	floatingIp, err := floatingip.Get(client, fip.ID).Extract()
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, floatingIp.FixedIP, fixedIP)
+	t.Logf("Floating IP %v is associated with Fixed IP %v", fip.IP, floatingIp.FixedIP)
+}
+
 func TestFloatingIP(t *testing.T) {
 	pool := os.Getenv("OS_POOL_NAME")
 	if pool == "" {
@@ -102,6 +161,8 @@
 		t.Logf("Floating IP deleted.")
 	}()
 
+	associateFloatingIPDeprecated(t, client, server.ID, fip)
 	associateFloatingIP(t, client, server.ID, fip)
+	associateFloatingIPFixed(t, client, server.ID, fip)
 
 }
diff --git a/acceptance/openstack/db/v1/common.go b/acceptance/openstack/db/v1/common.go
new file mode 100644
index 0000000..f7ffc37
--- /dev/null
+++ b/acceptance/openstack/db/v1/common.go
@@ -0,0 +1,70 @@
+// +build acceptance db
+
+package v1
+
+import (
+	"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"
+)
+
+func newClient(t *testing.T) *gophercloud.ServiceClient {
+	ao, err := openstack.AuthOptionsFromEnv()
+	th.AssertNoErr(t, err)
+
+	client, err := openstack.AuthenticatedClient(ao)
+	th.AssertNoErr(t, err)
+
+	c, err := openstack.NewDBV1(client, gophercloud.EndpointOpts{
+		Region: os.Getenv("OS_REGION_NAME"),
+	})
+	th.AssertNoErr(t, err)
+
+	return c
+}
+
+type context struct {
+	test       *testing.T
+	client     *gophercloud.ServiceClient
+	instanceID string
+	DBIDs      []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/openstack/db/v1/database_test.go b/acceptance/openstack/db/v1/database_test.go
new file mode 100644
index 0000000..2fd3175
--- /dev/null
+++ b/acceptance/openstack/db/v1/database_test.go
@@ -0,0 +1,45 @@
+// +build acceptance db
+
+package v1
+
+import (
+	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+func (c context) createDBs() {
+	opts := db.BatchCreateOpts{
+		db.CreateOpts{Name: "db1"},
+		db.CreateOpts{Name: "db2"},
+		db.CreateOpts{Name: "db3"},
+	}
+
+	err := db.Create(c.client, c.instanceID, opts).ExtractErr()
+	c.AssertNoErr(err)
+	c.Logf("Created three databases on instance %s: db1, db2, db3", c.instanceID)
+}
+
+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 []string{"db1", "db2", "db3"} {
+		err := db.Delete(c.client, c.instanceID, id).ExtractErr()
+		c.AssertNoErr(err)
+		c.Logf("Deleted DB %s", id)
+	}
+}
diff --git a/acceptance/openstack/db/v1/flavor_test.go b/acceptance/openstack/db/v1/flavor_test.go
new file mode 100644
index 0000000..46f986c
--- /dev/null
+++ b/acceptance/openstack/db/v1/flavor_test.go
@@ -0,0 +1,31 @@
+// +build acceptance db
+
+package v1
+
+import (
+	"github.com/rackspace/gophercloud/openstack/db/v1/flavors"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+func (c context) listFlavors() {
+	c.Logf("Listing flavors")
+
+	err := flavors.List(c.client).EachPage(func(page pagination.Page) (bool, error) {
+		flavorList, err := flavors.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/openstack/db/v1/instance_test.go b/acceptance/openstack/db/v1/instance_test.go
new file mode 100644
index 0000000..dfded21
--- /dev/null
+++ b/acceptance/openstack/db/v1/instance_test.go
@@ -0,0 +1,138 @@
+// +build acceptance db
+
+package v1
+
+import (
+	"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"
+)
+
+const envDSType = "DATASTORE_TYPE_ID"
+
+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()
+
+	// DATABASE tests
+	c.createDBs()
+	c.listDBs()
+
+	// USER tests
+	c.createUsers()
+	c.listUsers()
+
+	// TEARDOWN
+	c.deleteUsers()
+	c.deleteDBs()
+	c.deleteInstance()
+}
+
+func (c context) createInstance() {
+	if os.Getenv(envDSType) == "" {
+		c.test.Fatalf("%s must be set as an environment var", envDSType)
+	}
+
+	opts := instances.CreateOpts{
+		FlavorRef: "2",
+		Size:      5,
+		Name:      tools.RandomString("gopher_db", 5),
+		Datastore: &instances.DatastoreOpts{Type: os.Getenv(envDSType)},
+	}
+
+	instance, err := instances.Create(c.client, opts).Extract()
+	th.AssertNoErr(c.test, err)
+
+	c.Logf("Restarting %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).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: %s", instance.ID)
+}
+
+func (c context) deleteInstance() {
+	err := instances.Delete(c.client, c.instanceID).ExtractErr()
+	c.AssertNoErr(err)
+	c.Logf("Deleted instance %s", c.instanceID)
+}
+
+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? %d", 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, "3").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, 4).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)
+}
diff --git a/acceptance/openstack/db/v1/pkg.go b/acceptance/openstack/db/v1/pkg.go
new file mode 100644
index 0000000..b7b1f99
--- /dev/null
+++ b/acceptance/openstack/db/v1/pkg.go
@@ -0,0 +1 @@
+package v1
diff --git a/acceptance/openstack/db/v1/user_test.go b/acceptance/openstack/db/v1/user_test.go
new file mode 100644
index 0000000..25a4794
--- /dev/null
+++ b/acceptance/openstack/db/v1/user_test.go
@@ -0,0 +1,70 @@
+// +build acceptance db
+
+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"
+)
+
+func (c context) createUsers() {
+	users := []string{
+		tools.RandomString("user_", 5),
+		tools.RandomString("user_", 5),
+		tools.RandomString("user_", 5),
+	}
+
+	db1 := db.CreateOpts{Name: "db1"}
+	db2 := db.CreateOpts{Name: "db2"}
+	db3 := db.CreateOpts{Name: "db3"}
+
+	opts := u.BatchCreateOpts{
+		u.CreateOpts{
+			Name:      users[0],
+			Password:  tools.RandomString("", 5),
+			Databases: db.BatchCreateOpts{db1, db2, db3},
+		},
+		u.CreateOpts{
+			Name:      users[1],
+			Password:  tools.RandomString("", 5),
+			Databases: db.BatchCreateOpts{db1, db2},
+		},
+		u.CreateOpts{
+			Name:      users[2],
+			Password:  tools.RandomString("", 5),
+			Databases: db.BatchCreateOpts{db3},
+		},
+	}
+
+	err := u.Create(c.client, c.instanceID, opts).ExtractErr()
+	c.AssertNoErr(err)
+	c.Logf("Created three users on instance %s: %s, %s, %s", c.instanceID, users[0], users[1], users[2])
+	c.users = users
+}
+
+func (c context) listUsers() {
+	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) deleteUsers() {
+	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/openstack/identity/v2/token_test.go b/acceptance/openstack/identity/v2/token_test.go
index d903140..e01b3b3 100644
--- a/acceptance/openstack/identity/v2/token_test.go
+++ b/acceptance/openstack/identity/v2/token_test.go
@@ -9,7 +9,8 @@
 	th "github.com/rackspace/gophercloud/testhelper"
 )
 
-func TestAuthenticate(t *testing.T) {
+func TestAuthenticateAndValidate(t *testing.T) {
+	// 1. TestAuthenticate
 	ao := v2AuthOptions(t)
 	service := unauthenticatedClient(t)
 
@@ -35,4 +36,19 @@
 			t.Logf("      - region=[%s] publicURL=[%s]", endpoint.Region, endpoint.PublicURL)
 		}
 	}
+
+	// 2. TestValidate
+	client := authenticatedClient(t)
+
+	// Validate Token!
+	getResult := tokens2.Get(client, token.ID)
+
+	// Extract and print the user.
+	user, err := getResult.ExtractUser()
+	th.AssertNoErr(t, err)
+
+	t.Logf("Acquired User: [%s]", user.Name)
+	t.Logf("The User id: [%s]", user.ID)
+	t.Logf("The User username: [%s]", user.UserName)
+	t.Logf("The User roles: [%#v]", user.Roles)
 }
diff --git a/acceptance/openstack/networking/v2/port_test.go b/acceptance/openstack/networking/v2/port_test.go
index 03e8e27..91bf5bd 100644
--- a/acceptance/openstack/networking/v2/port_test.go
+++ b/acceptance/openstack/networking/v2/port_test.go
@@ -45,10 +45,20 @@
 	th.AssertEquals(t, p.ID, portID)
 
 	// Update port
-	p, err = ports.Update(Client, portID, ports.UpdateOpts{Name: "new_port_name"}).Extract()
+	updateOpts := ports.UpdateOpts{
+		Name: "new_port_name",
+		AllowedAddressPairs: []ports.AddressPair{
+			ports.AddressPair{IPAddress: "192.168.199.201"},
+		},
+	}
+	p, err = ports.Update(Client, portID, updateOpts).Extract()
+
 	th.AssertNoErr(t, err)
 	th.AssertEquals(t, p.Name, "new_port_name")
 
+	updatedPort, err := ports.Get(Client, portID).Extract()
+	th.AssertEquals(t, updatedPort.AllowedAddressPairs[0].IPAddress, "192.168.199.201")
+
 	// Delete port
 	res := ports.Delete(Client, portID)
 	th.AssertNoErr(t, res.Err)
@@ -82,8 +92,8 @@
 		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)
+			t.Logf("Port: ID [%s] Name [%s] Status [%s] MAC addr [%s] Fixed IPs [%#v] Security groups [%#v] Allowed Address Pairs [%#v]",
+				p.ID, p.Name, p.Status, p.MACAddress, p.FixedIPs, p.SecurityGroups, p.AllowedAddressPairs)
 		}
 
 		return true, nil
@@ -108,6 +118,9 @@
 		IPVersion:  subnets.IPv4,
 		Name:       "my_subnet",
 		EnableDHCP: subnets.Down,
+		AllocationPools: []subnets.AllocationPool{
+			subnets.AllocationPool{Start: "192.168.199.2", End: "192.168.199.200"},
+		},
 	}).Extract()
 	return s.ID, err
 }
diff --git a/acceptance/openstack/orchestration/v1/stacks_test.go b/acceptance/openstack/orchestration/v1/stacks_test.go
index 01e76d6..db31cd4 100644
--- a/acceptance/openstack/orchestration/v1/stacks_test.go
+++ b/acceptance/openstack/orchestration/v1/stacks_test.go
@@ -79,3 +79,75 @@
 	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/openstack/orchestration/v1/stacktemplates_test.go b/acceptance/openstack/orchestration/v1/stacktemplates_test.go
index 14d8f44..22d5e88 100644
--- a/acceptance/openstack/orchestration/v1/stacktemplates_test.go
+++ b/acceptance/openstack/orchestration/v1/stacktemplates_test.go
@@ -46,22 +46,21 @@
 	th.AssertNoErr(t, err)
 	t.Logf("retrieved template: %+v\n", tmpl)
 
-	validateOpts := stacktemplates.ValidateOpts{
-		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{}{
+	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": map[string]interface{}{
-				"hello_world": map[string]interface{}{
+			"resources": {
+				"hello_world": {
 					"type": "OS::Nova::Server",
-					"properties": map[string]interface{}{
+					"properties": {
 						"key_name": "heat_key",
-						"flavor": map[string]interface{}{
+						"flavor": {
 							"get_param": "flavor",
 						},
 						"image":     "ad091b52-742f-469e-8f3c-fd81cadf0743",
@@ -69,8 +68,7 @@
 					},
 				},
 			},
-		},
-	}
+		}`}
 	validatedTemplate, err := stacktemplates.Validate(client, validateOpts).Extract()
 	th.AssertNoErr(t, err)
 	t.Logf("validated template: %+v\n", validatedTemplate)
diff --git a/acceptance/rackspace/db/v1/backup_test.go b/acceptance/rackspace/db/v1/backup_test.go
new file mode 100644
index 0000000..522aace
--- /dev/null
+++ b/acceptance/rackspace/db/v1/backup_test.go
@@ -0,0 +1,84 @@
+// +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
new file mode 100644
index 0000000..24512b9
--- /dev/null
+++ b/acceptance/rackspace/db/v1/common.go
@@ -0,0 +1,73 @@
+// +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
new file mode 100644
index 0000000..81bd40a
--- /dev/null
+++ b/acceptance/rackspace/db/v1/config_group_test.go
@@ -0,0 +1,93 @@
+// +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
new file mode 100644
index 0000000..d5c448f
--- /dev/null
+++ b/acceptance/rackspace/db/v1/database_test.go
@@ -0,0 +1,54 @@
+// +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
new file mode 100644
index 0000000..0d6e6df
--- /dev/null
+++ b/acceptance/rackspace/db/v1/flavor_test.go
@@ -0,0 +1,32 @@
+// +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
new file mode 100644
index 0000000..b5540e3
--- /dev/null
+++ b/acceptance/rackspace/db/v1/instance_test.go
@@ -0,0 +1,169 @@
+// +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/pkg.go b/acceptance/rackspace/db/v1/pkg.go
new file mode 100644
index 0000000..b7b1f99
--- /dev/null
+++ b/acceptance/rackspace/db/v1/pkg.go
@@ -0,0 +1 @@
+package v1
diff --git a/acceptance/rackspace/db/v1/replica_test.go b/acceptance/rackspace/db/v1/replica_test.go
new file mode 100644
index 0000000..89edf9d
--- /dev/null
+++ b/acceptance/rackspace/db/v1/replica_test.go
@@ -0,0 +1,33 @@
+// +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
new file mode 100644
index 0000000..0488f5d
--- /dev/null
+++ b/acceptance/rackspace/db/v1/user_test.go
@@ -0,0 +1,125 @@
+// +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/orchestration/v1/stacks_test.go b/acceptance/rackspace/orchestration/v1/stacks_test.go
index cfec4e9..61969b5 100644
--- a/acceptance/rackspace/orchestration/v1/stacks_test.go
+++ b/acceptance/rackspace/orchestration/v1/stacks_test.go
@@ -80,3 +80,75 @@
 	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
index 1f7b217..e4ccd9e 100644
--- a/acceptance/rackspace/orchestration/v1/stacktemplates_test.go
+++ b/acceptance/rackspace/orchestration/v1/stacktemplates_test.go
@@ -49,21 +49,20 @@
 	t.Logf("retrieved template: %+v\n", tmpl)
 
 	validateOpts := osStacktemplates.ValidateOpts{
-		Template: map[string]interface{}{
-			"heat_template_version": "2013-05-23",
+		Template: `{"heat_template_version": "2013-05-23",
 			"description":           "Simple template to test heat commands",
-			"parameters": map[string]interface{}{
-				"flavor": map[string]interface{}{
+			"parameters": {
+				"flavor": {
 					"default": "m1.tiny",
 					"type":    "string",
 				},
 			},
-			"resources": map[string]interface{}{
-				"hello_world": map[string]interface{}{
+			"resources": {
+				"hello_world": {
 					"type": "OS::Nova::Server",
-					"properties": map[string]interface{}{
+					"properties": {
 						"key_name": "heat_key",
-						"flavor": map[string]interface{}{
+						"flavor": {
 							"get_param": "flavor",
 						},
 						"image":     "ad091b52-742f-469e-8f3c-fd81cadf0743",
@@ -71,8 +70,7 @@
 					},
 				},
 			},
-		},
-	}
+		}`}
 	validatedTemplate, err := stacktemplates.Validate(client, validateOpts).Extract()
 	th.AssertNoErr(t, err)
 	t.Logf("validated template: %+v\n", validatedTemplate)
diff --git a/openstack/client.go b/openstack/client.go
index 1193b19..33602a6 100644
--- a/openstack/client.go
+++ b/openstack/client.go
@@ -167,6 +167,7 @@
 
 	if options.AllowReauth {
 		client.ReauthFunc = func() error {
+			client.TokenID = ""
 			return AuthenticateV3(client, options)
 		}
 	}
@@ -261,3 +262,13 @@
 	}
 	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("database")
+	url, err := client.EndpointLocator(eo)
+	if err != nil {
+		return nil, err
+	}
+	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
+}
diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go
index c0ba368..c8edee0 100644
--- a/openstack/compute/v2/extensions/bootfromvolume/requests.go
+++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go
@@ -99,6 +99,11 @@
 		return res
 	}
 
+	// 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{
 		OkCodes: []int{200, 202},
 	})
diff --git a/openstack/compute/v2/extensions/floatingip/fixtures.go b/openstack/compute/v2/extensions/floatingip/fixtures.go
index 26f3299..e47fa4c 100644
--- a/openstack/compute/v2/extensions/floatingip/fixtures.go
+++ b/openstack/compute/v2/extensions/floatingip/fixtures.go
@@ -155,6 +155,25 @@
 	})
 }
 
+// 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) {
diff --git a/openstack/compute/v2/extensions/floatingip/requests.go b/openstack/compute/v2/extensions/floatingip/requests.go
index 8abb72d..8206462 100644
--- a/openstack/compute/v2/extensions/floatingip/requests.go
+++ b/openstack/compute/v2/extensions/floatingip/requests.go
@@ -26,6 +26,18 @@
 	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 == "" {
@@ -35,6 +47,26 @@
 	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
@@ -68,6 +100,7 @@
 // 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
 
@@ -79,7 +112,33 @@
 	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
 
@@ -90,3 +149,23 @@
 	_, 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
index ed2460e..4d86fe2 100644
--- a/openstack/compute/v2/extensions/floatingip/requests_test.go
+++ b/openstack/compute/v2/extensions/floatingip/requests_test.go
@@ -57,7 +57,7 @@
 	th.AssertNoErr(t, err)
 }
 
-func TestAssociate(t *testing.T) {
+func TestAssociateDeprecated(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
 	HandleAssociateSuccessfully(t)
@@ -68,7 +68,36 @@
 	th.AssertNoErr(t, err)
 }
 
-func TestDisassociate(t *testing.T) {
+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)
@@ -78,3 +107,17 @@
 	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/db/v1/configurations/doc.go b/openstack/db/v1/configurations/doc.go
new file mode 100644
index 0000000..45b9cfb
--- /dev/null
+++ b/openstack/db/v1/configurations/doc.go
@@ -0,0 +1,11 @@
+// Package configurations provides information and interaction with the
+// configuration API resource in the Rackspace Database service.
+//
+// A configuration group is a collection of key/value pairs which define how a
+// particular database operates. These key/value pairs are specific to each
+// datastore type and serve like settings. Some directives are capable of being
+// applied dynamically, while other directives require a server restart to take
+// effect. The configuration group can be applied to an instance at creation or
+// applied to an existing instance to modify the behavior of the running
+// datastore on the instance.
+package configurations
diff --git a/openstack/db/v1/configurations/fixtures.go b/openstack/db/v1/configurations/fixtures.go
new file mode 100644
index 0000000..ae65416
--- /dev/null
+++ b/openstack/db/v1/configurations/fixtures.go
@@ -0,0 +1,157 @@
+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
new file mode 100644
index 0000000..83c7102
--- /dev/null
+++ b/openstack/db/v1/configurations/requests.go
@@ -0,0 +1,287 @@
+package configurations
+
+import (
+	"errors"
+
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/openstack/db/v1/instances"
+	"github.com/rackspace/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 ConfigPage{pagination.SinglePageBase(r)}
+	}
+
+	return pagination.NewPager(client, baseURL(client), pageFn)
+}
+
+// CreateOptsBuilder is a top-level interface which renders a JSON map.
+type CreateOptsBuilder interface {
+	ToConfigCreateMap() (map[string]interface{}, error)
+}
+
+// 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
+}
+
+// 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
+	// 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
+}
+
+// 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
+}
+
+// Create will create a new configuration group.
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
+	var res CreateResult
+
+	reqBody, err := opts.ToConfigCreateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	_, res.Err = client.Request("POST", baseURL(client), gophercloud.RequestOpts{
+		OkCodes:      []int{200},
+		JSONBody:     &reqBody,
+		JSONResponse: &res.Body,
+	})
+
+	return res
+}
+
+// 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
+}
+
+// UpdateOptsBuilder is the top-level interface for casting update options into
+// JSON maps.
+type UpdateOptsBuilder interface {
+	ToConfigUpdateMap() (map[string]interface{}, error)
+}
+
+// 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
+	// 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
+}
+
+// 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
+}
+
+// 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()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	_, res.Err = client.Request("PATCH", resourceURL(client, configID), gophercloud.RequestOpts{
+		OkCodes:  []int{200},
+		JSONBody: &reqBody,
+	})
+
+	return res
+}
+
+// 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()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	_, res.Err = client.Request("PUT", resourceURL(client, configID), gophercloud.RequestOpts{
+		OkCodes:  []int{202},
+		JSONBody: &reqBody,
+	})
+
+	return res
+}
+
+// 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
+}
+
+// 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 instances.InstancePage{pagination.LinkedPageBase{PageResult: r}}
+	}
+	return pagination.NewPager(client, instancesURL(client, configID), pageFn)
+}
+
+// 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 {
+	pageFn := 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
+// 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) ParamResult {
+	var res ParamResult
+
+	_, res.Err = client.Request("GET", getDSParamURL(client, datastoreID, versionID, paramID), gophercloud.RequestOpts{
+		OkCodes:      []int{200},
+		JSONResponse: &res.Body,
+	})
+
+	return res
+}
+
+// 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 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
+}
diff --git a/openstack/db/v1/configurations/requests_test.go b/openstack/db/v1/configurations/requests_test.go
new file mode 100644
index 0000000..db66f29
--- /dev/null
+++ b/openstack/db/v1/configurations/requests_test.go
@@ -0,0 +1,236 @@
+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
new file mode 100644
index 0000000..d0d1d6e
--- /dev/null
+++ b/openstack/db/v1/configurations/results.go
@@ -0,0 +1,197 @@
+package configurations
+
+import (
+	"fmt"
+	"reflect"
+	"time"
+
+	"github.com/mitchellh/mapstructure"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/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"`
+	Description          string
+	ID                   string
+	Name                 string
+	Values               map[string]interface{}
+}
+
+// ConfigPage contains a page of Config resources in a paginated collection.
+type ConfigPage struct {
+	pagination.SinglePageBase
+}
+
+// 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
+}
+
+// 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"`
+	}
+
+	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
+}
+
+type commonResult struct {
+	gophercloud.Result
+}
+
+// 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 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
+}
+
+// GetResult represents the result of a Get operation.
+type GetResult struct {
+	commonResult
+}
+
+// CreateResult represents the result of a Create operation.
+type CreateResult struct {
+	commonResult
+}
+
+// UpdateResult represents the result of an Update operation.
+type UpdateResult struct {
+	gophercloud.ErrResult
+}
+
+// ReplaceResult represents the result of a Replace operation.
+type ReplaceResult struct {
+	gophercloud.ErrResult
+}
+
+// DeleteResult represents the result of a Delete operation.
+type DeleteResult struct {
+	gophercloud.ErrResult
+}
+
+// Param represents a configuration parameter API resource.
+type Param struct {
+	Max             int
+	Min             int
+	Name            string
+	RestartRequired bool `mapstructure:"restart_required" json:"restart_required"`
+	Type            string
+}
+
+// ParamPage contains a page of Param resources in a paginated collection.
+type ParamPage struct {
+	pagination.SinglePageBase
+}
+
+// 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
+}
+
+// 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"`
+	}
+
+	err := mapstructure.Decode(casted, &resp)
+	return resp.Params, err
+}
+
+// ParamResult represents the result of an operation which retrieves details
+// about a particular configuration param.
+type ParamResult struct {
+	gophercloud.Result
+}
+
+// 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
+}
diff --git a/openstack/db/v1/configurations/urls.go b/openstack/db/v1/configurations/urls.go
new file mode 100644
index 0000000..abea961
--- /dev/null
+++ b/openstack/db/v1/configurations/urls.go
@@ -0,0 +1,31 @@
+package configurations
+
+import "github.com/rackspace/gophercloud"
+
+func baseURL(c *gophercloud.ServiceClient) string {
+	return c.ServiceURL("configurations")
+}
+
+func resourceURL(c *gophercloud.ServiceClient, configID string) string {
+	return c.ServiceURL("configurations", configID)
+}
+
+func instancesURL(c *gophercloud.ServiceClient, configID string) string {
+	return c.ServiceURL("configurations", configID, "instances")
+}
+
+func listDSParamsURL(c *gophercloud.ServiceClient, datastoreID, versionID string) string {
+	return c.ServiceURL("datastores", datastoreID, "versions", versionID, "parameters")
+}
+
+func getDSParamURL(c *gophercloud.ServiceClient, datastoreID, versionID, paramID string) string {
+	return c.ServiceURL("datastores", datastoreID, "versions", versionID, "parameters", paramID)
+}
+
+func listGlobalParamsURL(c *gophercloud.ServiceClient, versionID string) string {
+	return c.ServiceURL("datastores", "versions", versionID, "parameters")
+}
+
+func getGlobalParamURL(c *gophercloud.ServiceClient, versionID, paramID string) string {
+	return c.ServiceURL("datastores", "versions", versionID, "parameters", paramID)
+}
diff --git a/openstack/db/v1/databases/doc.go b/openstack/db/v1/databases/doc.go
new file mode 100644
index 0000000..15275fe
--- /dev/null
+++ b/openstack/db/v1/databases/doc.go
@@ -0,0 +1,6 @@
+// Package flavors provides information and interaction with the database API
+// resource in the OpenStack Database service.
+//
+// A database, when referred to here, refers to the database engine running on
+// an instance.
+package databases
diff --git a/openstack/db/v1/databases/fixtures.go b/openstack/db/v1/databases/fixtures.go
new file mode 100644
index 0000000..3e67721
--- /dev/null
+++ b/openstack/db/v1/databases/fixtures.go
@@ -0,0 +1,61 @@
+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
new file mode 100644
index 0000000..f1eb5d9
--- /dev/null
+++ b/openstack/db/v1/databases/requests.go
@@ -0,0 +1,115 @@
+package databases
+
+import (
+	"fmt"
+
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// CreateOptsBuilder builds create options
+type CreateOptsBuilder interface {
+	ToDBCreateMap() (map[string]interface{}, error)
+}
+
+// DatabaseOpts 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
+	// 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
+	// "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
+	// 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
+}
+
+// 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")
+	}
+	if len(opts.Name) > 64 {
+		return nil, fmt.Errorf("Name must be less than 64 chars long")
+	}
+
+	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
+}
+
+// BatchCreateOpts allows for multiple databases to created and modified.
+type BatchCreateOpts []CreateOpts
+
+// ToDBCreateMap renders a JSON map for creating DBs.
+func (opts BatchCreateOpts) ToDBCreateMap() (map[string]interface{}, error) {
+	dbs := make([]map[string]string, len(opts))
+	for i, db := range opts {
+		dbMap, err := db.ToMap()
+		if err != nil {
+			return nil, err
+		}
+		dbs[i] = dbMap
+	}
+	return map[string]interface{}{"databases": dbs}, nil
+}
+
+// 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()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	_, res.Err = client.Request("POST", baseURL(client, instanceID), gophercloud.RequestOpts{
+		JSONBody: &reqBody,
+		OkCodes:  []int{202},
+	})
+
+	return res
+}
+
+// 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 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
+}
diff --git a/openstack/db/v1/databases/requests_test.go b/openstack/db/v1/databases/requests_test.go
new file mode 100644
index 0000000..8a1b297
--- /dev/null
+++ b/openstack/db/v1/databases/requests_test.go
@@ -0,0 +1,66 @@
+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
new file mode 100644
index 0000000..7d4b6ae
--- /dev/null
+++ b/openstack/db/v1/databases/results.go
@@ -0,0 +1,72 @@
+package databases
+
+import (
+	"github.com/mitchellh/mapstructure"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// Database represents a Database API resource.
+type Database struct {
+	// Specifies the name of the MySQL database.
+	Name string
+
+	// Set of symbols and encodings. The default character set is utf8.
+	CharSet string
+
+	// Set of rules for comparing characters in a character set. The default
+	// value for collate is utf8_general_ci.
+	Collate string
+}
+
+// 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
+}
+
+// DBPage represents a single page of a paginated DB collection.
+type DBPage struct {
+	pagination.LinkedPageBase
+}
+
+// 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
+}
+
+// NextPageURL will retrieve the next page URL.
+func (page DBPage) 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) ([]Database, error) {
+	casted := page.(DBPage).Body
+
+	var response struct {
+		Databases []Database `mapstructure:"databases"`
+	}
+
+	err := mapstructure.Decode(casted, &response)
+	return response.Databases, err
+}
diff --git a/openstack/db/v1/databases/urls.go b/openstack/db/v1/databases/urls.go
new file mode 100644
index 0000000..027ca58
--- /dev/null
+++ b/openstack/db/v1/databases/urls.go
@@ -0,0 +1,11 @@
+package databases
+
+import "github.com/rackspace/gophercloud"
+
+func baseURL(c *gophercloud.ServiceClient, instanceID string) string {
+	return c.ServiceURL("instances", instanceID, "databases")
+}
+
+func dbURL(c *gophercloud.ServiceClient, instanceID, dbName string) string {
+	return c.ServiceURL("instances", instanceID, "databases", dbName)
+}
diff --git a/openstack/db/v1/datastores/doc.go b/openstack/db/v1/datastores/doc.go
new file mode 100644
index 0000000..ae14026
--- /dev/null
+++ b/openstack/db/v1/datastores/doc.go
@@ -0,0 +1,3 @@
+// Package datastores provides information and interaction with the datastore
+// API resource in the Rackspace Database service.
+package datastores
diff --git a/openstack/db/v1/datastores/fixtures.go b/openstack/db/v1/datastores/fixtures.go
new file mode 100644
index 0000000..fd767cd
--- /dev/null
+++ b/openstack/db/v1/datastores/fixtures.go
@@ -0,0 +1,100 @@
+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
new file mode 100644
index 0000000..9e147ab
--- /dev/null
+++ b/openstack/db/v1/datastores/requests.go
@@ -0,0 +1,47 @@
+package datastores
+
+import (
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/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 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
+}
+
+// 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 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
+}
diff --git a/openstack/db/v1/datastores/requests_test.go b/openstack/db/v1/datastores/requests_test.go
new file mode 100644
index 0000000..b4ce871
--- /dev/null
+++ b/openstack/db/v1/datastores/requests_test.go
@@ -0,0 +1,78 @@
+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
new file mode 100644
index 0000000..a86a3cc
--- /dev/null
+++ b/openstack/db/v1/datastores/results.go
@@ -0,0 +1,123 @@
+package datastores
+
+import (
+	"github.com/mitchellh/mapstructure"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// Version represents a version API resource. Multiple versions belong to a Datastore.
+type Version struct {
+	ID    string
+	Links []gophercloud.Link
+	Name  string
+}
+
+// Datastore represents a Datastore API resource.
+type Datastore struct {
+	DefaultVersion string `json:"default_version" mapstructure:"default_version"`
+	ID             string
+	Links          []gophercloud.Link
+	Name           string
+	Versions       []Version
+}
+
+// DatastorePartial is a meta structure which is used in various API responses.
+// It is a lightweight and truncated version of a full Datastore resource,
+// offering details of the Version, Type and VersionID only.
+type DatastorePartial struct {
+	Version   string
+	Type      string
+	VersionID string `json:"version_id" mapstructure:"version_id"`
+}
+
+// GetResult represents the result of a Get operation.
+type GetResult struct {
+	gophercloud.Result
+}
+
+// GetVersionResult represents the result of getting a version.
+type GetVersionResult struct {
+	gophercloud.Result
+}
+
+// DatastorePage represents a page of datastore resources.
+type DatastorePage struct {
+	pagination.SinglePageBase
+}
+
+// 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
+}
+
+// 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"`
+	}
+
+	err := mapstructure.Decode(casted, &resp)
+	return resp.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 response struct {
+		Datastore Datastore `mapstructure:"datastore"`
+	}
+
+	err := mapstructure.Decode(r.Body, &response)
+	return &response.Datastore, err
+}
+
+// DatastorePage represents a page of version resources.
+type VersionPage struct {
+	pagination.SinglePageBase
+}
+
+// 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
+}
+
+// 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"`
+	}
+
+	err := mapstructure.Decode(casted, &resp)
+	return resp.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 response struct {
+		Version Version `mapstructure:"version"`
+	}
+
+	err := mapstructure.Decode(r.Body, &response)
+	return &response.Version, err
+}
diff --git a/openstack/db/v1/datastores/urls.go b/openstack/db/v1/datastores/urls.go
new file mode 100644
index 0000000..c4d5248
--- /dev/null
+++ b/openstack/db/v1/datastores/urls.go
@@ -0,0 +1,19 @@
+package datastores
+
+import "github.com/rackspace/gophercloud"
+
+func baseURL(c *gophercloud.ServiceClient) string {
+	return c.ServiceURL("datastores")
+}
+
+func resourceURL(c *gophercloud.ServiceClient, dsID string) string {
+	return c.ServiceURL("datastores", dsID)
+}
+
+func versionsURL(c *gophercloud.ServiceClient, dsID string) string {
+	return c.ServiceURL("datastores", dsID, "versions")
+}
+
+func versionURL(c *gophercloud.ServiceClient, dsID, versionID string) string {
+	return c.ServiceURL("datastores", dsID, "versions", versionID)
+}
diff --git a/openstack/db/v1/flavors/doc.go b/openstack/db/v1/flavors/doc.go
new file mode 100644
index 0000000..4d281d5
--- /dev/null
+++ b/openstack/db/v1/flavors/doc.go
@@ -0,0 +1,7 @@
+// Package flavors provides information and interaction with the flavor API
+// resource in the OpenStack Database service.
+//
+// A flavor is an available hardware configuration for a database instance.
+// Each flavor has a unique combination of disk space, memory capacity and
+// priority for CPU time.
+package flavors
diff --git a/openstack/db/v1/flavors/fixtures.go b/openstack/db/v1/flavors/fixtures.go
new file mode 100644
index 0000000..f0016bc
--- /dev/null
+++ b/openstack/db/v1/flavors/fixtures.go
@@ -0,0 +1,50 @@
+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
new file mode 100644
index 0000000..fa34446
--- /dev/null
+++ b/openstack/db/v1/flavors/requests.go
@@ -0,0 +1,29 @@
+package flavors
+
+import (
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/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 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
+}
diff --git a/openstack/db/v1/flavors/requests_test.go b/openstack/db/v1/flavors/requests_test.go
new file mode 100644
index 0000000..88b5871
--- /dev/null
+++ b/openstack/db/v1/flavors/requests_test.go
@@ -0,0 +1,91 @@
+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
new file mode 100644
index 0000000..2cee010
--- /dev/null
+++ b/openstack/db/v1/flavors/results.go
@@ -0,0 +1,92 @@
+package flavors
+
+import (
+	"github.com/mitchellh/mapstructure"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// 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"`
+	}
+
+	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
+		WeaklyTypedInput: true,
+		Result:           &result,
+	})
+
+	err = decoder.Decode(gr.Body)
+	return &result.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"`
+
+	// The RAM capacity for the flavor.
+	RAM int `mapstructure:"ram"`
+
+	// The Name field provides a human-readable moniker for the flavor.
+	Name string `mapstructure:"name"`
+
+	// Links to access the flavor.
+	Links []gophercloud.Link
+}
+
+// FlavorPage contains a single page of the response from a List call.
+type FlavorPage struct {
+	pagination.LinkedPageBase
+}
+
+// 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
+}
+
+// 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"`
+	}
+
+	var r resp
+	err := mapstructure.Decode(p.Body, &r)
+	if err != nil {
+		return "", err
+	}
+
+	return gophercloud.ExtractNextURL(r.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"`
+	}
+
+	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
+		WeaklyTypedInput: true,
+		Result:           &container,
+	})
+
+	err = decoder.Decode(casted)
+
+	return container.Flavors, err
+}
diff --git a/openstack/db/v1/flavors/urls.go b/openstack/db/v1/flavors/urls.go
new file mode 100644
index 0000000..80da11f
--- /dev/null
+++ b/openstack/db/v1/flavors/urls.go
@@ -0,0 +1,11 @@
+package flavors
+
+import "github.com/rackspace/gophercloud"
+
+func getURL(client *gophercloud.ServiceClient, id string) string {
+	return client.ServiceURL("flavors", id)
+}
+
+func listURL(client *gophercloud.ServiceClient) string {
+	return client.ServiceURL("flavors")
+}
diff --git a/openstack/db/v1/instances/doc.go b/openstack/db/v1/instances/doc.go
new file mode 100644
index 0000000..dc5c90f
--- /dev/null
+++ b/openstack/db/v1/instances/doc.go
@@ -0,0 +1,7 @@
+// Package instances provides information and interaction with the instance API
+// resource in the OpenStack Database service.
+//
+// A database instance is an isolated database environment with compute and
+// storage resources in a single tenant environment on a shared physical host
+// machine.
+package instances
diff --git a/openstack/db/v1/instances/fixtures.go b/openstack/db/v1/instances/fixtures.go
new file mode 100644
index 0000000..af7b185
--- /dev/null
+++ b/openstack/db/v1/instances/fixtures.go
@@ -0,0 +1,169 @@
+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
new file mode 100644
index 0000000..f4a63b8
--- /dev/null
+++ b/openstack/db/v1/instances/requests.go
@@ -0,0 +1,238 @@
+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"
+)
+
+// CreateOptsBuilder is the top-level interface for create options.
+type CreateOptsBuilder interface {
+	ToInstanceCreateMap() (map[string]interface{}, error)
+}
+
+// DatastoreOpts represents the configuration for how an instance stores data.
+type DatastoreOpts struct {
+	Version string
+	Type    string
+}
+
+func (opts DatastoreOpts) ToMap() (map[string]string, error) {
+	return map[string]string{
+		"version": opts.Version,
+		"type":    opts.Type,
+	}, nil
+}
+
+// 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 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
+}
+
+// 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")
+	}
+	if opts.FlavorRef == "" {
+		return nil, fmt.Errorf("FlavorRef is a required field")
+	}
+
+	instance := map[string]interface{}{
+		"volume":    map[string]int{"size": opts.Size},
+		"flavorRef": opts.FlavorRef,
+	}
+
+	if opts.Name != "" {
+		instance["name"] = opts.Name
+	}
+	if opts.Databases != nil {
+		dbs, err := opts.Databases.ToDBCreateMap()
+		if err != nil {
+			return nil, err
+		}
+		instance["databases"] = dbs["databases"]
+	}
+	if opts.Users != nil {
+		users, err := opts.Users.ToUserCreateMap()
+		if err != nil {
+			return nil, err
+		}
+		instance["users"] = users["users"]
+	}
+
+	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 CreateOptsBuilder) CreateResult {
+	var res CreateResult
+
+	reqBody, err := opts.ToInstanceCreateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	_, res.Err = client.Request("POST", baseURL(client), gophercloud.RequestOpts{
+		JSONBody:     &reqBody,
+		JSONResponse: &res.Body,
+		OkCodes:      []int{200},
+	})
+
+	return res
+}
+
+// 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 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
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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
+}
diff --git a/openstack/db/v1/instances/requests_test.go b/openstack/db/v1/instances/requests_test.go
new file mode 100644
index 0000000..3cc2b70
--- /dev/null
+++ b/openstack/db/v1/instances/requests_test.go
@@ -0,0 +1,133 @@
+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
new file mode 100644
index 0000000..95aed16
--- /dev/null
+++ b/openstack/db/v1/instances/results.go
@@ -0,0 +1,213 @@
+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"
+)
+
+// Volume represents information about an attached volume for a database instance.
+type Volume struct {
+	// The size in GB of the volume
+	Size int
+
+	Used float64
+}
+
+// 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 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 Volume
+
+	// Indicates how the instance stores data.
+	Datastore datastores.DatastorePartial
+}
+
+type commonResult struct {
+	gophercloud.Result
+}
+
+// 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
+}
+
+// Extract will extract an Instance from various result structs.
+func (r commonResult) Extract() (*Instance, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+
+	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
+}
+
+// InstancePage represents a single page of a paginated instance collection.
+type InstancePage struct {
+	pagination.LinkedPageBase
+}
+
+// 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
+}
+
+// NextPageURL will retrieve the next page URL.
+func (page InstancePage) NextPageURL() (string, error) {
+	type resp struct {
+		Links []gophercloud.Link `mapstructure:"instances_links"`
+	}
+
+	var r resp
+	err := mapstructure.Decode(page.Body, &r)
+	if err != nil {
+		return "", err
+	}
+
+	return gophercloud.ExtractNextURL(r.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"`
+	}
+
+	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
+}
+
+// UserRootResult represents the result of an operation to enable the root user.
+type UserRootResult 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
+	}
+
+	var response struct {
+		User users.User `mapstructure:"user"`
+	}
+
+	err := mapstructure.Decode(r.Body, &response)
+
+	return &response.User, err
+}
+
+// ActionResult represents the result of action requests, such as: restarting
+// an instance service, resizing its memory allocation, and resizing its
+// attached volume size.
+type ActionResult struct {
+	gophercloud.ErrResult
+}
diff --git a/openstack/db/v1/instances/urls.go b/openstack/db/v1/instances/urls.go
new file mode 100644
index 0000000..28c0bec
--- /dev/null
+++ b/openstack/db/v1/instances/urls.go
@@ -0,0 +1,19 @@
+package instances
+
+import "github.com/rackspace/gophercloud"
+
+func baseURL(c *gophercloud.ServiceClient) string {
+	return c.ServiceURL("instances")
+}
+
+func resourceURL(c *gophercloud.ServiceClient, id string) string {
+	return c.ServiceURL("instances", id)
+}
+
+func userRootURL(c *gophercloud.ServiceClient, id string) string {
+	return c.ServiceURL("instances", id, "root")
+}
+
+func actionURL(c *gophercloud.ServiceClient, id string) string {
+	return c.ServiceURL("instances", id, "action")
+}
diff --git a/openstack/db/v1/users/doc.go b/openstack/db/v1/users/doc.go
new file mode 100644
index 0000000..cf07832
--- /dev/null
+++ b/openstack/db/v1/users/doc.go
@@ -0,0 +1,3 @@
+// Package users provides information and interaction with the user API
+// resource in the OpenStack Database service.
+package users
diff --git a/openstack/db/v1/users/fixtures.go b/openstack/db/v1/users/fixtures.go
new file mode 100644
index 0000000..516b335
--- /dev/null
+++ b/openstack/db/v1/users/fixtures.go
@@ -0,0 +1,37 @@
+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
new file mode 100644
index 0000000..7533fc4
--- /dev/null
+++ b/openstack/db/v1/users/requests.go
@@ -0,0 +1,132 @@
+package users
+
+import (
+	"errors"
+
+	"github.com/rackspace/gophercloud"
+	db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// CreateOptsBuilder is the top-level interface for creating JSON maps.
+type CreateOptsBuilder interface {
+	ToUserCreateMap() (map[string]interface{}, error)
+}
+
+// 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
+	// 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" field is the only requirement for each option.
+	Databases db.BatchCreateOpts
+
+	// [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 CreateOpts) ToMap() (map[string]interface{}, error) {
+
+	if opts.Name == "root" {
+		return nil, errors.New("root is a reserved user name and cannot be used")
+	}
+	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
+}
+
+// BatchCreateOpts allows multiple users to be created at once.
+type BatchCreateOpts []CreateOpts
+
+// ToUserCreateMap will generate a JSON map.
+func (opts BatchCreateOpts) ToUserCreateMap() (map[string]interface{}, error) {
+	users := make([]map[string]interface{}, len(opts))
+	for i, opt := range opts {
+		user, err := opt.ToMap()
+		if err != nil {
+			return nil, err
+		}
+		users[i] = user
+	}
+	return map[string]interface{}{"users": users}, nil
+}
+
+// Create asynchronously provisions a new user for the specified database
+// 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()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	_, res.Err = client.Request("POST", baseURL(client, instanceID), gophercloud.RequestOpts{
+		JSONBody: &reqBody,
+		OkCodes:  []int{202},
+	})
+
+	return res
+}
+
+// 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 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
+}
diff --git a/openstack/db/v1/users/requests_test.go b/openstack/db/v1/users/requests_test.go
new file mode 100644
index 0000000..5711f63
--- /dev/null
+++ b/openstack/db/v1/users/requests_test.go
@@ -0,0 +1,84 @@
+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
new file mode 100644
index 0000000..217ddd8
--- /dev/null
+++ b/openstack/db/v1/users/results.go
@@ -0,0 +1,73 @@
+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
+
+	// The databases associated with this user
+	Databases []db.Database
+}
+
+// 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
+}
+
+// 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
+}
diff --git a/openstack/db/v1/users/urls.go b/openstack/db/v1/users/urls.go
new file mode 100644
index 0000000..2a3cacd
--- /dev/null
+++ b/openstack/db/v1/users/urls.go
@@ -0,0 +1,11 @@
+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)
+}
diff --git a/openstack/identity/v2/tokens/fixtures.go b/openstack/identity/v2/tokens/fixtures.go
index 1cb0d05..6245259 100644
--- a/openstack/identity/v2/tokens/fixtures.go
+++ b/openstack/identity/v2/tokens/fixtures.go
@@ -10,6 +10,7 @@
 
 	"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.
@@ -54,6 +55,14 @@
 	},
 }
 
+// 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 = `
 {
@@ -99,6 +108,39 @@
 }
 `
 
+// 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) {
@@ -115,6 +157,19 @@
 	})
 }
 
+// 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) {
@@ -126,3 +181,15 @@
 	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 074a89e..1f51438 100644
--- a/openstack/identity/v2/tokens/requests.go
+++ b/openstack/identity/v2/tokens/requests.go
@@ -88,3 +88,12 @@
 	})
 	return result
 }
+
+// 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{
+		OkCodes: []int{200, 203},
+	})
+	return result
+}
diff --git a/openstack/identity/v2/tokens/requests_test.go b/openstack/identity/v2/tokens/requests_test.go
index 8b78c85..f1ec339 100644
--- a/openstack/identity/v2/tokens/requests_test.go
+++ b/openstack/identity/v2/tokens/requests_test.go
@@ -139,3 +139,14 @@
 
 	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 1eddb9d..67c577b 100644
--- a/openstack/identity/v2/tokens/results.go
+++ b/openstack/identity/v2/tokens/results.go
@@ -25,6 +25,17 @@
 	Tenant tenants.Tenant
 }
 
+// Authorization need user info which can get from token authentication's response
+type Role struct {
+	Name string `mapstructure:"name"`
+}
+type User struct {
+	ID       string `mapstructure:"id"`
+	Name     string `mapstructure:"name"`
+	UserName string `mapstructure:"username"`
+	Roles    []Role `mapstructure:"roles"`
+}
+
 // Endpoint represents a single API endpoint offered by a service.
 // It provides the public and internal URLs, if supported, along with a region specifier, again if provided.
 // The significance of the Region field will depend upon your provider.
@@ -74,6 +85,12 @@
 	gophercloud.Result
 }
 
+// GetResult is the deferred response from a Get call, which is the same with a Created token.
+// Use ExtractUser() to interpret it as a User.
+type GetResult struct {
+	CreateResult
+}
+
 // ExtractToken returns the just-created Token from a CreateResult.
 func (result CreateResult) ExtractToken() (*Token, error) {
 	if result.Err != nil {
@@ -131,3 +148,23 @@
 func createErr(err error) CreateResult {
 	return CreateResult{gophercloud.Result{Err: err}}
 }
+
+// ExtractUser returns the User from a GetResult.
+func (result GetResult) ExtractUser() (*User, error) {
+	if result.Err != nil {
+		return nil, result.Err
+	}
+
+	var response struct {
+		Access struct {
+			User User `mapstructure:"user"`
+		} `mapstructure:"access"`
+	}
+
+	err := mapstructure.Decode(result.Body, &response)
+	if err != nil {
+		return nil, err
+	}
+
+	return &response.Access.User, nil
+}
diff --git a/openstack/identity/v2/tokens/urls.go b/openstack/identity/v2/tokens/urls.go
index cd4c696..ee13932 100644
--- a/openstack/identity/v2/tokens/urls.go
+++ b/openstack/identity/v2/tokens/urls.go
@@ -6,3 +6,8 @@
 func CreateURL(client *gophercloud.ServiceClient) string {
 	return client.ServiceURL("tokens")
 }
+
+// GetURL generates the URL used to Validate Tokens.
+func GetURL(client *gophercloud.ServiceClient, token string) string {
+	return client.ServiceURL("tokens", token)
+}
diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go
index d449ca3..d63b1bb 100644
--- a/openstack/identity/v3/tokens/requests.go
+++ b/openstack/identity/v3/tokens/requests.go
@@ -15,9 +15,9 @@
 }
 
 func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string {
-	h := c.AuthenticatedHeaders()
-	h["X-Subject-Token"] = subjectToken
-	return h
+	return map[string]string{
+		"X-Subject-Token": subjectToken,
+	}
 }
 
 // Create authenticates and either generates a new token, or changes the Scope of an existing token.
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go
index 49d6f0b..29f752a 100644
--- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go
+++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go
@@ -102,6 +102,7 @@
 	// Populate request body
 	reqBody := request{FloatingIP: floatingIP{
 		FloatingNetworkID: opts.FloatingNetworkID,
+		FloatingIP:        opts.FloatingIP,
 		PortID:            opts.PortID,
 		FixedIP:           opts.FixedIP,
 		TenantID:          opts.TenantID,
diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go
old mode 100755
new mode 100644
index 077a717..1ffc136
--- a/openstack/networking/v2/extensions/layer3/routers/requests.go
+++ b/openstack/networking/v2/extensions/layer3/routers/requests.go
@@ -16,6 +16,7 @@
 	ID           string `q:"id"`
 	Name         string `q:"name"`
 	AdminStateUp *bool  `q:"admin_state_up"`
+	Distributed  *bool  `q:"distributed"`
 	Status       string `q:"status"`
 	TenantID     string `q:"tenant_id"`
 	Limit        int    `q:"limit"`
@@ -46,6 +47,7 @@
 type CreateOpts struct {
 	Name         string
 	AdminStateUp *bool
+	Distributed  *bool
 	TenantID     string
 	GatewayInfo  *GatewayInfo
 }
@@ -62,6 +64,7 @@
 	type router struct {
 		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"`
 	}
@@ -73,6 +76,7 @@
 	reqBody := request{Router: router{
 		Name:         gophercloud.MaybeString(opts.Name),
 		AdminStateUp: opts.AdminStateUp,
+		Distributed:  opts.Distributed,
 		TenantID:     gophercloud.MaybeString(opts.TenantID),
 	}}
 
@@ -96,7 +100,9 @@
 type UpdateOpts struct {
 	Name         string
 	AdminStateUp *bool
+	Distributed  *bool
 	GatewayInfo  *GatewayInfo
+	Routes       []Route
 }
 
 // Update allows routers to be updated. You can update the name, administrative
@@ -108,7 +114,9 @@
 	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"`
 	}
 
 	type request struct {
@@ -118,12 +126,17 @@
 	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{
diff --git a/openstack/networking/v2/extensions/layer3/routers/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/requests_test.go
old mode 100755
new mode 100644
index c34264d..dbdc6fa
--- a/openstack/networking/v2/extensions/layer3/routers/requests_test.go
+++ b/openstack/networking/v2/extensions/layer3/routers/requests_test.go
@@ -37,6 +37,7 @@
             "name": "second_routers",
             "admin_state_up": true,
             "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
+            "distributed": false,
             "id": "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b"
         },
         {
@@ -47,6 +48,7 @@
             "name": "router1",
             "admin_state_up": true,
             "tenant_id": "33a40233088643acb66ff6eb0ebea679",
+            "distributed": false,
             "id": "a9254bdb-2613-4a13-ac4c-adc581fba50d"
         }
     ]
@@ -69,6 +71,7 @@
 				Status:       "ACTIVE",
 				GatewayInfo:  GatewayInfo{NetworkID: ""},
 				AdminStateUp: true,
+				Distributed:  false,
 				Name:         "second_routers",
 				ID:           "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b",
 				TenantID:     "6b96ff0cb17a4b859e1e575d221683d3",
@@ -77,6 +80,7 @@
 				Status:       "ACTIVE",
 				GatewayInfo:  GatewayInfo{NetworkID: "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8"},
 				AdminStateUp: true,
+				Distributed:  false,
 				Name:         "router1",
 				ID:           "a9254bdb-2613-4a13-ac4c-adc581fba50d",
 				TenantID:     "33a40233088643acb66ff6eb0ebea679",
@@ -127,6 +131,7 @@
         "name": "foo_router",
         "admin_state_up": false,
         "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
+        "distributed": false,
         "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e"
     }
 }
@@ -167,9 +172,16 @@
         "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"
     }
 }
@@ -185,6 +197,7 @@
 	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) {
@@ -202,7 +215,13 @@
 			"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"
+            }
+        ]
     }
 }
 			`)
@@ -220,20 +239,76 @@
         "name": "new_name",
         "admin_state_up": true,
         "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
-        "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e"
+        "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"}
-	options := UpdateOpts{Name: "new_name", GatewayInfo: &gwi}
+	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) {
diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go
old mode 100755
new mode 100644
index bdad4cb..4534123
--- a/openstack/networking/v2/extensions/layer3/routers/results.go
+++ b/openstack/networking/v2/extensions/layer3/routers/results.go
@@ -12,6 +12,11 @@
 	NetworkID string `json:"network_id" mapstructure:"network_id"`
 }
 
+type Route struct {
+	NextHop         string `mapstructure:"nexthop" json:"nexthop"`
+	DestinationCIDR string `mapstructure:"destination" json:"destination"`
+}
+
 // Router represents a Neutron router. A router is a logical entity that
 // forwards packets across internal subnets and NATs (network address
 // translation) them on external networks through an appropriate gateway.
@@ -30,6 +35,9 @@
 	// Administrative state of the router.
 	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
 
+	// Whether router is disitrubted or not..
+	Distributed bool `json:"distributed" mapstructure:"distributed"`
+
 	// Human readable name for the router. Does not have to be unique.
 	Name string `json:"name" mapstructure:"name"`
 
@@ -39,6 +47,8 @@
 	// Owner of the router. Only admin users can specify a tenant identifier
 	// other than its own.
 	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+
+	Routes []Route `json:"routes" mapstructure:"routes"`
 }
 
 // RouterPage is the page returned by a pager when traversing over a
diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go
index a80ceb3..e06934a 100644
--- a/openstack/networking/v2/extensions/security/rules/requests.go
+++ b/openstack/networking/v2/extensions/security/rules/requests.go
@@ -104,8 +104,8 @@
 	TenantID string
 }
 
-// Create is an operation which provisions a new security group with default
-// security group rules for the IPv4 and IPv6 ether types.
+// 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
 
@@ -159,14 +159,14 @@
 	return res
 }
 
-// Get retrieves a particular security group based on its unique ID.
+// 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
 }
 
-// Delete will permanently delete a particular security group based on its unique ID.
+// 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)
diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go
index 2caf1ca..e73e10a 100644
--- a/openstack/networking/v2/ports/requests.go
+++ b/openstack/networking/v2/ports/requests.go
@@ -95,15 +95,16 @@
 
 // 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
+	NetworkID           string
+	Name                string
+	AdminStateUp        *bool
+	MACAddress          string
+	FixedIPs            interface{}
+	DeviceID            string
+	DeviceOwner         string
+	TenantID            string
+	SecurityGroups      []string
+	AllowedAddressPairs []AddressPair
 }
 
 // ToPortCreateMap casts a CreateOpts struct to a map.
@@ -139,6 +140,9 @@
 	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
 }
@@ -168,12 +172,13 @@
 
 // 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
+	Name                string
+	AdminStateUp        *bool
+	FixedIPs            interface{}
+	DeviceID            string
+	DeviceOwner         string
+	SecurityGroups      []string
+	AllowedAddressPairs []AddressPair
 }
 
 // ToPortUpdateMap casts an UpdateOpts struct to a map.
@@ -198,6 +203,9 @@
 	if opts.Name != "" {
 		p["name"] = opts.Name
 	}
+	if opts.AllowedAddressPairs != nil {
+		p["allowed_address_pairs"] = opts.AllowedAddressPairs
+	}
 
 	return map[string]interface{}{"port": p}, nil
 }
diff --git a/openstack/networking/v2/ports/requests_test.go b/openstack/networking/v2/ports/requests_test.go
index 9e323ef..b442996 100644
--- a/openstack/networking/v2/ports/requests_test.go
+++ b/openstack/networking/v2/ports/requests_test.go
@@ -164,7 +164,13 @@
 								"ip_address": "10.0.0.2"
 						}
 				],
-				"security_groups": ["foo"]
+				"security_groups": ["foo"],
+        "allowed_address_pairs": [
+          {
+            "ip_address": "10.0.0.4",
+            "mac_address": "fa:16:3e:c9:cb:f0"
+          }
+        ]
     }
 }
 			`)
@@ -177,7 +183,6 @@
     "port": {
         "status": "DOWN",
         "name": "private-port",
-        "allowed_address_pairs": [],
         "admin_state_up": true,
         "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
         "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
@@ -193,6 +198,12 @@
         "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": ""
     }
 }
@@ -208,6 +219,9 @@
 			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)
@@ -224,6 +238,9 @@
 	})
 	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) {
@@ -252,6 +269,12 @@
                 "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"
         ]
@@ -278,6 +301,12 @@
                 "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"
@@ -294,6 +323,9 @@
 			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()
@@ -303,6 +335,9 @@
 	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"})
 }
 
diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go
index 2511ff5..1f7eea1 100644
--- a/openstack/networking/v2/ports/results.go
+++ b/openstack/networking/v2/ports/results.go
@@ -19,7 +19,6 @@
 	var res struct {
 		Port *Port `json:"port"`
 	}
-
 	err := mapstructure.Decode(r.Body, &res)
 
 	return res.Port, err
@@ -51,6 +50,11 @@
 	IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"`
 }
 
+type AddressPair struct {
+	IPAddress  string `mapstructure:"ip_address" json:"ip_address,omitempty"`
+	MACAddress string `mapstructure:"mac_address" json:"mac_address,omitempty"`
+}
+
 // Port represents a Neutron port. See package documentation for a top-level
 // description of what this is.
 type Port struct {
@@ -78,6 +82,8 @@
 	SecurityGroups []string `mapstructure:"security_groups" json:"security_groups"`
 	// Identifies the device (e.g., virtual server) using this port.
 	DeviceID string `mapstructure:"device_id" json:"device_id"`
+	// Identifies the list of IP addresses the port will recognize/accept
+	AllowedAddressPairs []AddressPair `mapstructure:"allowed_address_pairs" json:"allowed_address_pairs"`
 }
 
 // PortPage is the page returned by a pager when traversing over a collection
diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go
index c2fbaae..f85add0 100644
--- a/openstack/objectstorage/v1/objects/requests.go
+++ b/openstack/objectstorage/v1/objects/requests.go
@@ -1,12 +1,13 @@
 package objects
 
 import (
-	"bytes"
+	"bufio"
 	"crypto/hmac"
 	"crypto/md5"
 	"crypto/sha1"
 	"fmt"
 	"io"
+	"io/ioutil"
 	"strings"
 	"time"
 
@@ -167,7 +168,7 @@
 	ObjectManifest     string `h:"X-Object-Manifest"`
 	TransferEncoding   string `h:"Transfer-Encoding"`
 	Expires            string `q:"expires"`
-	MultipartManifest  string `q:"multiple-manifest"`
+	MultipartManifest  string `q:"multipart-manifest"`
 	Signature          string `q:"signature"`
 }
 
@@ -213,19 +214,20 @@
 	}
 
 	hash := md5.New()
+	bufioReader := bufio.NewReader(io.TeeReader(content, hash))
+	io.Copy(ioutil.Discard, bufioReader)
+	localChecksum := hash.Sum(nil)
 
-	contentBuffer := bytes.NewBuffer([]byte{})
-	_, err := io.Copy(contentBuffer, io.TeeReader(content, hash))
+	h["ETag"] = fmt.Sprintf("%x", localChecksum)
+
+	_, err := content.Seek(0, 0)
 	if err != nil {
 		res.Err = err
 		return res
 	}
 
-	localChecksum := hash.Sum(nil)
-	h["ETag"] = fmt.Sprintf("%x", localChecksum)
-
 	ropts := gophercloud.RequestOpts{
-		RawBody:     strings.NewReader(contentBuffer.String()),
+		RawBody:     content,
 		MoreHeaders: h,
 	}
 
diff --git a/openstack/orchestration/v1/stackevents/requests.go b/openstack/orchestration/v1/stackevents/requests.go
index 53c3916..70c6b97 100644
--- a/openstack/orchestration/v1/stackevents/requests.go
+++ b/openstack/orchestration/v1/stackevents/requests.go
@@ -163,8 +163,8 @@
 	SortDir SortDir `q:"sort_dir"`
 }
 
-// ToResourceEventsListQuery formats a ListOpts into a query string.
-func (opts ListOpts) ToResourceEventsListQuery() (string, error) {
+// ToResourceEventListQuery formats a ListResourceEventsOpts into a query string.
+func (opts ListResourceEventsOpts) ToResourceEventListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
 	if err != nil {
 		return "", err
diff --git a/openstack/orchestration/v1/stackresources/fixtures.go b/openstack/orchestration/v1/stackresources/fixtures.go
index c3c3d3f..952dc54 100644
--- a/openstack/orchestration/v1/stackresources/fixtures.go
+++ b/openstack/orchestration/v1/stackresources/fixtures.go
@@ -28,10 +28,13 @@
 		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",
 	},
 }
 
@@ -40,6 +43,8 @@
 {
   "resources": [
   {
+  	"description": "Some resource",
+  	"attributes": {"SXSW": "atx"},
     "resource_name": "hello_world",
     "links": [
       {
@@ -54,6 +59,7 @@
     "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",
@@ -93,10 +99,13 @@
 		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",
 	},
 }
 
@@ -121,7 +130,10 @@
     "required_by": [],
     "resource_status": "CREATE_IN_PROGRESS",
     "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
-    "resource_type": "OS::Nova::Server"
+	"creation_time": "2015-02-05T21:33:10",
+    "resource_type": "OS::Nova::Server",
+	"attributes": {"SXSW": "atx"},
+	"description": "Some resource"
   }
 ]
 }`
@@ -162,6 +174,7 @@
 		},
 	},
 	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{}{},
@@ -174,6 +187,8 @@
 const GetOutput = `
 {
   "resource": {
+    "description": "Some resource",
+    "attributes": {"SXSW": "atx"},
     "resource_name": "wordpress_instance",
     "description": "",
     "links": [
@@ -240,7 +255,7 @@
 }
 
 // ListTypesExpected represents the expected object from a ListTypes request.
-var ListTypesExpected = []string{
+var ListTypesExpected = ResourceTypes{
 	"OS::Nova::Server",
 	"OS::Heat::RandomString",
 	"OS::Swift::Container",
@@ -251,6 +266,18 @@
 	"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 = `
 {
@@ -296,6 +323,11 @@
 		},
 	},
 	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.
@@ -314,7 +346,12 @@
       "description": "A resource description."
     }
   },
-  "resource_type": "OS::Heat::AResourceName"
+  "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`
@@ -332,56 +369,7 @@
 }
 
 // GetTemplateExpected represents the expected object from a Template request.
-var GetTemplateExpected = &TypeTemplate{
-	HeatTemplateFormatVersion: "2012-12-12",
-	Outputs: map[string]interface{}{
-		"private_key": map[string]interface{}{
-			"Description": "The private key if it has been saved.",
-			"Value":       "{\"Fn::GetAtt\": [\"KeyPair\", \"private_key\"]}",
-		},
-		"public_key": map[string]interface{}{
-			"Description": "The public key.",
-			"Value":       "{\"Fn::GetAtt\": [\"KeyPair\", \"public_key\"]}",
-		},
-	},
-	Parameters: map[string]interface{}{
-		"name": map[string]interface{}{
-			"Description": "The name of the key pair.",
-			"Type":        "String",
-		},
-		"public_key": map[string]interface{}{
-			"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": map[string]interface{}{
-			"AllowedValues": []string{
-				"True",
-				"true",
-				"False",
-				"false",
-			},
-			"Default":     false,
-			"Description": "True if the system should remember a generated private key; False otherwise.",
-			"Type":        "String",
-		},
-	},
-	Resources: map[string]interface{}{
-		"KeyPair": map[string]interface{}{
-			"Properties": map[string]interface{}{
-				"name": map[string]interface{}{
-					"Ref": "name",
-				},
-				"public_key": map[string]interface{}{
-					"Ref": "public_key",
-				},
-				"save_private_key": map[string]interface{}{
-					"Ref": "save_private_key",
-				},
-			},
-			"Type": "OS::Nova::KeyPair",
-		},
-	},
-}
+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 = `
diff --git a/openstack/orchestration/v1/stackresources/requests_test.go b/openstack/orchestration/v1/stackresources/requests_test.go
index f137878..e5045a7 100644
--- a/openstack/orchestration/v1/stackresources/requests_test.go
+++ b/openstack/orchestration/v1/stackresources/requests_test.go
@@ -1,6 +1,7 @@
 package stackresources
 
 import (
+	"sort"
 	"testing"
 
 	"github.com/rackspace/gophercloud/pagination"
@@ -75,6 +76,9 @@
 		th.AssertNoErr(t, err)
 
 		th.CheckDeepEquals(t, ListTypesExpected, actual)
+		// test if sorting works
+		sort.Sort(actual)
+		th.CheckDeepEquals(t, SortedListTypesExpected, actual)
 
 		return true, nil
 	})
@@ -103,5 +107,5 @@
 	th.AssertNoErr(t, err)
 
 	expected := GetTemplateExpected
-	th.AssertDeepEquals(t, expected, actual)
+	th.AssertDeepEquals(t, expected, string(actual))
 }
diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go
index df79d58..6ddc766 100644
--- a/openstack/orchestration/v1/stackresources/results.go
+++ b/openstack/orchestration/v1/stackresources/results.go
@@ -1,6 +1,7 @@
 package stackresources
 
 import (
+	"encoding/json"
 	"fmt"
 	"reflect"
 	"time"
@@ -12,15 +13,18 @@
 
 // Resource represents a stack resource.
 type Resource struct {
-	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{} `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:"-"`
 }
 
 // FindResult represents the result of a Find operation.
@@ -54,6 +58,13 @@
 			}
 			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
@@ -75,18 +86,6 @@
 	return len(resources) == 0, nil
 }
 
-// LastMarker returns the last container name in a ListResult.
-func (r ResourcePage) LastMarker() (string, error) {
-	resources, err := ExtractResources(r)
-	if err != nil {
-		return "", err
-	}
-	if len(resources) == 0 {
-		return "", nil
-	}
-	return resources[len(resources)-1].PhysicalID, nil
-}
-
 // 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
@@ -94,8 +93,9 @@
 	var response struct {
 		Resources []Resource `mapstructure:"resources"`
 	}
-	err := mapstructure.Decode(casted, &response)
-
+	if err := mapstructure.Decode(casted, &response); err != nil {
+		return nil, err
+	}
 	var resources []interface{}
 	switch casted.(type) {
 	case map[string]interface{}:
@@ -115,9 +115,16 @@
 			}
 			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, err
+	return response.Resources, nil
 }
 
 // GetResult represents the result of a Get operation.
@@ -149,6 +156,13 @@
 		}
 		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
 }
@@ -192,21 +206,42 @@
 	return len(rts) == 0, nil
 }
 
+// ResourceTypes represents the type that holds the result of ExtractResourceTypes.
+// We define methods on this type to sort it before output
+type ResourceTypes []string
+
+func (r ResourceTypes) Len() int {
+	return len(r)
+}
+
+func (r ResourceTypes) Swap(i, j int) {
+	r[i], r[j] = r[j], r[i]
+}
+
+func (r ResourceTypes) Less(i, j int) bool {
+	return r[i] < r[j]
+}
+
 // ExtractResourceTypes extracts and returns resource types.
-func ExtractResourceTypes(page pagination.Page) ([]string, error) {
+func ExtractResourceTypes(page pagination.Page) (ResourceTypes, error) {
+	casted := page.(ResourceTypePage).Body
+
 	var response struct {
-		ResourceTypes []string `mapstructure:"resource_types"`
+		ResourceTypes ResourceTypes `mapstructure:"resource_types"`
 	}
 
-	err := mapstructure.Decode(page.(ResourceTypePage).Body, &response)
-	return response.ResourceTypes, err
+	if err := mapstructure.Decode(casted, &response); err != nil {
+		return nil, err
+	}
+	return response.ResourceTypes, nil
 }
 
 // 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"`
+	Attributes    map[string]interface{} `mapstructure:"attributes"`
+	Properties    map[string]interface{} `mapstrucutre:"properties"`
+	ResourceType  string                 `mapstructure:"resource_type"`
+	SupportStatus map[string]interface{} `mapstructure:"support_status"`
 }
 
 // SchemaResult represents the result of a Schema operation.
@@ -230,31 +265,20 @@
 	return &res, nil
 }
 
-// TypeTemplate represents a stack resource template.
-type TypeTemplate struct {
-	HeatTemplateFormatVersion string
-	Outputs                   map[string]interface{}
-	Parameters                map[string]interface{}
-	Resources                 map[string]interface{}
-}
-
 // TemplateResult represents the result of a Template operation.
 type TemplateResult struct {
 	gophercloud.Result
 }
 
-// Extract returns a pointer to a TypeTemplate object and is called after a
+// Extract returns the template and is called after a
 // Template operation.
-func (r TemplateResult) Extract() (*TypeTemplate, error) {
+func (r TemplateResult) Extract() ([]byte, error) {
 	if r.Err != nil {
 		return nil, r.Err
 	}
-
-	var res TypeTemplate
-
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
+	template, err := json.MarshalIndent(r.Body, "", "  ")
+	if err != nil {
 		return nil, err
 	}
-
-	return &res, nil
+	return template, nil
 }
diff --git a/openstack/orchestration/v1/stacks/environment.go b/openstack/orchestration/v1/stacks/environment.go
new file mode 100644
index 0000000..abaff20
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/environment.go
@@ -0,0 +1,137 @@
+package stacks
+
+import (
+	"fmt"
+	"strings"
+)
+
+// Environment is a structure that represents stack environments
+type Environment struct {
+	TE
+}
+
+// EnvironmentSections is a map containing allowed sections in a stack environment file
+var EnvironmentSections = map[string]bool{
+	"parameters":         true,
+	"parameter_defaults": true,
+	"resource_registry":  true,
+}
+
+// Validate validates the contents of the Environment
+func (e *Environment) Validate() error {
+	if e.Parsed == nil {
+		if err := e.Parse(); err != nil {
+			return err
+		}
+	}
+	for key := range e.Parsed {
+		if _, ok := EnvironmentSections[key]; !ok {
+			return fmt.Errorf("Environment has wrong section: %s", key)
+		}
+	}
+	return nil
+}
+
+// Parse environment file to resolve the URL's of the resources. This is done by
+// reading from the `Resource Registry` section, which is why the function is
+// named GetRRFileContents.
+func (e *Environment) getRRFileContents(ignoreIf igFunc) error {
+	// initialize environment if empty
+	if e.Files == nil {
+		e.Files = make(map[string]string)
+	}
+	if e.fileMaps == nil {
+		e.fileMaps = make(map[string]string)
+	}
+
+	// get the resource registry
+	rr := e.Parsed["resource_registry"]
+
+	// search the resource registry for URLs
+	switch rr.(type) {
+	// process further only if the resource registry is a map
+	case map[string]interface{}, map[interface{}]interface{}:
+		rrMap, err := toStringKeys(rr)
+		if err != nil {
+			return err
+		}
+		// the resource registry might contain a base URL for the resource. If
+		// such a field is present, use it. Otherwise, use the default base URL.
+		var baseURL string
+		if val, ok := rrMap["base_url"]; ok {
+			baseURL = val.(string)
+		} else {
+			baseURL = e.baseURL
+		}
+
+		// The contents of the resource may be located in a remote file, which
+		// will be a template. Instantiate a temporary template to manage the
+		// contents.
+		tempTemplate := new(Template)
+		tempTemplate.baseURL = baseURL
+		tempTemplate.client = e.client
+
+		// Fetch the contents of remote resource URL's
+		if err = tempTemplate.getFileContents(rr, ignoreIf, false); err != nil {
+			return err
+		}
+		// check the `resources` section (if it exists) for more URL's. Note that
+		// the previous call to GetFileContents was (deliberately) not recursive
+		// as we want more control over where to look for URL's
+		if val, ok := rrMap["resources"]; ok {
+			switch val.(type) {
+			// process further only if the contents are a map
+			case map[string]interface{}, map[interface{}]interface{}:
+				resourcesMap, err := toStringKeys(val)
+				if err != nil {
+					return err
+				}
+				for _, v := range resourcesMap {
+					switch v.(type) {
+					case map[string]interface{}, map[interface{}]interface{}:
+						resourceMap, err := toStringKeys(v)
+						if err != nil {
+							return err
+						}
+						var resourceBaseURL string
+						// if base_url for the resource type is defined, use it
+						if val, ok := resourceMap["base_url"]; ok {
+							resourceBaseURL = val.(string)
+						} else {
+							resourceBaseURL = baseURL
+						}
+						tempTemplate.baseURL = resourceBaseURL
+						if err := tempTemplate.getFileContents(v, ignoreIf, false); err != nil {
+							return err
+						}
+					}
+				}
+			}
+		}
+		// if the resource registry contained any URL's, store them. This can
+		// then be passed as parameter to api calls to Heat api.
+		e.Files = tempTemplate.Files
+		return nil
+	default:
+		return nil
+	}
+}
+
+// function to choose keys whose values are other environment files
+func ignoreIfEnvironment(key string, value interface{}) bool {
+	// base_url and hooks refer to components which cannot have urls
+	if key == "base_url" || key == "hooks" {
+		return true
+	}
+	// if value is not string, it cannot be a URL
+	valueString, ok := value.(string)
+	if !ok {
+		return true
+	}
+	// if value contains `::`, it must be a reference to another resource type
+	// e.g. OS::Nova::Server : Rackspace::Cloud::Server
+	if strings.Contains(valueString, "::") {
+		return true
+	}
+	return false
+}
diff --git a/openstack/orchestration/v1/stacks/environment_test.go b/openstack/orchestration/v1/stacks/environment_test.go
new file mode 100644
index 0000000..3a3c2b9
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/environment_test.go
@@ -0,0 +1,184 @@
+package stacks
+
+import (
+	"fmt"
+	"net/http"
+	"net/url"
+	"strings"
+	"testing"
+
+	th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestEnvironmentValidation(t *testing.T) {
+	environmentJSON := new(Environment)
+	environmentJSON.Bin = []byte(ValidJSONEnvironment)
+	err := environmentJSON.Validate()
+	th.AssertNoErr(t, err)
+
+	environmentYAML := new(Environment)
+	environmentYAML.Bin = []byte(ValidYAMLEnvironment)
+	err = environmentYAML.Validate()
+	th.AssertNoErr(t, err)
+
+	environmentInvalid := new(Environment)
+	environmentInvalid.Bin = []byte(InvalidEnvironment)
+	if err = environmentInvalid.Validate(); err == nil {
+		t.Error("environment validation did not catch invalid environment")
+	}
+}
+
+func TestEnvironmentParsing(t *testing.T) {
+	environmentJSON := new(Environment)
+	environmentJSON.Bin = []byte(ValidJSONEnvironment)
+	err := environmentJSON.Parse()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, ValidJSONEnvironmentParsed, environmentJSON.Parsed)
+
+	environmentYAML := new(Environment)
+	environmentYAML.Bin = []byte(ValidJSONEnvironment)
+	err = environmentYAML.Parse()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, ValidJSONEnvironmentParsed, environmentYAML.Parsed)
+
+	environmentInvalid := new(Environment)
+	environmentInvalid.Bin = []byte("Keep Austin Weird")
+	err = environmentInvalid.Parse()
+	if err == nil {
+		t.Error("environment parsing did not catch invalid environment")
+	}
+}
+
+func TestIgnoreIfEnvironment(t *testing.T) {
+	var keyValueTests = []struct {
+		key   string
+		value interface{}
+		out   bool
+	}{
+		{"base_url", "afksdf", true},
+		{"not_type", "hooks", false},
+		{"get_file", "::", true},
+		{"hooks", "dfsdfsd", true},
+		{"type", "sdfubsduf.yaml", false},
+		{"type", "sdfsdufs.environment", false},
+		{"type", "sdfsdf.file", false},
+		{"type", map[string]string{"key": "value"}, true},
+	}
+	var result bool
+	for _, kv := range keyValueTests {
+		result = ignoreIfEnvironment(kv.key, kv.value)
+		if result != kv.out {
+			t.Errorf("key: %v, value: %v expected: %v, actual: %v", kv.key, kv.value, kv.out, result)
+		}
+	}
+}
+
+func TestGetRRFileContents(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	environmentContent := `
+heat_template_version: 2013-05-23
+
+description:
+  Heat WordPress template to support F18, using only Heat OpenStack-native
+  resource types, and without the requirement for heat-cfntools in the image.
+  WordPress is web software you can use to create a beautiful website or blog.
+  This template installs a single-instance WordPress deployment using a local
+  MySQL database to store the data.
+
+parameters:
+
+  key_name:
+    type: string
+    description : Name of a KeyPair to enable SSH access to the instance
+
+resources:
+  wordpress_instance:
+    type: OS::Nova::Server
+    properties:
+      image: { get_param: image_id }
+      flavor: { get_param: instance_type }
+      key_name: { get_param: key_name }`
+
+	dbContent := `
+heat_template_version: 2014-10-16
+
+description:
+  Test template for Trove resource capabilities
+
+parameters:
+  db_pass:
+    type: string
+    hidden: true
+    description: Database access password
+    default: secrete
+
+resources:
+
+service_db:
+  type: OS::Trove::Instance
+  properties:
+    name: trove_test_db
+    datastore_type: mariadb
+    flavor: 1GB Instance
+    size: 10
+    databases:
+    - name: test_data
+    users:
+    - name: kitchen_sink
+      password: { get_param: db_pass }
+      databases: [ test_data ]`
+	baseurl, err := getBasePath()
+	th.AssertNoErr(t, err)
+
+	fakeEnvURL := strings.Join([]string{baseurl, "my_env.yaml"}, "/")
+	urlparsed, err := url.Parse(fakeEnvURL)
+	th.AssertNoErr(t, err)
+	// handler for my_env.yaml
+	th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		w.Header().Set("Content-Type", "application/jason")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, environmentContent)
+	})
+
+	fakeDBURL := strings.Join([]string{baseurl, "my_db.yaml"}, "/")
+	urlparsed, err = url.Parse(fakeDBURL)
+	th.AssertNoErr(t, err)
+
+	// handler for my_db.yaml
+	th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		w.Header().Set("Content-Type", "application/jason")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, dbContent)
+	})
+
+	client := fakeClient{BaseClient: getHTTPClient()}
+	env := new(Environment)
+	env.Bin = []byte(`{"resource_registry": {"My::WP::Server": "my_env.yaml", "resources": {"my_db_server": {"OS::DBInstance": "my_db.yaml"}}}}`)
+	env.client = client
+
+	err = env.Parse()
+	th.AssertNoErr(t, err)
+	err = env.getRRFileContents(ignoreIfEnvironment)
+	th.AssertNoErr(t, err)
+	expectedEnvFilesContent := "\nheat_template_version: 2013-05-23\n\ndescription:\n  Heat WordPress template to support F18, using only Heat OpenStack-native\n  resource types, and without the requirement for heat-cfntools in the image.\n  WordPress is web software you can use to create a beautiful website or blog.\n  This template installs a single-instance WordPress deployment using a local\n  MySQL database to store the data.\n\nparameters:\n\n  key_name:\n    type: string\n    description : Name of a KeyPair to enable SSH access to the instance\n\nresources:\n  wordpress_instance:\n    type: OS::Nova::Server\n    properties:\n      image: { get_param: image_id }\n      flavor: { get_param: instance_type }\n      key_name: { get_param: key_name }"
+	expectedDBFilesContent := "\nheat_template_version: 2014-10-16\n\ndescription:\n  Test template for Trove resource capabilities\n\nparameters:\n  db_pass:\n    type: string\n    hidden: true\n    description: Database access password\n    default: secrete\n\nresources:\n\nservice_db:\n  type: OS::Trove::Instance\n  properties:\n    name: trove_test_db\n    datastore_type: mariadb\n    flavor: 1GB Instance\n    size: 10\n    databases:\n    - name: test_data\n    users:\n    - name: kitchen_sink\n      password: { get_param: db_pass }\n      databases: [ test_data ]"
+
+	th.AssertEquals(t, expectedEnvFilesContent, env.Files[fakeEnvURL])
+	th.AssertEquals(t, expectedDBFilesContent, env.Files[fakeDBURL])
+
+	env.fixFileRefs()
+	expectedParsed := map[string]interface{}{
+		"resource_registry": "2015-04-30",
+		"My::WP::Server":    fakeEnvURL,
+		"resources": map[string]interface{}{
+			"my_db_server": map[string]interface{}{
+				"OS::DBInstance": fakeDBURL,
+			},
+		},
+	}
+	env.Parse()
+	th.AssertDeepEquals(t, expectedParsed, env.Parsed)
+}
diff --git a/openstack/orchestration/v1/stacks/fixtures.go b/openstack/orchestration/v1/stacks/fixtures.go
index 3a621da..83f5dec 100644
--- a/openstack/orchestration/v1/stacks/fixtures.go
+++ b/openstack/orchestration/v1/stacks/fixtures.go
@@ -63,6 +63,7 @@
 		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",
@@ -78,6 +79,7 @@
 		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"},
 	},
 }
 
@@ -98,7 +100,8 @@
     "creation_time": "2015-02-03T20:07:39",
     "updated_time": null,
     "stack_status": "CREATE_COMPLETE",
-    "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87"
+    "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+	"tags": ["rackspace", "atx"]
   },
   {
     "description": "Simple template to test heat commands",
@@ -113,7 +116,8 @@
     "creation_time": "2014-12-11T17:39:16",
     "updated_time": "2014-12-11T17:40:37",
     "stack_status": "UPDATE_COMPLETE",
-    "id": "db6977b2-27aa-4775-9ae7-6213212d4ada"
+    "id": "db6977b2-27aa-4775-9ae7-6213212d4ada",
+	"tags": ["sfo", "satx"]
   }
   ]
 }
@@ -165,6 +169,7 @@
 	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.
@@ -194,7 +199,8 @@
     "stack_status": "CREATE_COMPLETE",
     "updated_time": null,
     "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
-    "template_description": "Simple template to test heat commands"
+    "template_description": "Simple template to test heat commands",
+	"tags": ["rackspace", "atx"]
   }
 }
 `
@@ -248,7 +254,6 @@
 		"OS::stack_name": "postman_stack",
 		"OS::stack_id":   "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
 	},
-	StatusReason: "Stack CREATE completed successfully",
 	Name:         "postman_stack",
 	CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
 	Links: []gophercloud.Link{
@@ -259,7 +264,6 @@
 	},
 	Capabilities:        []interface{}{},
 	NotificationTopics:  []interface{}{},
-	Status:              "CREATE_COMPLETE",
 	ID:                  "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
 	TemplateDescription: "Simple template to test heat commands",
 }
@@ -316,6 +320,20 @@
 			"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.
@@ -354,21 +372,233 @@
       "name": "hello_world",
       "resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63",
       "action": "CREATE",
-      "type": "OS::Nova::Server",
+      "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) {
-	th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/abandon", func(w http.ResponseWriter, r *http.Request) {
+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, AbandonOutput)
+		fmt.Fprintf(w, output)
 	})
 }
+
+// ValidJSONTemplate is a valid OpenStack Heat template in JSON format
+const ValidJSONTemplate = `
+{
+  "heat_template_version": "2014-10-16",
+  "parameters": {
+    "flavor": {
+      "default": 4353,
+      "description": "Flavor for the server to be created",
+      "hidden": true,
+      "type": "string"
+    }
+  },
+  "resources": {
+    "test_server": {
+      "properties": {
+        "flavor": "2 GB General Purpose v1",
+        "image": "Debian 7 (Wheezy) (PVHVM)",
+        "name": "test-server"
+      },
+      "type": "OS::Nova::Server"
+    }
+  }
+}
+`
+
+// 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
+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)
+`
+
+// InvalidTemplateNoVersion is an invalid template as it has no `version` section
+const InvalidTemplateNoVersion = `
+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)
+`
+
+// 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"
+          ]
+        }
+      }
+    }
+  }
+}
+`
+
+// ValidJSONEnvironmentParsed is the expected parsed version of ValidJSONEnvironment
+var ValidJSONEnvironmentParsed = map[string]interface{}{
+	"parameters": map[string]interface{}{
+		"user_key": "userkey",
+	},
+	"resource_registry": map[string]interface{}{
+		"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": map[string]interface{}{
+			"my_db_server": map[string]interface{}{
+				"OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml",
+			},
+			"my_server": map[string]interface{}{
+				"OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml",
+				"hooks":          "pre-create",
+			},
+			"nested_stack": map[string]interface{}{
+				"nested_resource": map[string]interface{}{
+					"hooks": "pre-update",
+				},
+				"another_resource": map[string]interface{}{
+					"hooks": []interface{}{
+						"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
+`
diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go
index 0dd6af2..1fc484d 100644
--- a/openstack/orchestration/v1/stacks/requests.go
+++ b/openstack/orchestration/v1/stacks/requests.go
@@ -2,6 +2,7 @@
 
 import (
 	"errors"
+	"strings"
 
 	"github.com/rackspace/gophercloud"
 	"github.com/rackspace/gophercloud/pagination"
@@ -32,9 +33,16 @@
 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
+	// 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:
@@ -50,8 +58,14 @@
 	// 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"}`
@@ -60,6 +74,8 @@
 	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
 }
 
 // ToStackCreateMap casts a CreateOpts struct to a map.
@@ -70,25 +86,60 @@
 		return s, errors.New("Required field 'Name' not provided.")
 	}
 	s["stack_name"] = opts.Name
-
-	if opts.Template != "" {
-		s["template"] = opts.Template
-	} else if opts.TemplateURL != "" {
-		s["template_url"] = opts.TemplateURL
+	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 {
-		return s, errors.New("Either Template or TemplateURL must be provided.")
+		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 opts.DisableRollback != nil {
+		s["disable_rollback"] = &opts.DisableRollback
+	}
+
+	if opts.EnvironmentOpts != nil {
+		if err := opts.EnvironmentOpts.Parse(); err != nil {
+			return nil, err
+		}
+		if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil {
+			return nil, err
+		}
+		opts.EnvironmentOpts.fixFileRefs()
+		for k, v := range opts.EnvironmentOpts.Files {
+			Files[k] = v
+		}
+		s["environment"] = string(opts.EnvironmentOpts.Bin)
+	} else if opts.Environment != "" {
+		s["environment"] = opts.Environment
+	}
+
+	if opts.Files != nil {
+		s["files"] = opts.Files
+	} else {
+		s["files"] = Files
 	}
 
 	if opts.DisableRollback != nil {
 		s["disable_rollback"] = &opts.DisableRollback
 	}
 
-	if opts.Environment != "" {
-		s["environment"] = opts.Environment
-	}
-	if opts.Files != nil {
-		s["files"] = opts.Files
-	}
 	if opts.Parameters != nil {
 		s["parameters"] = opts.Parameters
 	}
@@ -97,6 +148,9 @@
 		s["timeout_mins"] = opts.Timeout
 	}
 
+	if opts.Tags != nil {
+		s["tags"] = strings.Join(opts.Tags, ",")
+	}
 	return s, nil
 }
 
@@ -133,9 +187,16 @@
 	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
+	// 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:
@@ -151,8 +212,14 @@
 	// 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"}`
@@ -169,40 +236,69 @@
 		return s, errors.New("Required field 'Name' not provided.")
 	}
 	s["stack_name"] = opts.Name
-
-	if opts.Template != "" {
-		s["template"] = opts.Template
-	} else if opts.TemplateURL != "" {
-		s["template_url"] = opts.TemplateURL
+	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 {
-		return s, errors.New("Either Template or TemplateURL must be provided.")
-	}
+		if err := opts.TemplateOpts.Parse(); err != nil {
+			return nil, err
+		}
 
-	if opts.AdoptStackData == "" {
-		return s, errors.New("Required field 'AdoptStackData' not provided.")
+		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
+		}
 	}
-	s["adopt_stack_data"] = opts.AdoptStackData
 
 	if opts.DisableRollback != nil {
 		s["disable_rollback"] = &opts.DisableRollback
 	}
 
-	if opts.Environment != "" {
+	if opts.EnvironmentOpts != nil {
+		if err := opts.EnvironmentOpts.Parse(); err != nil {
+			return nil, err
+		}
+		if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil {
+			return nil, err
+		}
+		opts.EnvironmentOpts.fixFileRefs()
+		for k, v := range opts.EnvironmentOpts.Files {
+			Files[k] = v
+		}
+		s["environment"] = string(opts.EnvironmentOpts.Bin)
+	} else if opts.Environment != "" {
 		s["environment"] = opts.Environment
 	}
+
 	if opts.Files != nil {
 		s["files"] = opts.Files
+	} else {
+		s["files"] = Files
 	}
+
 	if opts.Parameters != nil {
 		s["parameters"] = opts.Parameters
 	}
 
-	if opts.Timeout == 0 {
-		return nil, errors.New("Required field 'Timeout' not provided.")
+	if opts.Timeout != 0 {
+		s["timeout"] = opts.Timeout
 	}
 	s["timeout_mins"] = opts.Timeout
 
-	return map[string]interface{}{"stack": s}, nil
+	return s, nil
 }
 
 // Adopt accepts an AdoptOpts struct and creates a new stack using the resources
@@ -305,9 +401,16 @@
 // 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
+	// 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:
@@ -319,8 +422,14 @@
 	// }
 	// 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"}`
@@ -329,26 +438,58 @@
 	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
 }
 
 // ToStackUpdateMap casts a CreateOpts struct to a map.
 func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) {
 	s := make(map[string]interface{})
-
-	if opts.Template != "" {
-		s["template"] = opts.Template
-	} else if opts.TemplateURL != "" {
-		s["template_url"] = opts.TemplateURL
+	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 {
-		return s, errors.New("Either Template or TemplateURL must be provided.")
+		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 opts.Environment != "" {
+	if opts.EnvironmentOpts != nil {
+		if err := opts.EnvironmentOpts.Parse(); err != nil {
+			return nil, err
+		}
+		if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil {
+			return nil, err
+		}
+		opts.EnvironmentOpts.fixFileRefs()
+		for k, v := range opts.EnvironmentOpts.Files {
+			Files[k] = v
+		}
+		s["environment"] = string(opts.EnvironmentOpts.Bin)
+	} else if opts.Environment != "" {
 		s["environment"] = opts.Environment
 	}
 
 	if opts.Files != nil {
 		s["files"] = opts.Files
+	} else {
+		s["files"] = Files
 	}
 
 	if opts.Parameters != nil {
@@ -359,6 +500,10 @@
 		s["timeout_mins"] = opts.Timeout
 	}
 
+	if opts.Tags != nil {
+		s["tags"] = strings.Join(opts.Tags, ",")
+	}
+
 	return s, nil
 }
 
@@ -397,9 +542,16 @@
 	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
+	// 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:
@@ -415,8 +567,14 @@
 	// 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"}`
@@ -433,25 +591,56 @@
 		return s, errors.New("Required field 'Name' not provided.")
 	}
 	s["stack_name"] = opts.Name
-
-	if opts.Template != "" {
-		s["template"] = opts.Template
-	} else if opts.TemplateURL != "" {
-		s["template_url"] = opts.TemplateURL
+	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 {
-		return s, errors.New("Either Template or TemplateURL must be provided.")
-	}
+		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 opts.DisableRollback != nil {
 		s["disable_rollback"] = &opts.DisableRollback
 	}
 
-	if opts.Environment != "" {
+	if opts.EnvironmentOpts != nil {
+		if err := opts.EnvironmentOpts.Parse(); err != nil {
+			return nil, err
+		}
+		if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil {
+			return nil, err
+		}
+		opts.EnvironmentOpts.fixFileRefs()
+		for k, v := range opts.EnvironmentOpts.Files {
+			Files[k] = v
+		}
+		s["environment"] = string(opts.EnvironmentOpts.Bin)
+	} else if opts.Environment != "" {
 		s["environment"] = opts.Environment
 	}
+
 	if opts.Files != nil {
 		s["files"] = opts.Files
+	} else {
+		s["files"] = Files
 	}
+
 	if opts.Parameters != nil {
 		s["parameters"] = opts.Parameters
 	}
diff --git a/openstack/orchestration/v1/stacks/requests_test.go b/openstack/orchestration/v1/stacks/requests_test.go
index 1e32ca2..0fde44b 100644
--- a/openstack/orchestration/v1/stacks/requests_test.go
+++ b/openstack/orchestration/v1/stacks/requests_test.go
@@ -52,6 +52,35 @@
 	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()
@@ -97,6 +126,52 @@
 	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()
@@ -163,6 +238,30 @@
 	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()
@@ -215,3 +314,45 @@
 	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 dca06e4..432bc8e 100644
--- a/openstack/orchestration/v1/stacks/results.go
+++ b/openstack/orchestration/v1/stacks/results.go
@@ -69,6 +69,7 @@
 	Name         string             `mapstructure:"stack_name"`
 	Status       string             `mapstructure:"stack_status"`
 	StatusReason string             `mapstructure:"stack_status_reason"`
+	Tags         []string           `mapstructure:"tags"`
 	UpdatedTime  time.Time          `mapstructure:"-"`
 }
 
@@ -81,7 +82,7 @@
 		Stacks []ListedStack `mapstructure:"stacks"`
 	}
 
-	err := mapstructure.Decode(page.(StackPage).Body, &res)
+	err := mapstructure.Decode(casted, &res)
 	if err != nil {
 		return nil, err
 	}
@@ -133,6 +134,7 @@
 	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:"-"`
@@ -200,21 +202,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           []map[string]interface{} `mapstructure:"resources"`
-	Status              string                   `mapstructure:"stack_status"`
-	StatusReason        string                   `mapstructure:"stack_status_reason"`
-	TemplateDescription string                   `mapstructure:"template_description"`
-	Timeout             int                      `mapstructure:"timeout_mins"`
-	UpdatedTime         time.Time                `mapstructure:"-"`
+	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:"-"`
 }
 
 // PreviewResult represents the result of a Preview operation.
@@ -269,12 +269,16 @@
 
 // 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"`
+	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"`
 }
 
 // AbandonResult represents the result of an Abandon operation.
diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go
new file mode 100644
index 0000000..234ce49
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/template.go
@@ -0,0 +1,139 @@
+package stacks
+
+import (
+	"fmt"
+	"github.com/rackspace/gophercloud"
+	"reflect"
+	"strings"
+)
+
+// Template is a structure that represents OpenStack Heat templates
+type Template struct {
+	TE
+}
+
+// TemplateFormatVersions is a map containing allowed variations of the template format version
+// Note that this contains the permitted variations of the _keys_ not the values.
+var TemplateFormatVersions = map[string]bool{
+	"HeatTemplateFormatVersion": true,
+	"heat_template_version":     true,
+	"AWSTemplateFormatVersion":  true,
+}
+
+// Validate validates the contents of the Template
+func (t *Template) Validate() error {
+	if t.Parsed == nil {
+		if err := t.Parse(); err != nil {
+			return err
+		}
+	}
+	for key := range t.Parsed {
+		if _, ok := TemplateFormatVersions[key]; ok {
+			return nil
+		}
+	}
+	return fmt.Errorf("Template format version not found.")
+}
+
+// GetFileContents recursively parses a template to search for urls. These urls
+// are assumed to point to other templates (known in OpenStack Heat as child
+// templates). The contents of these urls are fetched and stored in the `Files`
+// parameter of the template structure. This is the only way that a user can
+// use child templates that are located in their filesystem; urls located on the
+// web (e.g. on github or swift) can be fetched directly by Heat engine.
+func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool) error {
+	// initialize template if empty
+	if t.Files == nil {
+		t.Files = make(map[string]string)
+	}
+	if t.fileMaps == nil {
+		t.fileMaps = make(map[string]string)
+	}
+	switch te.(type) {
+	// if te is a map
+	case map[string]interface{}, map[interface{}]interface{}:
+		teMap, err := toStringKeys(te)
+		if err != nil {
+			return err
+		}
+		for k, v := range teMap {
+			value, ok := v.(string)
+			if !ok {
+				// if the value is not a string, recursively parse that value
+				if err := t.getFileContents(v, ignoreIf, recurse); err != nil {
+					return err
+				}
+			} else if !ignoreIf(k, value) {
+				// at this point, the k, v pair has a reference to an external template.
+				// The assumption of heatclient is that value v is a reference
+				// to a file in the users environment
+
+				// create a new child template
+				childTemplate := new(Template)
+
+				// initialize child template
+
+				// get the base location of the child template
+				baseURL, err := gophercloud.NormalizePathURL(t.baseURL, value)
+				if err != nil {
+					return err
+				}
+				childTemplate.baseURL = baseURL
+				childTemplate.client = t.client
+
+				// fetch the contents of the child template
+				if err := childTemplate.Parse(); err != nil {
+					return err
+				}
+
+				// process child template recursively if required. This is
+				// required if the child template itself contains references to
+				// other templates
+				if recurse {
+					if err := childTemplate.getFileContents(childTemplate.Parsed, ignoreIf, recurse); err != nil {
+						return err
+					}
+				}
+				// update parent template with current child templates' content.
+				// At this point, the child template has been parsed recursively.
+				t.fileMaps[value] = childTemplate.URL
+				t.Files[childTemplate.URL] = string(childTemplate.Bin)
+
+			}
+		}
+		return nil
+	// if te is a slice, call the function on each element of the slice.
+	case []interface{}:
+		teSlice := te.([]interface{})
+		for i := range teSlice {
+			if err := t.getFileContents(teSlice[i], ignoreIf, recurse); err != nil {
+				return err
+			}
+		}
+	// if te is anything else, return
+	case string, bool, float64, nil, int:
+		return nil
+	default:
+		return fmt.Errorf("%v: Unrecognized type", reflect.TypeOf(te))
+
+	}
+	return nil
+}
+
+// function to choose keys whose values are other template files
+func ignoreIfTemplate(key string, value interface{}) bool {
+	// key must be either `get_file` or `type` for value to be a URL
+	if key != "get_file" && key != "type" {
+		return true
+	}
+	// value must be a string
+	valueString, ok := value.(string)
+	if !ok {
+		return true
+	}
+	// `.template` and `.yaml` are allowed suffixes for template URLs when referred to by `type`
+	if key == "type" && !(strings.HasSuffix(valueString, ".template") || strings.HasSuffix(valueString, ".yaml")) {
+		return true
+	}
+	return false
+}
diff --git a/openstack/orchestration/v1/stacks/template_test.go b/openstack/orchestration/v1/stacks/template_test.go
new file mode 100644
index 0000000..6884db8
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/template_test.go
@@ -0,0 +1,148 @@
+package stacks
+
+import (
+	"fmt"
+	"net/http"
+	"net/url"
+	"strings"
+	"testing"
+
+	th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestTemplateValidation(t *testing.T) {
+	templateJSON := new(Template)
+	templateJSON.Bin = []byte(ValidJSONTemplate)
+	err := templateJSON.Validate()
+	th.AssertNoErr(t, err)
+
+	templateYAML := new(Template)
+	templateYAML.Bin = []byte(ValidYAMLTemplate)
+	err = templateYAML.Validate()
+	th.AssertNoErr(t, err)
+
+	templateInvalid := new(Template)
+	templateInvalid.Bin = []byte(InvalidTemplateNoVersion)
+	if err = templateInvalid.Validate(); err == nil {
+		t.Error("Template validation did not catch invalid template")
+	}
+}
+
+func TestTemplateParsing(t *testing.T) {
+	templateJSON := new(Template)
+	templateJSON.Bin = []byte(ValidJSONTemplate)
+	err := templateJSON.Parse()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, ValidJSONTemplateParsed, templateJSON.Parsed)
+
+	templateYAML := new(Template)
+	templateYAML.Bin = []byte(ValidJSONTemplate)
+	err = templateYAML.Parse()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, ValidJSONTemplateParsed, templateYAML.Parsed)
+
+	templateInvalid := new(Template)
+	templateInvalid.Bin = []byte("Keep Austin Weird")
+	err = templateInvalid.Parse()
+	if err == nil {
+		t.Error("Template parsing did not catch invalid template")
+	}
+}
+
+func TestIgnoreIfTemplate(t *testing.T) {
+	var keyValueTests = []struct {
+		key   string
+		value interface{}
+		out   bool
+	}{
+		{"not_get_file", "afksdf", true},
+		{"not_type", "sdfd", true},
+		{"get_file", "shdfuisd", false},
+		{"type", "dfsdfsd", true},
+		{"type", "sdfubsduf.yaml", false},
+		{"type", "sdfsdufs.template", false},
+		{"type", "sdfsdf.file", true},
+		{"type", map[string]string{"key": "value"}, true},
+	}
+	var result bool
+	for _, kv := range keyValueTests {
+		result = ignoreIfTemplate(kv.key, kv.value)
+		if result != kv.out {
+			t.Errorf("key: %v, value: %v expected: %v, actual: %v", kv.key, kv.value, result, kv.out)
+		}
+	}
+}
+
+func TestGetFileContents(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	baseurl, err := getBasePath()
+	th.AssertNoErr(t, err)
+	fakeURL := strings.Join([]string{baseurl, "my_nova.yaml"}, "/")
+	urlparsed, err := url.Parse(fakeURL)
+	th.AssertNoErr(t, err)
+	myNovaContent := `heat_template_version: 2014-10-16
+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)
+      networks:
+      - {uuid: 11111111-1111-1111-1111-111111111111}`
+	th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		w.Header().Set("Content-Type", "application/jason")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, myNovaContent)
+	})
+
+	client := fakeClient{BaseClient: getHTTPClient()}
+	te := new(Template)
+	te.Bin = []byte(`heat_template_version: 2015-04-30
+resources:
+  my_server:
+    type: my_nova.yaml`)
+	te.client = client
+
+	err = te.Parse()
+	th.AssertNoErr(t, err)
+	err = te.getFileContents(te.Parsed, ignoreIfTemplate, true)
+	th.AssertNoErr(t, err)
+	expectedFiles := map[string]string{
+		"my_nova.yaml": `heat_template_version: 2014-10-16
+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)
+      networks:
+      - {uuid: 11111111-1111-1111-1111-111111111111}`}
+	th.AssertEquals(t, expectedFiles["my_nova.yaml"], te.Files[fakeURL])
+	te.fixFileRefs()
+	expectedParsed := map[string]interface{}{
+		"heat_template_version": "2015-04-30",
+		"resources": map[string]interface{}{
+			"my_server": map[string]interface{}{
+				"type": fakeURL,
+			},
+		},
+	}
+	te.Parse()
+	th.AssertDeepEquals(t, expectedParsed, te.Parsed)
+}
diff --git a/openstack/orchestration/v1/stacks/utils.go b/openstack/orchestration/v1/stacks/utils.go
new file mode 100644
index 0000000..7b476a9
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/utils.go
@@ -0,0 +1,161 @@
+package stacks
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"path/filepath"
+	"reflect"
+	"strings"
+
+	"github.com/rackspace/gophercloud"
+	"gopkg.in/yaml.v2"
+)
+
+// Client is an interface that expects a Get method similar to http.Get. This
+// is needed for unit testing, since we can mock an http client. Thus, the
+// client will usually be an http.Client EXCEPT in unit tests.
+type Client interface {
+	Get(string) (*http.Response, error)
+}
+
+// TE is a base structure for both Template and Environment
+type TE struct {
+	// Bin stores the contents of the template or environment.
+	Bin []byte
+	// URL stores the URL of the template. This is allowed to be a 'file://'
+	// for local files.
+	URL string
+	// Parsed contains a parsed version of Bin. Since there are 2 different
+	// fields referring to the same value, you must be careful when accessing
+	// this filed.
+	Parsed map[string]interface{}
+	// Files contains a mapping between the urls in templates to their contents.
+	Files map[string]string
+	// fileMaps is a map used internally when determining Files.
+	fileMaps map[string]string
+	// baseURL represents the location of the template or environment file.
+	baseURL string
+	// client is an interface which allows TE to fetch contents from URLS
+	client Client
+}
+
+// Fetch fetches the contents of a TE from its URL. Once a TE structure has a
+// URL, call the fetch method to fetch the contents.
+func (t *TE) Fetch() error {
+	// if the baseURL is not provided, use the current directors as the base URL
+	if t.baseURL == "" {
+		u, err := getBasePath()
+		if err != nil {
+			return err
+		}
+		t.baseURL = u
+	}
+
+	// if the contents are already present, do nothing.
+	if t.Bin != nil {
+		return nil
+	}
+
+	// get a fqdn from the URL using the baseURL of the TE. For local files,
+	// the URL's will have the `file` scheme.
+	u, err := gophercloud.NormalizePathURL(t.baseURL, t.URL)
+	if err != nil {
+		return err
+	}
+	t.URL = u
+
+	// get an HTTP client if none present
+	if t.client == nil {
+		t.client = getHTTPClient()
+	}
+
+	// use the client to fetch the contents of the TE
+	resp, err := t.client.Get(t.URL)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return err
+	}
+	t.Bin = body
+	return nil
+}
+
+// get the basepath of the TE
+func getBasePath() (string, error) {
+	basePath, err := filepath.Abs(".")
+	if err != nil {
+		return "", err
+	}
+	u, err := gophercloud.NormalizePathURL("", basePath)
+	if err != nil {
+		return "", err
+	}
+	return u, nil
+}
+
+// get a an HTTP client to retrieve URL's. This client allows the use of `file`
+// scheme since we may need to fetch files from users filesystem
+func getHTTPClient() Client {
+	transport := &http.Transport{}
+	transport.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
+	return &http.Client{Transport: transport}
+}
+
+// Parse will parse the contents and then validate. The contents MUST be either JSON or YAML.
+func (t *TE) Parse() error {
+	if err := t.Fetch(); err != nil {
+		return err
+	}
+	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 t.Validate()
+}
+
+// Validate validates the contents of TE
+func (t *TE) Validate() error {
+	return nil
+}
+
+// igfunc is a parameter used by GetFileContents and GetRRFileContents to check
+// for valid URL's.
+type igFunc func(string, interface{}) bool
+
+// convert map[interface{}]interface{} to map[string]interface{}
+func toStringKeys(m interface{}) (map[string]interface{}, error) {
+	switch m.(type) {
+	case map[string]interface{}, map[interface{}]interface{}:
+		typedMap := make(map[string]interface{})
+		if _, ok := m.(map[interface{}]interface{}); ok {
+			for k, v := range m.(map[interface{}]interface{}) {
+				typedMap[k.(string)] = v
+			}
+		} else {
+			typedMap = m.(map[string]interface{})
+		}
+		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))
+
+	}
+}
+
+// fix the reference to files by replacing relative URL's by absolute
+// URL's
+func (t *TE) fixFileRefs() {
+	tStr := string(t.Bin)
+	if t.fileMaps == nil {
+		return
+	}
+	for k, v := range t.fileMaps {
+		tStr = strings.Replace(tStr, k, v, -1)
+	}
+	t.Bin = []byte(tStr)
+}
diff --git a/openstack/orchestration/v1/stacks/utils_test.go b/openstack/orchestration/v1/stacks/utils_test.go
new file mode 100644
index 0000000..2536e03
--- /dev/null
+++ b/openstack/orchestration/v1/stacks/utils_test.go
@@ -0,0 +1,94 @@
+package stacks
+
+import (
+	"fmt"
+	"net/http"
+	"net/url"
+	"strings"
+	"testing"
+
+	th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestTEFixFileRefs(t *testing.T) {
+	te := TE{
+		Bin: []byte(`string_to_replace: my fair lady`),
+		fileMaps: map[string]string{
+			"string_to_replace": "london bridge is falling down",
+		},
+	}
+	te.fixFileRefs()
+	th.AssertEquals(t, string(te.Bin), `london bridge is falling down: my fair lady`)
+}
+
+func TesttoStringKeys(t *testing.T) {
+	var test1 interface{} = map[interface{}]interface{}{
+		"Adam":  "Smith",
+		"Isaac": "Newton",
+	}
+	result1, err := toStringKeys(test1)
+	th.AssertNoErr(t, err)
+
+	expected := map[string]interface{}{
+		"Adam":  "Smith",
+		"Isaac": "Newton",
+	}
+	th.AssertDeepEquals(t, result1, expected)
+}
+
+func TestGetBasePath(t *testing.T) {
+	_, err := getBasePath()
+	th.AssertNoErr(t, err)
+}
+
+// test if HTTP client can read file type URLS. Read the URL of this file
+// because if this test is running, it means this file _must_ exist
+func TestGetHTTPClient(t *testing.T) {
+	client := getHTTPClient()
+	baseurl, err := getBasePath()
+	th.AssertNoErr(t, err)
+	resp, err := client.Get(baseurl)
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, resp.StatusCode, 200)
+}
+
+// Implement a fakeclient that can be used to mock out HTTP requests
+type fakeClient struct {
+	BaseClient Client
+}
+
+// this client's Get method first changes the URL given to point to
+// testhelper's (th) endpoints. This is done because the http Mux does not seem
+// to work for fqdns with the `file` scheme
+func (c fakeClient) Get(url string) (*http.Response, error) {
+	newurl := strings.Replace(url, "file://", th.Endpoint(), 1)
+	return c.BaseClient.Get(newurl)
+}
+
+// test the fetch function
+func TestFetch(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	baseurl, err := getBasePath()
+	th.AssertNoErr(t, err)
+	fakeURL := strings.Join([]string{baseurl, "file.yaml"}, "/")
+	urlparsed, err := url.Parse(fakeURL)
+	th.AssertNoErr(t, err)
+
+	th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		w.Header().Set("Content-Type", "application/jason")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, "Fee-fi-fo-fum")
+	})
+
+	client := fakeClient{BaseClient: getHTTPClient()}
+	te := TE{
+		URL:    "file.yaml",
+		client: client,
+	}
+	err = te.Fetch()
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, fakeURL, te.URL)
+	th.AssertEquals(t, "Fee-fi-fo-fum", string(te.Bin))
+}
diff --git a/openstack/orchestration/v1/stacktemplates/fixtures.go b/openstack/orchestration/v1/stacktemplates/fixtures.go
index 71fa808..fa9b301 100644
--- a/openstack/orchestration/v1/stacktemplates/fixtures.go
+++ b/openstack/orchestration/v1/stacktemplates/fixtures.go
@@ -10,29 +10,7 @@
 )
 
 // GetExpected represents the expected object from a Get request.
-var GetExpected = &Template{
-	Description:         "Simple template to test heat commands",
-	HeatTemplateVersion: "2013-05-23",
-	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",
-			},
-		},
-	},
-}
+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 = `
@@ -53,8 +31,7 @@
         "flavor": {
           "get_param": "flavor"
         },
-        "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
-        "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
+        "image": "ad091b52-742f-469e-8f3c-fd81cadf0743"
       }
     }
   }
diff --git a/openstack/orchestration/v1/stacktemplates/requests.go b/openstack/orchestration/v1/stacktemplates/requests.go
index ad1e468..c0cea35 100644
--- a/openstack/orchestration/v1/stacktemplates/requests.go
+++ b/openstack/orchestration/v1/stacktemplates/requests.go
@@ -23,14 +23,14 @@
 
 // ValidateOpts specifies the template validation parameters.
 type ValidateOpts struct {
-	Template    map[string]interface{}
+	Template    string
 	TemplateURL string
 }
 
 // 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 != nil {
+	if opts.Template != "" {
 		vo["template"] = opts.Template
 		return vo, nil
 	}
diff --git a/openstack/orchestration/v1/stacktemplates/requests_test.go b/openstack/orchestration/v1/stacktemplates/requests_test.go
index d31c4ac..42667c9 100644
--- a/openstack/orchestration/v1/stacktemplates/requests_test.go
+++ b/openstack/orchestration/v1/stacktemplates/requests_test.go
@@ -16,7 +16,7 @@
 	th.AssertNoErr(t, err)
 
 	expected := GetExpected
-	th.AssertDeepEquals(t, expected, actual)
+	th.AssertDeepEquals(t, expected, string(actual))
 }
 
 func TestValidateTemplate(t *testing.T) {
@@ -25,29 +25,29 @@
 	HandleValidateSuccessfully(t, ValidateOutput)
 
 	opts := ValidateOpts{
-		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",
-					},
-				},
-			},
-		},
+		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)
diff --git a/openstack/orchestration/v1/stacktemplates/results.go b/openstack/orchestration/v1/stacktemplates/results.go
index ac2f24b..4e9ba5a 100644
--- a/openstack/orchestration/v1/stacktemplates/results.go
+++ b/openstack/orchestration/v1/stacktemplates/results.go
@@ -1,42 +1,33 @@
 package stacktemplates
 
 import (
+	"encoding/json"
 	"github.com/mitchellh/mapstructure"
 	"github.com/rackspace/gophercloud"
 )
 
-// Template represents a stack template.
-type Template struct {
-	Description         string                 `mapstructure:"description"`
-	HeatTemplateVersion string                 `mapstructure:"heat_template_version"`
-	Parameters          map[string]interface{} `mapstructure:"parameters"`
-	Resources           map[string]interface{} `mapstructure:"resources"`
-}
-
 // GetResult represents the result of a Get operation.
 type GetResult struct {
 	gophercloud.Result
 }
 
-// Extract returns a pointer to a Template object and is called after a
-// Get operation.
-func (r GetResult) Extract() (*Template, error) {
+// Extract returns the JSON template and is called after a Get operation.
+func (r GetResult) Extract() ([]byte, error) {
 	if r.Err != nil {
 		return nil, r.Err
 	}
-
-	var res Template
-	if err := mapstructure.Decode(r.Body, &res); err != nil {
+	template, err := json.MarshalIndent(r.Body, "", "  ")
+	if err != nil {
 		return nil, err
 	}
-
-	return &res, nil
+	return template, nil
 }
 
 // ValidatedTemplate represents the parsed object returned from a Validate request.
 type ValidatedTemplate struct {
-	Description string
-	Parameters  map[string]interface{}
+	Description     string                 `mapstructure:"Description"`
+	Parameters      map[string]interface{} `mapstructure:"Parameters"`
+	ParameterGroups map[string]interface{} `mapstructure:"ParameterGroups"`
 }
 
 // ValidateResult represents the result of a Validate operation.
diff --git a/provider_client.go b/provider_client.go
index d920913..152a091 100644
--- a/provider_client.go
+++ b/provider_client.go
@@ -192,10 +192,13 @@
 			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
 		}
 	}
 
@@ -243,6 +246,8 @@
 		return []int{201, 202}
 	case method == "PUT":
 		return []int{201, 202}
+	case method == "PATCH":
+		return []int{200, 204}
 	case method == "DELETE":
 		return []int{202, 204}
 	}
@@ -296,6 +301,24 @@
 	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{}
diff --git a/rackspace/client.go b/rackspace/client.go
index db3f305..a8f413e 100644
--- a/rackspace/client.go
+++ b/rackspace/client.go
@@ -212,3 +212,13 @@
 	}
 	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
+}
diff --git a/rackspace/db/v1/backups/doc.go b/rackspace/db/v1/backups/doc.go
new file mode 100644
index 0000000..664eead
--- /dev/null
+++ b/rackspace/db/v1/backups/doc.go
@@ -0,0 +1,6 @@
+// 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
new file mode 100644
index 0000000..45c2376
--- /dev/null
+++ b/rackspace/db/v1/backups/fixtures.go
@@ -0,0 +1,66 @@
+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
new file mode 100644
index 0000000..9170d78
--- /dev/null
+++ b/rackspace/db/v1/backups/requests.go
@@ -0,0 +1,138 @@
+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
new file mode 100644
index 0000000..d706733
--- /dev/null
+++ b/rackspace/db/v1/backups/requests_test.go
@@ -0,0 +1,131 @@
+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
new file mode 100644
index 0000000..04faf32
--- /dev/null
+++ b/rackspace/db/v1/backups/results.go
@@ -0,0 +1,149 @@
+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
new file mode 100644
index 0000000..553444e
--- /dev/null
+++ b/rackspace/db/v1/backups/urls.go
@@ -0,0 +1,11 @@
+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
new file mode 100644
index 0000000..d8cb48a
--- /dev/null
+++ b/rackspace/db/v1/configurations/delegate.go
@@ -0,0 +1,79 @@
+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
new file mode 100644
index 0000000..580f02a
--- /dev/null
+++ b/rackspace/db/v1/configurations/delegate_test.go
@@ -0,0 +1,237 @@
+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
new file mode 100644
index 0000000..48c51d6
--- /dev/null
+++ b/rackspace/db/v1/configurations/doc.go
@@ -0,0 +1 @@
+package configurations
diff --git a/rackspace/db/v1/configurations/fixtures.go b/rackspace/db/v1/configurations/fixtures.go
new file mode 100644
index 0000000..d8a2233
--- /dev/null
+++ b/rackspace/db/v1/configurations/fixtures.go
@@ -0,0 +1,159 @@
+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
new file mode 100644
index 0000000..56552d1
--- /dev/null
+++ b/rackspace/db/v1/databases/delegate.go
@@ -0,0 +1,19 @@
+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
new file mode 100644
index 0000000..b9e50a5
--- /dev/null
+++ b/rackspace/db/v1/databases/delegate_test.go
@@ -0,0 +1,71 @@
+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
new file mode 100644
index 0000000..1a178b6
--- /dev/null
+++ b/rackspace/db/v1/databases/doc.go
@@ -0,0 +1,3 @@
+// 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
new file mode 100644
index 0000000..18cbec7
--- /dev/null
+++ b/rackspace/db/v1/databases/urls.go
@@ -0,0 +1 @@
+package databases
diff --git a/rackspace/db/v1/datastores/delegate.go b/rackspace/db/v1/datastores/delegate.go
new file mode 100644
index 0000000..573496d
--- /dev/null
+++ b/rackspace/db/v1/datastores/delegate.go
@@ -0,0 +1,28 @@
+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
new file mode 100644
index 0000000..71111b9
--- /dev/null
+++ b/rackspace/db/v1/datastores/delegate_test.go
@@ -0,0 +1,79 @@
+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
new file mode 100644
index 0000000..f36997a
--- /dev/null
+++ b/rackspace/db/v1/datastores/doc.go
@@ -0,0 +1 @@
+package datastores
diff --git a/rackspace/db/v1/flavors/delegate.go b/rackspace/db/v1/flavors/delegate.go
new file mode 100644
index 0000000..689b81e
--- /dev/null
+++ b/rackspace/db/v1/flavors/delegate.go
@@ -0,0 +1,17 @@
+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
new file mode 100644
index 0000000..f5f6442
--- /dev/null
+++ b/rackspace/db/v1/flavors/delegate_test.go
@@ -0,0 +1,95 @@
+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
new file mode 100644
index 0000000..922a4e6
--- /dev/null
+++ b/rackspace/db/v1/flavors/doc.go
@@ -0,0 +1,3 @@
+// 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
new file mode 100644
index 0000000..f2656fe
--- /dev/null
+++ b/rackspace/db/v1/instances/delegate.go
@@ -0,0 +1,49 @@
+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
new file mode 100644
index 0000000..716e0a4
--- /dev/null
+++ b/rackspace/db/v1/instances/delegate_test.go
@@ -0,0 +1,107 @@
+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
new file mode 100644
index 0000000..0c8ad63
--- /dev/null
+++ b/rackspace/db/v1/instances/doc.go
@@ -0,0 +1,3 @@
+// 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
new file mode 100644
index 0000000..c5ff37a
--- /dev/null
+++ b/rackspace/db/v1/instances/fixtures.go
@@ -0,0 +1,340 @@
+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
new file mode 100644
index 0000000..f4df692
--- /dev/null
+++ b/rackspace/db/v1/instances/requests.go
@@ -0,0 +1,199 @@
+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
new file mode 100644
index 0000000..7fa4601
--- /dev/null
+++ b/rackspace/db/v1/instances/requests_test.go
@@ -0,0 +1,246 @@
+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
new file mode 100644
index 0000000..cdcc9c7
--- /dev/null
+++ b/rackspace/db/v1/instances/results.go
@@ -0,0 +1,191 @@
+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
new file mode 100644
index 0000000..5955f4c
--- /dev/null
+++ b/rackspace/db/v1/instances/urls.go
@@ -0,0 +1,23 @@
+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
new file mode 100644
index 0000000..8298c46
--- /dev/null
+++ b/rackspace/db/v1/users/delegate.go
@@ -0,0 +1,16 @@
+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
new file mode 100644
index 0000000..7a1b773
--- /dev/null
+++ b/rackspace/db/v1/users/delegate_test.go
@@ -0,0 +1,48 @@
+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
new file mode 100644
index 0000000..84f2eb3
--- /dev/null
+++ b/rackspace/db/v1/users/doc.go
@@ -0,0 +1,3 @@
+// 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
new file mode 100644
index 0000000..5314e85
--- /dev/null
+++ b/rackspace/db/v1/users/fixtures.go
@@ -0,0 +1,77 @@
+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
new file mode 100644
index 0000000..74e47ab
--- /dev/null
+++ b/rackspace/db/v1/users/requests.go
@@ -0,0 +1,176 @@
+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
new file mode 100644
index 0000000..2f2dca7
--- /dev/null
+++ b/rackspace/db/v1/users/requests_test.go
@@ -0,0 +1,156 @@
+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
new file mode 100644
index 0000000..85b3a7a
--- /dev/null
+++ b/rackspace/db/v1/users/results.go
@@ -0,0 +1,149 @@
+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
new file mode 100644
index 0000000..bac8788
--- /dev/null
+++ b/rackspace/db/v1/users/urls.go
@@ -0,0 +1,19 @@
+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/orchestration/v1/stackresources/delegate_test.go b/rackspace/orchestration/v1/stackresources/delegate_test.go
index 18e9614..116e44c 100644
--- a/rackspace/orchestration/v1/stackresources/delegate_test.go
+++ b/rackspace/orchestration/v1/stackresources/delegate_test.go
@@ -104,5 +104,5 @@
 	th.AssertNoErr(t, err)
 
 	expected := os.GetTemplateExpected
-	th.AssertDeepEquals(t, expected, actual)
+	th.AssertDeepEquals(t, expected, string(actual))
 }
diff --git a/rackspace/orchestration/v1/stacks/delegate_test.go b/rackspace/orchestration/v1/stacks/delegate_test.go
index a1fb393..553ae94 100644
--- a/rackspace/orchestration/v1/stacks/delegate_test.go
+++ b/rackspace/orchestration/v1/stacks/delegate_test.go
@@ -172,6 +172,170 @@
 	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()
@@ -336,6 +500,172 @@
 	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()
@@ -390,6 +720,45 @@
 	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()
@@ -443,19 +812,59 @@
 	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.HandleAbandonSuccessfully(t, os.AbandonOutput)
 
-	//actual, err := Abandon(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract()
-	//th.AssertNoErr(t, err)
-	res := Abandon(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87") //.Extract()
-	th.AssertNoErr(t, res.Err)
-	t.Logf("actual: %+v", res)
+	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)
+	expected := os.AbandonExpected
+	th.AssertDeepEquals(t, expected, actual)
 }
-*/
diff --git a/rackspace/orchestration/v1/stacktemplates/delegate_test.go b/rackspace/orchestration/v1/stacktemplates/delegate_test.go
index d4006c4..d4d0f8f 100644
--- a/rackspace/orchestration/v1/stacktemplates/delegate_test.go
+++ b/rackspace/orchestration/v1/stacktemplates/delegate_test.go
@@ -17,7 +17,7 @@
 	th.AssertNoErr(t, err)
 
 	expected := os.GetExpected
-	th.AssertDeepEquals(t, expected, actual)
+	th.AssertDeepEquals(t, expected, string(actual))
 }
 
 func TestValidateTemplate(t *testing.T) {
@@ -26,29 +26,18 @@
 	os.HandleValidateSuccessfully(t, os.ValidateOutput)
 
 	opts := os.ValidateOpts{
-		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",
-					},
-				},
-			},
-		},
+		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)
diff --git a/testhelper/fixture/helper.go b/testhelper/fixture/helper.go
new file mode 100644
index 0000000..d54355d
--- /dev/null
+++ b/testhelper/fixture/helper.go
@@ -0,0 +1,31 @@
+package fixture
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/rackspace/gophercloud/testhelper/client"
+)
+
+func SetupHandler(t *testing.T, url, method, requestBody, responseBody string, status int) {
+	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, method)
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		if requestBody != "" {
+			th.TestJSONRequest(t, r, requestBody)
+		}
+
+		if responseBody != "" {
+			w.Header().Add("Content-Type", "application/json")
+		}
+
+		w.WriteHeader(status)
+
+		if responseBody != "" {
+			fmt.Fprintf(w, responseBody)
+		}
+	})
+}
diff --git a/util.go b/util.go
index fbd9fe9..3d6a4e4 100644
--- a/util.go
+++ b/util.go
@@ -2,6 +2,8 @@
 
 import (
 	"errors"
+	"net/url"
+	"path/filepath"
 	"strings"
 	"time"
 )
@@ -42,3 +44,39 @@
 	}
 	return url
 }
+
+// NormalizePathURL is used to convert rawPath to a fqdn, using basePath as
+// a reference in the filesystem, if necessary. basePath is assumed to contain
+// either '.' when first used, or the file:// type fqdn of the parent resource.
+// e.g. myFavScript.yaml => file://opt/lib/myFavScript.yaml
+func NormalizePathURL(basePath, rawPath string) (string, error) {
+	u, err := url.Parse(rawPath)
+	if err != nil {
+		return "", err
+	}
+	// if a scheme is defined, it must be a fqdn already
+	if u.Scheme != "" {
+		return u.String(), nil
+	}
+	// if basePath is a url, then child resources are assumed to be relative to it
+	bu, err := url.Parse(basePath)
+	if err != nil {
+		return "", err
+	}
+	var basePathSys, absPathSys string
+	if bu.Scheme != "" {
+		basePathSys = filepath.FromSlash(bu.Path)
+		absPathSys = filepath.Join(basePathSys, rawPath)
+		bu.Path = filepath.ToSlash(absPathSys)
+		return bu.String(), nil
+	}
+
+	absPathSys = filepath.Join(basePath, rawPath)
+	u.Path = filepath.ToSlash(absPathSys)
+	if err != nil {
+		return "", err
+	}
+	u.Scheme = "file"
+	return u.String(), nil
+
+}
diff --git a/util_test.go b/util_test.go
index 5a15a00..dcec77f 100644
--- a/util_test.go
+++ b/util_test.go
@@ -1,6 +1,9 @@
 package gophercloud
 
 import (
+	"os"
+	"path/filepath"
+	"strings"
 	"testing"
 
 	th "github.com/rackspace/gophercloud/testhelper"
@@ -12,3 +15,71 @@
 	})
 	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)
+
+}