Addressing comments
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancer_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancer_test.go
index b1fb0f6..7cc84ab 100644
--- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancer_test.go
+++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancer_test.go
@@ -73,7 +73,7 @@
 	getLoadbalancerWaitActive(t, loadbalancerID)
 
 	// create listener
-	listenerID := createListener(t, "HTTP", 80, loadbalancerID)
+	listenerID := createListener(t, listeners.ProtocolHTTP, 80, loadbalancerID)
 
 	// list listeners
 	listListeners(t)
@@ -91,7 +91,7 @@
 	getLoadbalancerWaitActive(t, loadbalancerID)
 
 	// create pool
-	poolID := createPool(t, "HTTP", listenerID, pools.LBMethodRoundRobin)
+	poolID := createPool(t, pools.ProtocolHTTP, listenerID, pools.LBMethodRoundRobin)
 
 	// list pools
 	listPools(t)
@@ -127,7 +127,7 @@
 	getLoadbalancerWaitActive(t, loadbalancerID)
 
 	// create monitor
-	monitorID := createMonitor(t, poolID, monitors.TypeHTTP, 10, 10, 3, "/login", "GET", "200")
+	monitorID := createMonitor(t, poolID, monitors.TypePING, 10, 10, 3)
 
 	// list monitors
 	listMonitors(t)
@@ -141,6 +141,29 @@
 	// get monitor
 	getMonitor(t, monitorID)
 
+	// get loadbalancer statuses tree
+	rawStatusTree, err := loadbalancers.GetStatuses(base.Client, loadbalancerID).ExtractStatuses()
+	if err == nil {
+		// verify statuses tree ID's of relevant objects
+		if rawStatusTree.Loadbalancer.ID != loadbalancerID {
+			t.Errorf("Loadbalancer ID did not match")
+		}
+		if rawStatusTree.Loadbalancer.Listeners[0].ID != listenerID {
+			t.Errorf("Listner ID did not match")
+		}
+		if rawStatusTree.Loadbalancer.Listeners[0].Pools[0].ID != poolID {
+			t.Errorf("Pool ID did not match")
+		}
+		if rawStatusTree.Loadbalancer.Listeners[0].Pools[0].Members[0].ID != memberID {
+			t.Errorf("Member ID did not match")
+		}
+		if rawStatusTree.Loadbalancer.Listeners[0].Pools[0].Monitor.ID != monitorID {
+			t.Errorf("Monitor ID did not match")
+		}
+	} else {
+		t.Errorf("Failed to extract Loadbalancer statuses tree: %v", err)
+	}
+
 	getLoadbalancerWaitActive(t, loadbalancerID)
 	deleteMonitor(t, monitorID)
 	getLoadbalancerWaitActive(t, loadbalancerID)
@@ -244,7 +267,7 @@
 }
 
 func listListeners(t *testing.T) {
-	err := listeners.List(base.Client, listeners.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+	err := listeners.List(base.Client, listeners.ListOpts{Name: "tmp_listener"}).EachPage(func(page pagination.Page) (bool, error) {
 		listenerList, err := listeners.ExtractListeners(page)
 		if err != nil {
 			t.Errorf("Failed to extract Listeners: %v", err)
@@ -262,7 +285,7 @@
 	th.AssertNoErr(t, err)
 }
 
-func createListener(t *testing.T, protocol string, protocolPort int, loadbalancerID string) string {
+func createListener(t *testing.T, protocol listeners.Protocol, protocolPort int, loadbalancerID string) string {
 	l, err := listeners.Create(base.Client, listeners.CreateOpts{
 		Protocol:       protocol,
 		ProtocolPort:   protocolPort,
@@ -317,7 +340,7 @@
 	th.AssertNoErr(t, err)
 }
 
-func createPool(t *testing.T, protocol string, listenerID string, lbMethod string) string {
+func createPool(t *testing.T, protocol pools.Protocol, listenerID string, lbMethod pools.LBMethod) string {
 	p, err := pools.Create(base.Client, pools.CreateOpts{
 		LBMethod:   lbMethod,
 		Protocol:   protocol,
@@ -411,17 +434,14 @@
 	t.Logf("Updated Member, ID [%s], in Pool, ID [%s]", memberID, poolID)
 }
 
-func createMonitor(t *testing.T, poolID string, checkType string, delay int, timeout int,
-	maxRetries int, urlPath string, httpMethod string, expectedCodes string) string {
+func createMonitor(t *testing.T, poolID string, checkType string, delay int, timeout int, maxRetries int) string {
 	m, err := monitors.Create(base.Client, monitors.CreateOpts{
-		PoolID:        poolID,
-		Delay:         delay,
-		Timeout:       timeout,
-		MaxRetries:    maxRetries,
-		Type:          checkType,
-		ExpectedCodes: expectedCodes,
-		URLPath:       expectedCodes,
-		HTTPMethod:    httpMethod,
+		PoolID:     poolID,
+		Name:       "tmp_monitor",
+		Delay:      delay,
+		Timeout:    timeout,
+		MaxRetries: maxRetries,
+		Type:       checkType,
 	}).Extract()
 
 	th.AssertNoErr(t, err)
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/results.go b/openstack/networking/v2/extensions/lbaas/monitors/results.go
index d595abd..ac2801b 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/results.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/results.go
@@ -25,6 +25,9 @@
 	// The unique ID for the VIP.
 	ID string
 
+	// Monitor name. Does not have to be unique.
+	Name string
+
 	// Owner of the VIP. Only an administrative user can specify a tenant ID
 	// other than its own.
 	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
diff --git a/openstack/networking/v2/extensions/lbaas_v2/doc.go b/openstack/networking/v2/extensions/lbaas_v2/doc.go
index ec7f9d6..247a75f 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/doc.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/doc.go
@@ -1,3 +1,5 @@
 // Package lbaas_v2 provides information and interaction with the Load Balancer
 // as a Service v2 extension for the OpenStack Networking service.
+// lbaas v2 api docs: http://developer.openstack.org/api-ref-networking-v2-ext.html#lbaas-v2.0
+// lbaas v2 api schema: https://github.com/openstack/neutron-lbaas/blob/master/neutron_lbaas/extensions/loadbalancerv2.py
 package lbaas_v2
diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/fixtures.go
new file mode 100644
index 0000000..0c06671
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/fixtures.go
@@ -0,0 +1,213 @@
+// +build fixtures
+package listeners
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/rackspace/gophercloud/testhelper/client"
+)
+
+// ListenersListBody contains the canned body of a listeners list response.
+const ListenersListBody = `
+{
+	"listeners":[
+		{
+			"id": "db902c0c-d5ff-4753-b465-668ad9656918",
+			"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
+			"name": "web",
+			"description": "listener config for the web tier",
+			"loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}],
+			"protocol": "HTTP",
+			"protocol_port": 80,
+			"default_pool_id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
+			"admin_state_up": true,
+			"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
+			"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
+		},
+		{
+			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+			"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
+			"name": "db",
+			"description": "listener config for the db tier",
+			"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+			"protocol": "TCP",
+			"protocol_port": 3306,
+			"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
+			"connection_limit": 2000,
+			"admin_state_up": true,
+			"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
+			"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
+		}
+	]
+}
+`
+
+// SingleServerBody is the canned body of a Get request on an existing listener.
+const SingleListenerBody = `
+{
+	"listener": {
+		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
+		"name": "db",
+		"description": "listener config for the db tier",
+		"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+		"protocol": "TCP",
+		"protocol_port": 3306,
+		"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
+		"connection_limit": 2000,
+		"admin_state_up": true,
+		"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
+		"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
+	}
+}
+`
+
+// PostUpdateListenerBody is the canned response body of a Update request on an existing listener.
+const PostUpdateListenerBody = `
+{
+	"listener": {
+		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
+		"name": "NewListenerName",
+		"description": "listener config for the db tier",
+		"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+		"protocol": "TCP",
+		"protocol_port": 3306,
+		"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
+		"connection_limit": 1000,
+		"admin_state_up": true,
+		"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
+		"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
+	}
+}
+`
+
+var (
+	ListenerWeb = Listener{
+		ID:                     "db902c0c-d5ff-4753-b465-668ad9656918",
+		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
+		Name:                   "web",
+		Description:            "listener config for the web tier",
+		Loadbalancers:          []LoadBalancerID{{ID: "53306cda-815d-4354-9444-59e09da9c3c5"}},
+		Protocol:               "HTTP",
+		ProtocolPort:           80,
+		DefaultPoolID:          "fad389a3-9a4a-4762-a365-8c7038508b5d",
+		AdminStateUp:           true,
+		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
+		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
+	}
+	ListenerDb = Listener{
+		ID:                     "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
+		Name:                   "db",
+		Description:            "listener config for the db tier",
+		Loadbalancers:          []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
+		Protocol:               "TCP",
+		ProtocolPort:           3306,
+		DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
+		ConnLimit:              2000,
+		AdminStateUp:           true,
+		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
+		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
+	}
+	ListenerUpdated = Listener{
+		ID:                     "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
+		Name:                   "NewListenerName",
+		Description:            "listener config for the db tier",
+		Loadbalancers:          []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
+		Protocol:               "TCP",
+		ProtocolPort:           3306,
+		DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
+		ConnLimit:              1000,
+		AdminStateUp:           true,
+		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
+		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
+	}
+)
+
+// HandleListenerListSuccessfully sets up the test server to respond to a listener List request.
+func HandleListenerListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, ListenersListBody)
+		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
+			fmt.Fprintf(w, `{ "listeners": [] }`)
+		default:
+			t.Fatalf("/v2.0/lbaas/listeners invoked with unexpected marker=[%s]", marker)
+		}
+	})
+}
+
+// HandleListenerCreationSuccessfully sets up the test server to respond to a listener creation request
+// with a given response.
+func HandleListenerCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			    "listener": {
+			        "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab",
+			        "protocol": "TCP",
+			        "name": "db",
+			        "admin_state_up": true,
+			        "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
+			        "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
+			        "protocol_port": 3306
+			    }
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandleListenerGetSuccessfully sets up the test server to respond to a listener Get request.
+func HandleListenerGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleListenerBody)
+	})
+}
+
+// HandleListenerDeletionSuccessfully sets up the test server to respond to a listener deletion request.
+func HandleListenerDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleListenerUpdateSuccessfully sets up the test server to respond to a listener Update request.
+func HandleListenerUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `{
+			"listener": {
+				"name": "NewListenerName",
+				"connection_limit": 1001
+			}
+		}`)
+
+		fmt.Fprintf(w, PostUpdateListenerBody)
+	})
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go
index e903244..a62e0cd 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go
@@ -11,62 +11,9 @@
 // operations. It is recommended that users use the `Up` and `Down` enums.
 type AdminState *bool
 
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
-)
-
-// ListOpts allows the filtering and sorting of paginated collections through
-// the API. Filtering is achieved by passing in struct field values that map to
-// the floating IP attributes you want to see returned. SortKey allows you to
-// sort by a particular network attribute. SortDir sets the direction, and is
-// either `asc' or `desc'. Marker and Limit are used for pagination.
-type ListOpts struct {
-	ID              string `q:"id"`
-	Name            string `q:"name"`
-	AdminStateUp    *bool  `q:"admin_state_up"`
-	TenantID        string `q:"tenant_id"`
-	DefaultPoolID   string `q:"default_pool_id"`
-	Protocol        string `q:"protocol"`
-	ProtocolPort    int    `q:"protocol_port"`
-	ConnectionLimit int    `q:"connection_limit"`
-	Limit           int    `q:"limit"`
-	Marker          string `q:"marker"`
-	SortKey         string `q:"sort_key"`
-	SortDir         string `q:"sort_dir"`
-}
-
-// List returns a Pager which allows you to iterate over a collection of
-// routers. It accepts a ListOpts struct, which allows you to filter and sort
-// the returned collection for greater efficiency.
-//
-// Default policy settings return only those routers that are owned by the
-// tenant who submits the request, unless an admin user submits the request.
-func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
-	q, err := gophercloud.BuildQueryString(&opts)
-	if err != nil {
-		return pagination.Pager{Err: err}
-	}
-	u := rootURL(c) + q.String()
-	return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
-		return ListenerPage{pagination.LinkedPageBase{PageResult: r}}
-	})
-}
-
-var (
-	errLoadbalancerIdRequired = fmt.Errorf("Loadbalancer ID is required")
-	errProtocolRequired       = fmt.Errorf("Protocol is required")
-	errProtocolPortRequired   = fmt.Errorf("Protocol port is required")
-)
-
-// CreateOpts contains all the values needed to create a new Listener.
-type CreateOpts struct {
+type listenerOpts struct {
 	// Required. The protocol - can either be TCP, HTTP or HTTPS.
-	Protocol string
+	Protocol Protocol
 
 	// Required. The port on which to listen for client traffic.
 	ProtocolPort int
@@ -100,6 +47,146 @@
 	AdminStateUp *bool
 }
 
+// Convenience vars for AdminStateUp values.
+var (
+	iTrue  = true
+	iFalse = false
+
+	Up   AdminState = &iTrue
+	Down AdminState = &iFalse
+)
+
+// ListOptsBuilder allows extensions to add additional parameters to the
+// List request.
+type ListOptsBuilder interface {
+	ToListenerListQuery() (string, error)
+}
+
+// ListOpts allows the filtering and sorting of paginated collections through
+// the API. Filtering is achieved by passing in struct field values that map to
+// the floating IP attributes you want to see returned. SortKey allows you to
+// sort by a particular listener attribute. SortDir sets the direction, and is
+// either `asc' or `desc'. Marker and Limit are used for pagination.
+type ListOpts struct {
+	ID              string `q:"id"`
+	Name            string `q:"name"`
+	AdminStateUp    *bool  `q:"admin_state_up"`
+	TenantID        string `q:"tenant_id"`
+	LoadbalancerID  string `q:"loadbalancer_id"`
+	DefaultPoolID   string `q:"default_pool_id"`
+	Protocol        string `q:"protocol"`
+	ProtocolPort    int    `q:"protocol_port"`
+	ConnectionLimit int    `q:"connection_limit"`
+	Limit           int    `q:"limit"`
+	Marker          string `q:"marker"`
+	SortKey         string `q:"sort_key"`
+	SortDir         string `q:"sort_dir"`
+}
+
+// ToListenerListQuery formats a ListOpts into a query string.
+func (opts ListOpts) ToListenerListQuery() (string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return "", err
+	}
+	return q.String(), nil
+}
+
+// List returns a Pager which allows you to iterate over a collection of
+// routers. It accepts a ListOpts struct, which allows you to filter and sort
+// the returned collection for greater efficiency.
+//
+// Default policy settings return only those routers that are owned by the
+// tenant who submits the request, unless an admin user submits the request.
+func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
+	url := rootURL(c)
+	if opts != nil {
+		query, err := opts.ToListenerListQuery()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		url += query
+	}
+
+	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
+		return ListenerPage{pagination.LinkedPageBase{PageResult: r}}
+	})
+}
+
+type Protocol string
+
+// Supported attributes for create/update operations.
+const (
+	ProtocolTCP   Protocol = "TCP"
+	ProtocolHTTP  Protocol = "HTTP"
+	ProtocolHTTPS Protocol = "HTTPS"
+)
+
+var (
+	errLoadbalancerIdRequired = fmt.Errorf("LoadbalancerID is required")
+	errProtocolRequired       = fmt.Errorf("Protocol is required")
+	errProtocolPortRequired   = fmt.Errorf("ProtocolPort  is required")
+)
+
+// CreateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the main Create operation in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type CreateOptsBuilder interface {
+	ToListenerCreateMap() (map[string]interface{}, error)
+}
+
+// CreateOpts is the common options struct used in this package's Create
+// operation.
+type CreateOpts listenerOpts
+
+// ToListenerCreateMap casts a CreateOpts struct to a map.
+func (opts CreateOpts) ToListenerCreateMap() (map[string]interface{}, error) {
+	l := make(map[string]interface{})
+
+	if opts.LoadbalancerID != "" {
+		l["loadbalancer_id"] = opts.LoadbalancerID
+	} else {
+		return nil, errLoadbalancerIdRequired
+	}
+	if opts.Protocol != "" {
+		l["protocol"] = opts.Protocol
+	} else {
+		return nil, errProtocolRequired
+	}
+	if opts.ProtocolPort != 0 {
+		l["protocol_port"] = opts.ProtocolPort
+	} else {
+		return nil, errProtocolPortRequired
+	}
+	if opts.AdminStateUp != nil {
+		l["admin_state_up"] = &opts.AdminStateUp
+	}
+	if opts.Name != "" {
+		l["name"] = opts.Name
+	}
+	if opts.TenantID != "" {
+		l["tenant_id"] = opts.TenantID
+	}
+	if opts.DefaultPoolID != "" {
+		l["default_pool_id"] = opts.DefaultPoolID
+	}
+	if opts.Description != "" {
+		l["description"] = opts.Description
+	}
+	if opts.ConnLimit != nil {
+		l["connection_limit"] = &opts.ConnLimit
+	}
+	if opts.DefaultTlsContainerRef != "" {
+		l["default_tls_container_ref"] = opts.DefaultTlsContainerRef
+	}
+	if opts.SniContainerRefs != nil {
+		l["sni_container_refs"] = opts.SniContainerRefs
+	}
+
+	return map[string]interface{}{"listener": l}, nil
+}
+
 // Create is an operation which provisions a new Listeners based on the
 // configuration defined in the CreateOpts struct. Once the request is
 // validated and progress has started on the provisioning process, a
@@ -110,52 +197,13 @@
 func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 	var res CreateResult
 
-	// Validate required opts
-	if opts.LoadbalancerID == "" {
-		res.Err = errLoadbalancerIdRequired
-		return res
-	}
-	if opts.Protocol == "" {
-		res.Err = errProtocolRequired
-		return res
-	}
-	if opts.ProtocolPort == 0 {
-		res.Err = errProtocolPortRequired
+	reqBody, err := opts.ToListenerCreateMap()
+	if err != nil {
+		res.Err = err
 		return res
 	}
 
-	type listener struct {
-		Name                   *string  `json:"name,omitempty"`
-		LoadbalancerID         string   `json:"loadbalancer_id,omitempty"`
-		Protocol               string   `json:"protocol"`
-		ProtocolPort           int      `json:"protocol_port"`
-		DefaultPoolID          *string  `json:"default_pool_id,omitempty"`
-		Description            *string  `json:"description,omitempty"`
-		TenantID               *string  `json:"tenant_id,omitempty"`
-		ConnLimit              *int     `json:"connection_limit,omitempty"`
-		AdminStateUp           *bool    `json:"admin_state_up,omitempty"`
-		DefaultTlsContainerRef *string  `json:"default_tls_container_ref,omitempty"`
-		SniContainerRefs       []string `json:"sni_container_refs,omitempty"`
-	}
-
-	type request struct {
-		Listener listener `json:"listener"`
-	}
-
-	reqBody := request{Listener: listener{
-		Name:                   gophercloud.MaybeString(opts.Name),
-		LoadbalancerID:         opts.LoadbalancerID,
-		Protocol:               opts.Protocol,
-		ProtocolPort:           opts.ProtocolPort,
-		DefaultPoolID:          gophercloud.MaybeString(opts.DefaultPoolID),
-		Description:            gophercloud.MaybeString(opts.Description),
-		TenantID:               gophercloud.MaybeString(opts.TenantID),
-		ConnLimit:              opts.ConnLimit,
-		AdminStateUp:           opts.AdminStateUp,
-		DefaultTlsContainerRef: gophercloud.MaybeString(opts.DefaultTlsContainerRef),
-		SniContainerRefs:       opts.SniContainerRefs,
-	}}
-
+	// Send request to API
 	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 	return res
 }
@@ -167,55 +215,55 @@
 	return res
 }
 
-// UpdateOpts contains all the values that can be updated on an existing Listener.
-// Attributes not listed here but appear in CreateOpts are immutable and cannot
-// be updated.
-type UpdateOpts struct {
-	// Human-readable name for the Listener. Does not have to be unique.
-	Name string
+// UpdateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the main Update operation in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type UpdateOptsBuilder interface {
+	ToListenerUpdateMap() (map[string]interface{}, error)
+}
 
-	// Optional. Human-readable description for the Listener.
-	Description string
+// UpdateOpts is the common options struct used in this package's Update
+// operation.
+type UpdateOpts listenerOpts
 
-	// Optional. The maximum number of connections allowed for the Listener.
-	ConnLimit *int
+// ToListenerUpdateMap casts a UpdateOpts struct to a map.
+func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) {
+	l := make(map[string]interface{})
 
-	// Optional. A reference to a container of TLS secrets.
-	DefaultTlsContainerRef string
+	if opts.Name != "" {
+		l["name"] = opts.Name
+	}
+	if opts.Description != "" {
+		l["description"] = opts.Description
+	}
+	if opts.ConnLimit != nil {
+		l["connection_limit"] = &opts.ConnLimit
+	}
+	if opts.DefaultTlsContainerRef != "" {
+		l["default_tls_container_ref"] = opts.DefaultTlsContainerRef
+	}
+	if opts.SniContainerRefs != nil {
+		l["sni_container_refs"] = opts.SniContainerRefs
+	}
+	if opts.AdminStateUp != nil {
+		l["admin_state_up"] = &opts.AdminStateUp
+	}
 
-	// Optional. A list of references to TLS secrets.
-	SniContainerRefs []string
-
-	// Optional. The administrative state of the Listener. A valid value is true (UP)
-	// or false (DOWN).
-	AdminStateUp *bool
+	return map[string]interface{}{"listener": l}, nil
 }
 
 // Update is an operation which modifies the attributes of the specified Listener.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type listener struct {
-		Name                   string   `json:"name,omitempty"`
-		Description            *string  `json:"description,omitempty"`
-		ConnLimit              *int     `json:"connection_limit,omitempty"`
-		AdminStateUp           *bool    `json:"admin_state_up,omitempty"`
-		DefaultTlsContainerRef *string  `json:"default_tls_container_ref,omitempty"`
-		SniContainerRefs       []string `json:"sni_container_refs,omitempty"`
-	}
-
-	type request struct {
-		Listener listener `json:"listener"`
-	}
-
-	reqBody := request{Listener: listener{
-		Name:                   opts.Name,
-		Description:            gophercloud.MaybeString(opts.Description),
-		ConnLimit:              opts.ConnLimit,
-		AdminStateUp:           opts.AdminStateUp,
-		DefaultTlsContainerRef: gophercloud.MaybeString(opts.DefaultTlsContainerRef),
-		SniContainerRefs:       opts.SniContainerRefs,
-	}}
-
 	var res UpdateResult
+
+	reqBody, err := opts.ToListenerUpdateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	// Send request to API
 	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests_test.go
index dc14755..4ce3114 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests_test.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests_test.go
@@ -1,13 +1,10 @@
 package listeners
 
 import (
-	"fmt"
-	"net/http"
-	"testing"
-	//"github.com/davecgh/go-spew/spew"
 	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
 	"github.com/rackspace/gophercloud/pagination"
 	th "github.com/rackspace/gophercloud/testhelper"
+	"testing"
 )
 
 func TestURLs(t *testing.T) {
@@ -18,171 +15,66 @@
 	th.AssertEquals(t, th.Endpoint()+"v2.0/lbaas/listeners/foo", resourceURL(fake.ServiceClient(), "foo"))
 }
 
-func TestList(t *testing.T) {
+func TestListListeners(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandleListenerListSuccessfully(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+	pages := 0
+	err := List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
 
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "listeners":[
-         {
-           "id": "db902c0c-d5ff-4753-b465-668ad9656918",
-           "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
-           "name": "web_listener",
-           "description": "listener config for the web tier",
-           "loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}],
-           "protocol": "HTTP",
-           "protocol_port": 80,
-           "default_pool_id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
-           "admin_state_up": true,
-           "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
-           "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
-         },
-         {
-           "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-           "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
-           "name": "db_listener",
-	   "description": "listener config for the db tier",
-           "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
-           "protocol": "TCP",
-           "protocol_port": 3306,
-           "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
-           "connection_limit": 2000,
-           "admin_state_up": true,
-           "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
-           "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
-         }
-      ]
-}
-			`)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
 		actual, err := ExtractListeners(page)
 		if err != nil {
-			t.Errorf("Failed to extract LBs: %v", err)
 			return false, err
 		}
 
-		expected := []Listener{
-			{
-				ID:                     "db902c0c-d5ff-4753-b465-668ad9656918",
-				TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
-				Name:                   "web_listener",
-				Description:            "listener config for the web tier",
-				Loadbalancers:          []map[string]interface{}{{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}},
-				Protocol:               "HTTP",
-				ProtocolPort:           80,
-				DefaultPoolID:          "fad389a3-9a4a-4762-a365-8c7038508b5d",
-				AdminStateUp:           true,
-				DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
-				SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
-			},
-			{
-				ID:                     "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-				TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
-				Name:                   "db_listener",
-				Description:            "listener config for the db tier",
-				Loadbalancers:          []map[string]interface{}{{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}},
-				Protocol:               "TCP",
-				ProtocolPort:           3306,
-				DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
-				ConnLimit:              2000,
-				AdminStateUp:           true,
-				DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
-				SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
-			},
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 listeners, got %d", len(actual))
 		}
-
-		th.CheckDeepEquals(t, expected, actual)
+		th.CheckDeepEquals(t, ListenerWeb, actual[0])
+		th.CheckDeepEquals(t, ListenerDb, actual[1])
 
 		return true, nil
 	})
 
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "listener": {
-        "loadbalancer_id": "53306cda-815d-4354-9444-59e09da9c3c5",
-        "protocol": "HTTP",
-        "name": "NewListener",
-        "admin_state_up": true,
-        "default_tls_container_ref": "8032909d-47a1-4715-90af-5153ffe39861",
-        "default_pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
-        "protocol_port": 80
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "listener": {
-        "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-	"tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
-	"name": "NewListener",
-	"description": "",
-	"loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}],
-	"protocol": "HTTP",
-	"protocol_port": 80,
-	"connection_limit": -1,
-	"default_pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
-	"admin_state_up": true,
-	"default_tls_container_ref": "8032909d-47a1-4715-90af-5153ffe39861"
-    }
-}
-		`)
-	})
-
-	opts := CreateOpts{
-		Protocol:               "HTTP",
-		Name:                   "NewListener",
-		LoadbalancerID:         "53306cda-815d-4354-9444-59e09da9c3c5",
-		AdminStateUp:           Up,
-		DefaultTlsContainerRef: "8032909d-47a1-4715-90af-5153ffe39861",
-		DefaultPoolID:          "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
-		ProtocolPort:           80,
-	}
-
-	r, err := Create(fake.ServiceClient(), opts).Extract()
 	th.AssertNoErr(t, err)
 
-	th.AssertEquals(t, "HTTP", r.Protocol)
-	th.AssertEquals(t, "", r.Description)
-	th.AssertEquals(t, true, r.AdminStateUp)
-	th.AssertEquals(t, "8032909d-47a1-4715-90af-5153ffe39861", r.DefaultTlsContainerRef)
-	th.AssertEquals(t, "83657cfcdfe44cd5920adaf26c48ceea", r.TenantID)
-	th.AssertEquals(t, -1, r.ConnLimit)
-	th.AssertEquals(t, "61b1f87a-7a21-4ad3-9dda-7f81d249944f", r.DefaultPoolID)
-	th.AssertEquals(t, 80, r.ProtocolPort)
-	th.AssertEquals(t, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", r.ID)
-	th.AssertEquals(t, "53306cda-815d-4354-9444-59e09da9c3c5", r.Loadbalancers[0]["id"])
-	th.AssertEquals(t, "NewListener", r.Name)
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
+	}
+}
+
+func TestListAllListeners(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListenerListSuccessfully(t)
+
+	allPages, err := List(fake.ServiceClient(), ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := ExtractListeners(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, ListenerWeb, actual[0])
+	th.CheckDeepEquals(t, ListenerDb, actual[1])
+}
+
+func TestCreateListener(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListenerCreationSuccessfully(t, SingleListenerBody)
+
+	actual, err := Create(fake.ServiceClient(), CreateOpts{
+		Protocol:               "TCP",
+		Name:                   "db",
+		LoadbalancerID:         "79e05663-7f03-45d2-a092-8b94062f22ab",
+		AdminStateUp:           Up,
+		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
+		DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
+		ProtocolPort:           3306,
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, ListenerDb, *actual)
 }
 
 func TestRequiredCreateOpts(t *testing.T) {
@@ -208,115 +100,43 @@
 	}
 }
 
-func TestGet(t *testing.T) {
+func TestGetListener(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandleListenerGetSuccessfully(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-    "listener": {
-        "id": "4ec89087-d057-4e2c-911f-60a3b47ee304",
-	"tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
-	"name": "NewListener",
-	"description": "",
-	"loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}],
-	"protocol": "HTTP",
-	"protocol_port": 80,
-	"connection_limit": -1,
-	"default_pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
-	"admin_state_up": true,
-	"default_tls_container_ref": "8032909d-47a1-4715-90af-5153ffe39861"
-    }
-}
-			`)
-	})
-
-	l, err := Get(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "HTTP", l.Protocol)
-	th.AssertEquals(t, "", l.Description)
-	th.AssertEquals(t, true, l.AdminStateUp)
-	th.AssertEquals(t, "8032909d-47a1-4715-90af-5153ffe39861", l.DefaultTlsContainerRef)
-	th.AssertEquals(t, "83657cfcdfe44cd5920adaf26c48ceea", l.TenantID)
-	th.AssertEquals(t, -1, l.ConnLimit)
-	th.AssertEquals(t, "61b1f87a-7a21-4ad3-9dda-7f81d249944f", l.DefaultPoolID)
-	th.AssertEquals(t, 80, l.ProtocolPort)
-	th.AssertEquals(t, "4ec89087-d057-4e2c-911f-60a3b47ee304", l.ID)
-	th.AssertEquals(t, "53306cda-815d-4354-9444-59e09da9c3c5", l.Loadbalancers[0]["id"])
-	th.AssertEquals(t, "NewListener", l.Name)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "listener": {
-        "name": "NewListenerName",
-        "connection_limit": 1001
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-
-		fmt.Fprintf(w, `
-{
-    "listener": {
-        "id": "4ec89087-d057-4e2c-911f-60a3b47ee304",
-	"tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
-	"name": "NewListenerName",
-	"description": "",
-	"loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}],
-	"protocol": "HTTP",
-	"protocol_port": 80,
-	"connection_limit": 1001,
-	"default_pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
-	"admin_state_up": true,
-	"default_tls_container_ref": "8032909d-47a1-4715-90af-5153ffe39861"
-    }
-}
-		`)
-	})
-
-	i1001 := 1001
-	options := UpdateOpts{
-		Name:      "NewListenerName",
-		ConnLimit: &i1001,
+	client := fake.ServiceClient()
+	actual, err := Get(client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
 	}
 
-	l, err := Update(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, *(options.ConnLimit), l.ConnLimit)
-	th.AssertEquals(t, options.Name, l.Name)
+	th.CheckDeepEquals(t, ListenerDb, *actual)
 }
 
-func TestDelete(t *testing.T) {
+func TestDeleteListener(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
+	HandleListenerDeletionSuccessfully(t)
 
 	res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
 	th.AssertNoErr(t, res.Err)
 }
+
+func TestUpdateListener(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListenerUpdateSuccessfully(t)
+
+	client := fake.ServiceClient()
+	i1001 := 1001
+	actual, err := Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", UpdateOpts{
+		Name:      "NewListenerName",
+		ConnLimit: &i1001,
+	}).Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Update error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, ListenerUpdated, *actual)
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go
index 76b374c..77a084a 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go
@@ -3,10 +3,14 @@
 import (
 	"github.com/mitchellh/mapstructure"
 	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
 	"github.com/rackspace/gophercloud/pagination"
-	// "github.com/davecgh/go-spew/spew"
 )
 
+type LoadBalancerID struct {
+	ID string `mapstructure:"id" json:"id"`
+}
+
 // Listener is the primary load balancing configuration object that specifies
 // the loadbalancer and port on which client traffic is received, as well
 // as other details such as the load balancing method to be use, protocol, etc.
@@ -33,8 +37,8 @@
 	// The UUID of default pool. Must have compatible protocol with listener.
 	DefaultPoolID string `mapstructure:"default_pool_id" json:"default_pool_id"`
 
-	// A list of load balancer objects IDs.
-	Loadbalancers []map[string]interface{} `mapstructure:"loadbalancers" json:"loadbalancers"`
+	// A list of load balancer IDs.
+	Loadbalancers []LoadBalancerID `mapstructure:"loadbalancers" json:"loadbalancers"`
 
 	// The maximum number of connections allowed for the Loadbalancer. Default is -1,
 	// meaning no limit.
@@ -48,6 +52,8 @@
 
 	// The administrative state of the Listener. A valid value is true (UP) or false (DOWN).
 	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+
+	Pools []pools.Pool `mapstructure:"pools" json:"pools"`
 }
 
 // ListenerPage is the page returned by a pager when traversing over a
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/fixtures.go
new file mode 100644
index 0000000..da0d21a
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/fixtures.go
@@ -0,0 +1,277 @@
+// +build fixtures
+package loadbalancers
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/rackspace/gophercloud/testhelper/client"
+
+	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
+	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
+	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
+)
+
+// LoadbalancersListBody contains the canned body of a loadbalancer list response.
+const LoadbalancersListBody = `
+{
+	"loadbalancers":[
+	         {
+			"id": "c331058c-6a40-4144-948e-b9fb1df9db4b",
+			"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
+			"name": "web_lb",
+			"description": "lb config for the web tier",
+			"vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154",
+			"vip_address": "10.30.176.47",
+			"flavor": "small",
+			"provider": "haproxy",
+			"admin_state_up": true,
+			"provisioning_status": "ACTIVE",
+			"operating_status": "ONLINE"
+		},
+		{
+			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+			"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
+			"name": "db_lb",
+			"description": "lb config for the db tier",
+			"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+			"vip_address": "10.30.176.48",
+			"flavor": "medium",
+			"provider": "haproxy",
+			"admin_state_up": true,
+			"provisioning_status": "PENDING_CREATE",
+			"operating_status": "OFFLINE"
+		}
+	]
+}
+`
+
+// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer.
+const SingleLoadbalancerBody = `
+{
+	"loadbalancer": {
+		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
+		"name": "db_lb",
+		"description": "lb config for the db tier",
+		"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+		"vip_address": "10.30.176.48",
+		"flavor": "medium",
+		"provider": "haproxy",
+		"admin_state_up": true,
+		"provisioning_status": "PENDING_CREATE",
+		"operating_status": "OFFLINE"
+	}
+}
+`
+
+// PostUpdateLoadbalancerBody is the canned response body of a Update request on an existing loadbalancer.
+const PostUpdateLoadbalancerBody = `
+{
+	"loadbalancer": {
+		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
+		"name": "NewLoadbalancerName",
+		"description": "lb config for the db tier",
+		"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+		"vip_address": "10.30.176.48",
+		"flavor": "medium",
+		"provider": "haproxy",
+		"admin_state_up": true,
+		"provisioning_status": "PENDING_CREATE",
+		"operating_status": "OFFLINE"
+	}
+}
+`
+
+// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer.
+const LoadbalancerStatuesesTree = `
+{
+	"statuses" : {
+		"loadbalancer": {
+			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+			"name": "db_lb",
+			"provisioning_status": "PENDING_UPDATE",
+			"operating_status": "ACTIVE",
+			"listeners": [{
+				"id": "db902c0c-d5ff-4753-b465-668ad9656918",
+				"name": "db",
+				"pools": [{
+					"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
+					"name": "db",
+					"healthmonitor": {
+						"id": "67306cda-815d-4354-9fe4-59e09da9c3c5",
+						"type":"PING"
+					},
+					"members":[{
+						"id": "2a280670-c202-4b0b-a562-34077415aabf",
+						"name": "db",
+						"address": "10.0.2.11",
+						"protocol_port": 80
+					}]
+				}]
+			}]
+		}
+	}
+}
+`
+
+var (
+	LoadbalancerWeb = LoadBalancer{
+		ID:                 "c331058c-6a40-4144-948e-b9fb1df9db4b",
+		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
+		Name:               "web_lb",
+		Description:        "lb config for the web tier",
+		VipSubnetID:        "8a49c438-848f-467b-9655-ea1548708154",
+		VipAddress:         "10.30.176.47",
+		Flavor:             "small",
+		Provider:           "haproxy",
+		AdminStateUp:       true,
+		ProvisioningStatus: "ACTIVE",
+		OperatingStatus:    "ONLINE",
+	}
+	LoadbalancerDb = LoadBalancer{
+		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
+		Name:               "db_lb",
+		Description:        "lb config for the db tier",
+		VipSubnetID:        "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+		VipAddress:         "10.30.176.48",
+		Flavor:             "medium",
+		Provider:           "haproxy",
+		AdminStateUp:       true,
+		ProvisioningStatus: "PENDING_CREATE",
+		OperatingStatus:    "OFFLINE",
+	}
+	LoadbalancerUpdated = LoadBalancer{
+		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
+		Name:               "NewLoadbalancerName",
+		Description:        "lb config for the db tier",
+		VipSubnetID:        "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+		VipAddress:         "10.30.176.48",
+		Flavor:             "medium",
+		Provider:           "haproxy",
+		AdminStateUp:       true,
+		ProvisioningStatus: "PENDING_CREATE",
+		OperatingStatus:    "OFFLINE",
+	}
+	LoadbalancerStatusesTree = LoadBalancer{
+		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
+		Name:               "db_lb",
+		ProvisioningStatus: "PENDING_UPDATE",
+		OperatingStatus:    "ACTIVE",
+		Listeners: []listeners.Listener{{
+			ID:   "db902c0c-d5ff-4753-b465-668ad9656918",
+			Name: "db",
+			Pools: []pools.Pool{{
+				ID:   "fad389a3-9a4a-4762-a365-8c7038508b5d",
+				Name: "db",
+				Monitor: monitors.Monitor{
+					ID:   "67306cda-815d-4354-9fe4-59e09da9c3c5",
+					Type: "PING",
+				},
+				Members: []pools.Member{{
+					ID:           "2a280670-c202-4b0b-a562-34077415aabf",
+					Name:         "db",
+					Address:      "10.0.2.11",
+					ProtocolPort: 80,
+				}},
+			}},
+		}},
+	}
+)
+
+// HandleLoadbalancerListSuccessfully sets up the test server to respond to a loadbalancer List request.
+func HandleLoadbalancerListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, LoadbalancersListBody)
+		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
+			fmt.Fprintf(w, `{ "loadbalancers": [] }`)
+		default:
+			t.Fatalf("/v2.0/lbaas/loadbalancers invoked with unexpected marker=[%s]", marker)
+		}
+	})
+}
+
+// HandleLoadbalancerCreationSuccessfully sets up the test server to respond to a loadbalancer creation request
+// with a given response.
+func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"loadbalancer": {
+				"name": "db_lb",
+				"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+				"vip_address": "10.30.176.48",
+				"flavor": "medium",
+				"provider": "haproxy",
+				"admin_state_up": true
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandleLoadbalancerGetSuccessfully sets up the test server to respond to a loadbalancer Get request.
+func HandleLoadbalancerGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleLoadbalancerBody)
+	})
+}
+
+// HandleLoadbalancerGetStatusesTree sets up the test server to respond to a loadbalancer Get statuses tree request.
+func HandleLoadbalancerGetStatusesTree(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/statuses", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, LoadbalancerStatuesesTree)
+	})
+}
+
+// HandleLoadbalancerDeletionSuccessfully sets up the test server to respond to a loadbalancer deletion request.
+func HandleLoadbalancerDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleLoadbalancerUpdateSuccessfully sets up the test server to respond to a loadbalancer Update request.
+func HandleLoadbalancerUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `{
+			"loadbalancer": {
+				"name": "NewLoadbalancerName"
+			}
+		}`)
+
+		fmt.Fprintf(w, PostUpdateLoadbalancerBody)
+	})
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go
index d75171d..6607b8a 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go
@@ -11,61 +11,7 @@
 // operations. It is recommended that users use the `Up` and `Down` enums.
 type AdminState *bool
 
-// Convenience vars for AdminStateUp values.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Up   AdminState = &iTrue
-	Down AdminState = &iFalse
-)
-
-// ListOpts allows the filtering and sorting of paginated collections through
-// the API. Filtering is achieved by passing in struct field values that map to
-// the Loadbalancer attributes you want to see returned. SortKey allows you to
-// sort by a particular attribute. SortDir sets the direction, and is
-// either `asc' or `desc'. Marker and Limit are used for pagination.
-type ListOpts struct {
-	Description        string `q:"description"`
-	AdminStateUp       *bool  `q:"admin_state_up"`
-	TenantID           string `q:"tenant_id"`
-	ProvisioningStatus string `q:"provisioning_status"`
-	VipAddress         string `q:"vip_address"`
-	VipSubnetID        string `q:"vip_subnet_id"`
-	ID                 string `q:"id"`
-	OperatingStatus    string `q:"operating_status"`
-	Name               string `q:"name"`
-	Flavor             string `q:"flavor"`
-	Provider           string `q:"provider"`
-	Limit              int    `q:"limit"`
-	Marker             string `q:"marker"`
-	SortKey            string `q:"sort_key"`
-	SortDir            string `q:"sort_dir"`
-}
-
-// List returns a Pager which allows you to iterate over a collection of
-// routers. It accepts a ListOpts struct, which allows you to filter and sort
-// the returned collection for greater efficiency.
-//
-// Default policy settings return only those routers that are owned by the
-// tenant who submits the request, unless an admin user submits the request.
-func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
-	q, err := gophercloud.BuildQueryString(&opts)
-	if err != nil {
-		return pagination.Pager{Err: err}
-	}
-	u := rootURL(c) + q.String()
-	return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
-		return LoadbalancerPage{pagination.LinkedPageBase{PageResult: r}}
-	})
-}
-
-var (
-	errVipSubnetIDRequried = fmt.Errorf("VipSubnetID is required")
-)
-
-// CreateOpts contains all the values needed to create a new Loadbalancer.
-type CreateOpts struct {
+type loadbalancerOpts struct {
 	// Optional. Human-readable name for the Loadbalancer. Does not have to be unique.
 	Name string
 
@@ -95,6 +41,124 @@
 	Provider string
 }
 
+// Convenience vars for AdminStateUp values.
+var (
+	iTrue  = true
+	iFalse = false
+
+	Up   AdminState = &iTrue
+	Down AdminState = &iFalse
+)
+
+// ListOptsBuilder allows extensions to add additional parameters to the
+// List request.
+type ListOptsBuilder interface {
+	ToLoadbalancerListQuery() (string, error)
+}
+
+// ListOpts allows the filtering and sorting of paginated collections through
+// the API. Filtering is achieved by passing in struct field values that map to
+// the Loadbalancer attributes you want to see returned. SortKey allows you to
+// sort by a particular attribute. SortDir sets the direction, and is
+// either `asc' or `desc'. Marker and Limit are used for pagination.
+type ListOpts struct {
+	Description        string `q:"description"`
+	AdminStateUp       *bool  `q:"admin_state_up"`
+	TenantID           string `q:"tenant_id"`
+	ProvisioningStatus string `q:"provisioning_status"`
+	VipAddress         string `q:"vip_address"`
+	VipSubnetID        string `q:"vip_subnet_id"`
+	ID                 string `q:"id"`
+	OperatingStatus    string `q:"operating_status"`
+	Name               string `q:"name"`
+	Flavor             string `q:"flavor"`
+	Provider           string `q:"provider"`
+	Limit              int    `q:"limit"`
+	Marker             string `q:"marker"`
+	SortKey            string `q:"sort_key"`
+	SortDir            string `q:"sort_dir"`
+}
+
+// ToLoadbalancerListQuery formats a ListOpts into a query string.
+func (opts ListOpts) ToLoadbalancerListQuery() (string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return "", err
+	}
+	return q.String(), nil
+}
+
+// List returns a Pager which allows you to iterate over a collection of
+// routers. It accepts a ListOpts struct, which allows you to filter and sort
+// the returned collection for greater efficiency.
+//
+// Default policy settings return only those routers that are owned by the
+// tenant who submits the request, unless an admin user submits the request.
+func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
+	url := rootURL(c)
+	if opts != nil {
+		query, err := opts.ToLoadbalancerListQuery()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		url += query
+	}
+
+	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
+		return LoadbalancerPage{pagination.LinkedPageBase{PageResult: r}}
+	})
+}
+
+var (
+	errVipSubnetIDRequried = fmt.Errorf("VipSubnetID is required")
+)
+
+// CreateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the main Create operation in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type CreateOptsBuilder interface {
+	ToLoadbalancerCreateMap() (map[string]interface{}, error)
+}
+
+// CreateOpts is the common options struct used in this package's Create
+// operation.
+type CreateOpts loadbalancerOpts
+
+// ToLoadbalancerCreateMap casts a CreateOpts struct to a map.
+func (opts CreateOpts) ToLoadbalancerCreateMap() (map[string]interface{}, error) {
+	l := make(map[string]interface{})
+
+	if opts.VipSubnetID != "" {
+		l["vip_subnet_id"] = opts.VipSubnetID
+	} else {
+		return nil, errVipSubnetIDRequried
+	}
+	if opts.AdminStateUp != nil {
+		l["admin_state_up"] = &opts.AdminStateUp
+	}
+	if opts.Name != "" {
+		l["name"] = opts.Name
+	}
+	if opts.TenantID != "" {
+		l["tenant_id"] = opts.TenantID
+	}
+	if opts.Description != "" {
+		l["description"] = opts.Description
+	}
+	if opts.VipAddress != "" {
+		l["vip_address"] = opts.VipAddress
+	}
+	if opts.Flavor != "" {
+		l["flavor"] = opts.Flavor
+	}
+	if opts.Provider != "" {
+		l["provider"] = opts.Provider
+	}
+
+	return map[string]interface{}{"loadbalancer": l}, nil
+}
+
 // Create is an operation which provisions a new loadbalancer based on the
 // configuration defined in the CreateOpts struct. Once the request is
 // validated and progress has started on the provisioning process, a
@@ -105,83 +169,64 @@
 func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 	var res CreateResult
 
-	// Validate required opts
-	if opts.VipSubnetID == "" {
-		res.Err = errVipSubnetIDRequried
+	reqBody, err := opts.ToLoadbalancerCreateMap()
+	if err != nil {
+		res.Err = err
 		return res
 	}
 
-	type loadbalancer struct {
-		Name         *string `json:"name,omitempty"`
-		Description  *string `json:"description,omitempty"`
-		VipSubnetID  string  `json:"vip_subnet_id"`
-		TenantID     *string `json:"tenant_id,omitempty"`
-		VipAddress   *string `json:"vip_address,omitempty"`
-		AdminStateUp *bool   `json:"admin_state_up,omitempty"`
-		Flavor       *string `json:"flavor,omitempty"`
-		Provider     *string `json:"provider,omitempty"`
-	}
-
-	type request struct {
-		Loadbalancer loadbalancer `json:"loadbalancer"`
-	}
-
-	reqBody := request{Loadbalancer: loadbalancer{
-		Name:         gophercloud.MaybeString(opts.Name),
-		Description:  gophercloud.MaybeString(opts.Description),
-		VipSubnetID:  opts.VipSubnetID,
-		TenantID:     gophercloud.MaybeString(opts.TenantID),
-		VipAddress:   gophercloud.MaybeString(opts.VipAddress),
-		AdminStateUp: opts.AdminStateUp,
-		Flavor:       gophercloud.MaybeString(opts.Flavor),
-		Provider:     gophercloud.MaybeString(opts.Provider),
-	}}
-
+	// Send request to API
 	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 	return res
 }
 
-// Get retrieves a particular virtual IP based on its unique ID.
+// Get retrieves a particular Loadbalancer based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
 	var res GetResult
 	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 	return res
 }
 
-// UpdateOpts contains all the values needed to update an existing virtual
-// Loadbalancer. Attributes not listed here but appear in CreateOpts are
-// immutable and cannot be updated.
-type UpdateOpts struct {
-	// Optional. Human-readable name for the Loadbalancer. Does not have to be unique.
-	Name string
+// UpdateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the main Update operation in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type UpdateOptsBuilder interface {
+	ToLoadbalancerUpdateMap() (map[string]interface{}, error)
+}
 
-	// Optional. Human-readable description for the Loadbalancer.
-	Description string
+// UpdateOpts is the common options struct used in this package's Update
+// operation.
+type UpdateOpts loadbalancerOpts
 
-	// Optional. The administrative state of the Loadbalancer. A valid value is true (UP)
-	// or false (DOWN).
-	AdminStateUp *bool
+// ToLoadbalancerUpdateMap casts a UpdateOpts struct to a map.
+func (opts UpdateOpts) ToLoadbalancerUpdateMap() (map[string]interface{}, error) {
+	l := make(map[string]interface{})
+
+	if opts.Name != "" {
+		l["name"] = opts.Name
+	}
+	if opts.Description != "" {
+		l["description"] = opts.Description
+	}
+	if opts.AdminStateUp != nil {
+		l["admin_state_up"] = &opts.AdminStateUp
+	}
+
+	return map[string]interface{}{"loadbalancer": l}, nil
 }
 
 // Update is an operation which modifies the attributes of the specified Loadbalancer.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type loadbalancer struct {
-		Name         *string `json:"name,omitempty"`
-		Description  *string `json:"description,omitempty"`
-		AdminStateUp *bool   `json:"admin_state_up,omitempty"`
-	}
-
-	type request struct {
-		Loadbalancer loadbalancer `json:"loadbalancer"`
-	}
-
-	reqBody := request{Loadbalancer: loadbalancer{
-		Name:         gophercloud.MaybeString(opts.Name),
-		Description:  gophercloud.MaybeString(opts.Description),
-		AdminStateUp: opts.AdminStateUp,
-	}}
-
 	var res UpdateResult
+
+	reqBody, err := opts.ToLoadbalancerUpdateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	// Send request to API
 	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
@@ -195,3 +240,9 @@
 	_, res.Err = c.Delete(resourceURL(c, id), nil)
 	return res
 }
+
+func GetStatuses(c *gophercloud.ServiceClient, id string) GetResult {
+	var res GetResult
+	_, res.Err = c.Get(statusRootURL(c, id), &res.Body, nil)
+	return res
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests_test.go
index 3ff1996..4bc1f1f 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests_test.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests_test.go
@@ -1,8 +1,6 @@
 package loadbalancers
 
 import (
-	"fmt"
-	"net/http"
 	"testing"
 
 	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
@@ -18,166 +16,65 @@
 	th.AssertEquals(t, th.Endpoint()+"v2.0/lbaas/loadbalancers/foo", resourceURL(fake.ServiceClient(), "foo"))
 }
 
-func TestList(t *testing.T) {
+func TestListLoadbalancers(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandleLoadbalancerListSuccessfully(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+	pages := 0
+	err := List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
 
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-  "loadbalancers":[
-         {
-           "id": "c331058c-6a40-4144-948e-b9fb1df9db4b",
-           "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
-           "name": "web_lb",
-           "description": "lb config for the web tier",
-           "vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154",
-           "vip_address": "10.30.176.47",
-           "flavor": "small",
-           "provider": "provider_1",
-           "admin_state_up": true,
-           "provisioning_status": "ACTIVE",
-           "operating_status": "ONLINE"
-         },
-         {
-           "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-           "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
-           "name": "db_lb",
-	   "description": "lb config for the db tier",
-           "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
-           "vip_address": "10.30.176.48",
-           "flavor": "medium",
-           "provider": "provider_2",
-           "admin_state_up": true,
-           "provisioning_status": "PENDING_CREATE",
-           "operating_status": "OFFLINE"
-         }
-      ]
-}
-			`)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
 		actual, err := ExtractLoadbalancers(page)
 		if err != nil {
-			t.Errorf("Failed to extract LBs: %v", err)
 			return false, err
 		}
 
-		expected := []LoadBalancer{
-			{
-				ID:                 "c331058c-6a40-4144-948e-b9fb1df9db4b",
-				TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
-				Name:               "web_lb",
-				Description:        "lb config for the web tier",
-				VipSubnetID:        "8a49c438-848f-467b-9655-ea1548708154",
-				VipAddress:         "10.30.176.47",
-				Flavor:             "small",
-				Provider:           "provider_1",
-				AdminStateUp:       true,
-				ProvisioningStatus: "ACTIVE",
-				OperatingStatus:    "ONLINE",
-			},
-			{
-				ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
-				TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
-				Name:               "db_lb",
-				Description:        "lb config for the db tier",
-				VipSubnetID:        "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
-				VipAddress:         "10.30.176.48",
-				Flavor:             "medium",
-				Provider:           "provider_2",
-				AdminStateUp:       true,
-				ProvisioningStatus: "PENDING_CREATE",
-				OperatingStatus:    "OFFLINE",
-			},
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 loadbalancers, got %d", len(actual))
 		}
-
-		th.CheckDeepEquals(t, expected, actual)
+		th.CheckDeepEquals(t, LoadbalancerWeb, actual[0])
+		th.CheckDeepEquals(t, LoadbalancerDb, actual[1])
 
 		return true, nil
 	})
 
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
-}
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "loadbalancer": {
-	"name": "NewLb",
-	"vip_subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
-	"vip_address": "10.0.0.11",
-	"flavor": "small",
-	"provider": "provider_1",
-	"admin_state_up": true
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-    "loadbalancer": {
-        "id": "04816630-0320-4f9d-a17b-402b4e145d91",
-	"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
-	"name": "NewLb",
-	"description": "",
-	"vip_subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
-	"vip_address": "10.0.0.11",
-	"flavor": "small",
-	"provider": "provider_1",
-	"admin_state_up": true,
-	"provisioning_status": "PENDING_CREATE",
-	"operating_status": "OFFLINE"
-    }
-}
-		`)
-	})
-
-	opts := CreateOpts{
-		Name:         "NewLb",
-		AdminStateUp: Up,
-		VipSubnetID:  "8032909d-47a1-4715-90af-5153ffe39861",
-		VipAddress:   "10.0.0.11",
-		Flavor:       "small",
-		Provider:     "provider_1",
-	}
-
-	r, err := Create(fake.ServiceClient(), opts).Extract()
 	th.AssertNoErr(t, err)
 
-	th.AssertEquals(t, "PENDING_CREATE", r.ProvisioningStatus)
-	th.AssertEquals(t, "", r.Description)
-	th.AssertEquals(t, true, r.AdminStateUp)
-	th.AssertEquals(t, "8032909d-47a1-4715-90af-5153ffe39861", r.VipSubnetID)
-	th.AssertEquals(t, "54030507-44f7-473c-9342-b4d14a95f692", r.TenantID)
-	th.AssertEquals(t, "NewLb", r.Name)
-	th.AssertEquals(t, "OFFLINE", r.OperatingStatus)
-	th.AssertEquals(t, "small", r.Flavor)
-	th.AssertEquals(t, "provider_1", r.Provider)
-	th.AssertEquals(t, "10.0.0.11", r.VipAddress)
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
+	}
+}
+
+func TestListAllLoadbalancers(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleLoadbalancerListSuccessfully(t)
+
+	allPages, err := List(fake.ServiceClient(), ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := ExtractLoadbalancers(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, LoadbalancerWeb, actual[0])
+	th.CheckDeepEquals(t, LoadbalancerDb, actual[1])
+}
+
+func TestCreateLoadbalancer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleLoadbalancerCreationSuccessfully(t, SingleLoadbalancerBody)
+
+	actual, err := Create(fake.ServiceClient(), CreateOpts{
+		Name:         "db_lb",
+		AdminStateUp: Up,
+		VipSubnetID:  "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
+		VipAddress:   "10.30.176.48",
+		Flavor:       "medium",
+		Provider:     "haproxy",
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, LoadbalancerDb, *actual)
 }
 
 func TestRequiredCreateOpts(t *testing.T) {
@@ -199,106 +96,55 @@
 	}
 }
 
-func TestGet(t *testing.T) {
+func TestGetLoadbalancer(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandleLoadbalancerGetSuccessfully(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/3c073c6d-18f7-45e4-8921-84358b70542d", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "loadbalancer": {
-	"id": "3c073c6d-18f7-45e4-8921-84358b70542d",
-	"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
-	"name": "web_lb",
-	"description": "",
-	"vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154",
-	"vip_address": "10.30.176.47",
-	"flavor": "small",
-	"provider": "provider_1",
-	"admin_state_up": true,
-	"provisioning_status": "ACTIVE",
-	"operating_status": "ONLINE"
-	}
-}
-			`)
-	})
-
-	loadbalancer, err := Get(fake.ServiceClient(), "3c073c6d-18f7-45e4-8921-84358b70542d").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "ACTIVE", loadbalancer.ProvisioningStatus)
-	th.AssertEquals(t, "ONLINE", loadbalancer.OperatingStatus)
-	th.AssertEquals(t, "", loadbalancer.Description)
-	th.AssertEquals(t, true, loadbalancer.AdminStateUp)
-	th.AssertEquals(t, "web_lb", loadbalancer.Name)
-	th.AssertEquals(t, "10.30.176.47", loadbalancer.VipAddress)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/038d98e2-6a7d-473a-9ce4-76f7e1e0c6a4", 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, `
-{
-    "loadbalancer": {
-        "name": "NewLbName"
-    }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-
-		fmt.Fprintf(w, `
-{
-    "loadbalancer": {
-    "id": "038d98e2-6a7d-473a-9ce4-76f7e1e0c6a4",
-	"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
-	"name": "NewLbName",
-	"description": "",
-	"vip_subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
-	"vip_address": "10.0.0.11",
-	"flavor": "small",
-	"provider": "provider_1",
-	"admin_state_up": true,
-	"provisioning_status": "PENDING_UPDATE",
-	"operating_status": "OFFLINE"
-    }
-}
-		`)
-	})
-
-	options := UpdateOpts{
-		Name: "NewLbName",
+	client := fake.ServiceClient()
+	actual, err := Get(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
 	}
 
-	loadbalancer, err := Update(fake.ServiceClient(), "038d98e2-6a7d-473a-9ce4-76f7e1e0c6a4", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "NewLbName", loadbalancer.Name)
+	th.CheckDeepEquals(t, LoadbalancerDb, *actual)
 }
 
-func TestDelete(t *testing.T) {
+func TestGetLoadbalancerStatusesTree(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandleLoadbalancerGetStatusesTree(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/82c54b9a-cf84-460f-bdcf-fa591e6fd6f0", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
+	client := fake.ServiceClient()
+	actual, err := GetStatuses(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractStatuses()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
 
-	res := Delete(fake.ServiceClient(), "82c54b9a-cf84-460f-bdcf-fa591e6fd6f0")
+	th.CheckDeepEquals(t, LoadbalancerStatusesTree, *(actual.Loadbalancer))
+}
+
+func TestDeleteLoadbalancer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleLoadbalancerDeletionSuccessfully(t)
+
+	res := Delete(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab")
 	th.AssertNoErr(t, res.Err)
 }
+
+func TestUpdateLoadbalancer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleLoadbalancerUpdateSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", UpdateOpts{
+		Name: "NewLoadbalancerName",
+	}).Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Update error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, LoadbalancerUpdated, *actual)
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go
index 4306b06..911c156 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go
@@ -3,14 +3,13 @@
 import (
 	"github.com/mitchellh/mapstructure"
 	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
 	"github.com/rackspace/gophercloud/pagination"
 )
 
 // LoadBalancer is the primary load balancing configuration object that specifies
 // the virtual IP address on which client traffic is received, as well
 // as other details such as the load balancing method to be use, protocol, etc.
-// This entity is sometimes known in LB products under the name of a "virtual
-// server", a "vserver" or a "listener".
 type LoadBalancer struct {
 	// Human-readable description for the Loadbalancer.
 	Description string `mapstructure:"description" json:"description"`
@@ -44,6 +43,12 @@
 
 	// The name of the provider.
 	Provider string `mapstructure:"provider" json:"provider"`
+
+	Listeners []listeners.Listener `mapstructure:"listeners" json:"listeners"`
+}
+
+type StatusTree struct {
+	Loadbalancer *LoadBalancer `mapstructure:"loadbalancer" json:"loadbalancer"`
 }
 
 // LoadbalancerPage is the page returned by a pager when traversing over a
@@ -107,6 +112,19 @@
 	return res.LoadBalancer, err
 }
 
+// Extract is a function that accepts a result and extracts a Loadbalancer.
+func (r commonResult) ExtractStatuses() (*StatusTree, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+	var res struct {
+		LoadBalancer *StatusTree `mapstructure:"statuses" json:"statuses"`
+	}
+	err := mapstructure.Decode(r.Body, &res)
+
+	return res.LoadBalancer, err
+}
+
 // CreateResult represents the result of a create operation.
 type CreateResult struct {
 	commonResult
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go
index 48bcc39..a307490 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go
@@ -5,6 +5,7 @@
 const (
 	rootPath     = "lbaas"
 	resourcePath = "loadbalancers"
+	statusPath   = "statuses"
 )
 
 func rootURL(c *gophercloud.ServiceClient) string {
@@ -14,3 +15,7 @@
 func resourceURL(c *gophercloud.ServiceClient, id string) string {
 	return c.ServiceURL(rootPath, resourcePath, id)
 }
+
+func statusRootURL(c *gophercloud.ServiceClient, id string) string {
+	return c.ServiceURL(rootPath, resourcePath, id, statusPath)
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/fixtures.go
new file mode 100644
index 0000000..549dbbc
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/fixtures.go
@@ -0,0 +1,215 @@
+// +build fixtures
+package monitors
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/rackspace/gophercloud/testhelper/client"
+)
+
+// HealthmonitorsListBody contains the canned body of a healthmonitor list response.
+const HealthmonitorsListBody = `
+{
+	"healthmonitors":[
+		{
+			"admin_state_up":true,
+			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+			"delay":10,
+			"name":"web",
+			"max_retries":1,
+			"timeout":1,
+			"type":"PING",
+			"pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}],
+			"id":"466c8345-28d8-4f84-a246-e04380b0461d"
+		},
+		{
+			"admin_state_up":true,
+			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+			"delay":5,
+			"name":"db",
+			"expected_codes":"200",
+			"max_retries":2,
+			"http_method":"GET",
+			"timeout":2,
+			"url_path":"/",
+			"type":"HTTP",
+			"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
+			"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
+		}
+	]
+}
+`
+
+// SingleHealthmonitorBody is the canned body of a Get request on an existing healthmonitor.
+const SingleHealthmonitorBody = `
+{
+	"healthmonitor": {
+		"admin_state_up":true,
+		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+		"delay":5,
+		"name":"db",
+		"expected_codes":"200",
+		"max_retries":2,
+		"http_method":"GET",
+		"timeout":2,
+		"url_path":"/",
+		"type":"HTTP",
+		"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
+		"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
+	}
+}
+`
+
+// PostUpdateHealthmonitorBody is the canned response body of a Update request on an existing healthmonitor.
+const PostUpdateHealthmonitorBody = `
+{
+	"healthmonitor": {
+		"admin_state_up":true,
+		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+		"delay":3,
+		"name":"NewHealthmonitorName",
+		"expected_codes":"301",
+		"max_retries":10,
+		"http_method":"GET",
+		"timeout":20,
+		"url_path":"/another_check",
+		"type":"HTTP",
+		"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
+		"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
+	}
+}
+`
+
+var (
+	HealthmonitorWeb = Monitor{
+		AdminStateUp: true,
+		Name:         "web",
+		TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
+		Delay:        10,
+		MaxRetries:   1,
+		Timeout:      1,
+		Type:         "PING",
+		ID:           "466c8345-28d8-4f84-a246-e04380b0461d",
+		Pools:        []PoolID{{ID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}},
+	}
+	HealthmonitorDb = Monitor{
+		AdminStateUp:  true,
+		Name:          "db",
+		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+		Delay:         5,
+		ExpectedCodes: "200",
+		MaxRetries:    2,
+		Timeout:       2,
+		URLPath:       "/",
+		Type:          "HTTP",
+		HTTPMethod:    "GET",
+		ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
+		Pools:         []PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}},
+	}
+	HealthmonitorUpdated = Monitor{
+		AdminStateUp:  true,
+		Name:          "NewHealthmonitorName",
+		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+		Delay:         3,
+		ExpectedCodes: "301",
+		MaxRetries:    10,
+		Timeout:       20,
+		URLPath:       "/another_check",
+		Type:          "HTTP",
+		HTTPMethod:    "GET",
+		ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
+		Pools:         []PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}},
+	}
+)
+
+// HandleHealthmonitorListSuccessfully sets up the test server to respond to a healthmonitor List request.
+func HandleHealthmonitorListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, HealthmonitorsListBody)
+		case "556c8345-28d8-4f84-a246-e04380b0461d":
+			fmt.Fprintf(w, `{ "healthmonitors": [] }`)
+		default:
+			t.Fatalf("/v2.0/lbaas/healthmonitors invoked with unexpected marker=[%s]", marker)
+		}
+	})
+}
+
+// HandleHealthmonitorCreationSuccessfully sets up the test server to respond to a healthmonitor creation request
+// with a given response.
+func HandleHealthmonitorCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"healthmonitor": {
+				"type":"HTTP",
+				"pool_id":"84f1b61f-58c4-45bf-a8a9-2dafb9e5214d",
+				"tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
+				"delay":20,
+				"name":"db",
+				"timeout":10,
+				"max_retries":5,
+				"url_path":"/check",
+				"expected_codes":"200-299"
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandleHealthmonitorGetSuccessfully sets up the test server to respond to a healthmonitor Get request.
+func HandleHealthmonitorGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleHealthmonitorBody)
+	})
+}
+
+// HandleHealthmonitorDeletionSuccessfully sets up the test server to respond to a healthmonitor deletion request.
+func HandleHealthmonitorDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleHealthmonitorUpdateSuccessfully sets up the test server to respond to a healthmonitor Update request.
+func HandleHealthmonitorUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `{
+			"healthmonitor": {
+				"name": "NewHealthmonitorName",
+				"delay": 3,
+				"timeout": 20,
+				"max_retries": 10,
+				"url_path": "/another_check",
+				"expected_codes": "301"
+			}
+		}`)
+
+		fmt.Fprintf(w, PostUpdateHealthmonitorBody)
+	})
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go
index 71d0ade..92e7e83 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go
@@ -2,76 +2,17 @@
 
 import (
 	"fmt"
-
 	"github.com/rackspace/gophercloud"
 	"github.com/rackspace/gophercloud/pagination"
 )
 
-// ListOpts allows the filtering and sorting of paginated collections through
-// the API. Filtering is achieved by passing in struct field values that map to
-// the Monitor attributes you want to see returned. SortKey allows you to
-// sort by a particular Monitor attribute. SortDir sets the direction, and is
-// either `asc' or `desc'. Marker and Limit are used for pagination.
-type ListOpts struct {
-	ID            string `q:"id"`
-	TenantID      string `q:"tenant_id"`
-	Type          string `q:"type"`
-	Delay         int    `q:"delay"`
-	Timeout       int    `q:"timeout"`
-	MaxRetries    int    `q:"max_retries"`
-	HTTPMethod    string `q:"http_method"`
-	URLPath       string `q:"url_path"`
-	ExpectedCodes string `q:"expected_codes"`
-	AdminStateUp  *bool  `q:"admin_state_up"`
-	Status        string `q:"status"`
-	Limit         int    `q:"limit"`
-	Marker        string `q:"marker"`
-	SortKey       string `q:"sort_key"`
-	SortDir       string `q:"sort_dir"`
-}
-
-// List returns a Pager which allows you to iterate over a collection of
-// routers. It accepts a ListOpts struct, which allows you to filter and sort
-// the returned collection for greater efficiency.
-//
-// Default policy settings return only those routers that are owned by the
-// tenant who submits the request, unless an admin user submits the request.
-func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
-	q, err := gophercloud.BuildQueryString(&opts)
-	if err != nil {
-		return pagination.Pager{Err: err}
-	}
-	u := rootURL(c) + q.String()
-
-	return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
-		return MonitorPage{pagination.LinkedPageBase{PageResult: r}}
-	})
-}
-
-// Constants that represent approved monitoring types.
-const (
-	TypePING  = "PING"
-	TypeTCP   = "TCP"
-	TypeHTTP  = "HTTP"
-	TypeHTTPS = "HTTPS"
-)
-
-var (
-	errPoolIDRequired        = fmt.Errorf("Pool ID to monitor is required")
-	errValidTypeRequired     = fmt.Errorf("A valid Type is required. Supported values are PING, TCP, HTTP and HTTPS")
-	errDelayRequired         = fmt.Errorf("Delay is required")
-	errTimeoutRequired       = fmt.Errorf("Timeout is required")
-	errMaxRetriesRequired    = fmt.Errorf("MaxRetries is required")
-	errURLPathRequired       = fmt.Errorf("URL path is required")
-	errExpectedCodesRequired = fmt.Errorf("ExpectedCodes is required")
-	errDelayMustGETimeout    = fmt.Errorf("Delay must be greater than or equal to timeout")
-)
-
-// CreateOpts contains all the values needed to create a new Health Monitor.
-type CreateOpts struct {
-	// The Pool to Monitor.
+type monitorOpts struct {
+	// Required. The Pool to Monitor.
 	PoolID string
 
+	// Optional. The Name of the Monitor.
+	Name string
+
 	// Required for admins. Indicates the owner of the Loadbalancer.
 	TenantID string
 
@@ -106,85 +47,182 @@
 	AdminStateUp *bool
 }
 
-// Create is an operation which provisions a new Health Monitor. There are
-// different types of Monitor you can provision: PING, TCP or HTTP(S). Below
-// are examples of how to create each one.
-//
-// Here is an example config struct to use when creating a PING or TCP Monitor:
-//
-// CreateOpts{Type: TypePING, Delay: 20, Timeout: 10, MaxRetries: 3}
-// CreateOpts{Type: TypeTCP, Delay: 20, Timeout: 10, MaxRetries: 3}
-//
-// Here is an example config struct to use when creating a HTTP(S) Monitor:
-//
-// CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3,
-//  HttpMethod: "HEAD", ExpectedCodes: "200". PoolID: "2c946bfc-1804-43ab-a2ff-58f6a762b505"}
-//
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	var res CreateResult
+// ListOptsBuilder allows extensions to add additional parameters to the
+// List request.
+type ListOptsBuilder interface {
+	ToMonitorListQuery() (string, error)
+}
 
-	// Validate inputs
+// ListOpts allows the filtering and sorting of paginated collections through
+// the API. Filtering is achieved by passing in struct field values that map to
+// the Monitor attributes you want to see returned. SortKey allows you to
+// sort by a particular Monitor attribute. SortDir sets the direction, and is
+// either `asc' or `desc'. Marker and Limit are used for pagination.
+type ListOpts struct {
+	ID            string `q:"id"`
+	Name          string `q:"name"`
+	TenantID      string `q:"tenant_id"`
+	PoolID        string `q:"pool_id"`
+	Type          string `q:"type"`
+	Delay         int    `q:"delay"`
+	Timeout       int    `q:"timeout"`
+	MaxRetries    int    `q:"max_retries"`
+	HTTPMethod    string `q:"http_method"`
+	URLPath       string `q:"url_path"`
+	ExpectedCodes string `q:"expected_codes"`
+	AdminStateUp  *bool  `q:"admin_state_up"`
+	Status        string `q:"status"`
+	Limit         int    `q:"limit"`
+	Marker        string `q:"marker"`
+	SortKey       string `q:"sort_key"`
+	SortDir       string `q:"sort_dir"`
+}
+
+// ToMonitorListQuery formats a ListOpts into a query string.
+func (opts ListOpts) ToMonitorListQuery() (string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return "", err
+	}
+	return q.String(), nil
+}
+
+// List returns a Pager which allows you to iterate over a collection of
+// health monitors. It accepts a ListOpts struct, which allows you to filter and sort
+// the returned collection for greater efficiency.
+//
+// Default policy settings return only those health monitors that are owned by the
+// tenant who submits the request, unless an admin user submits the request.
+func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
+	url := rootURL(c)
+	if opts != nil {
+		query, err := opts.ToMonitorListQuery()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		url += query
+	}
+
+	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
+		return MonitorPage{pagination.LinkedPageBase{PageResult: r}}
+	})
+}
+
+// Constants that represent approved monitoring types.
+const (
+	TypePING  = "PING"
+	TypeTCP   = "TCP"
+	TypeHTTP  = "HTTP"
+	TypeHTTPS = "HTTPS"
+)
+
+var (
+	errPoolIDRequired        = fmt.Errorf("PoolID to monitor is required")
+	errValidTypeRequired     = fmt.Errorf("A valid Type is required. Supported values are PING, TCP, HTTP and HTTPS")
+	errDelayRequired         = fmt.Errorf("Delay is required")
+	errTimeoutRequired       = fmt.Errorf("Timeout is required")
+	errMaxRetriesRequired    = fmt.Errorf("MaxRetries is required")
+	errURLPathRequired       = fmt.Errorf("URL path is required")
+	errExpectedCodesRequired = fmt.Errorf("ExpectedCodes is required")
+	errDelayMustGETimeout    = fmt.Errorf("Delay must be greater than or equal to timeout")
+)
+
+// CreateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the main Create operation in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type CreateOptsBuilder interface {
+	ToMonitorCreateMap() (map[string]interface{}, error)
+}
+
+// CreateOpts is the common options struct used in this package's Create
+// operation.
+type CreateOpts monitorOpts
+
+// ToMonitorCreateMap casts a CreateOpts struct to a map.
+func (opts CreateOpts) ToMonitorCreateMap() (map[string]interface{}, error) {
+	l := make(map[string]interface{})
 	allowed := map[string]bool{TypeHTTP: true, TypeHTTPS: true, TypeTCP: true, TypePING: true}
-	if opts.Type == "" || allowed[opts.Type] == false {
-		res.Err = errValidTypeRequired
-	}
-	if opts.PoolID == "" {
-		res.Err = errPoolIDRequired
-	}
-	if opts.Delay == 0 {
-		res.Err = errDelayRequired
-	}
-	if opts.Timeout == 0 {
-		res.Err = errTimeoutRequired
-	}
-	if opts.MaxRetries == 0 {
-		res.Err = errMaxRetriesRequired
+
+	if allowed[opts.Type] {
+		l["type"] = opts.Type
+	} else {
+		return nil, errValidTypeRequired
 	}
 	if opts.Type == TypeHTTP || opts.Type == TypeHTTPS {
-		if opts.URLPath == "" {
-			res.Err = errURLPathRequired
+		if opts.URLPath != "" {
+			l["url_path"] = opts.URLPath
+		} else {
+			return nil, errURLPathRequired
 		}
-		if opts.ExpectedCodes == "" {
-			res.Err = errExpectedCodesRequired
+		if opts.ExpectedCodes != "" {
+			l["expected_codes"] = opts.ExpectedCodes
+		} else {
+			return nil, errExpectedCodesRequired
 		}
 	}
-	if opts.Delay < opts.Timeout {
-		res.Err = errDelayMustGETimeout
+	if opts.PoolID != "" {
+		l["pool_id"] = opts.PoolID
+	} else {
+		return nil, errPoolIDRequired
 	}
-	if res.Err != nil {
+	if opts.Delay != 0 {
+		l["delay"] = opts.Delay
+	} else {
+		return nil, errDelayRequired
+	}
+	if opts.Timeout != 0 {
+		l["timeout"] = opts.Timeout
+	} else {
+		return nil, errMaxRetriesRequired
+	}
+	if opts.MaxRetries != 0 {
+		l["max_retries"] = opts.MaxRetries
+	} else {
+		return nil, errMaxRetriesRequired
+	}
+	if opts.AdminStateUp != nil {
+		l["admin_state_up"] = &opts.AdminStateUp
+	}
+	if opts.Name != "" {
+		l["name"] = opts.Name
+	}
+	if opts.TenantID != "" {
+		l["tenant_id"] = opts.TenantID
+	}
+	if opts.HTTPMethod != "" {
+		l["http_method"] = opts.HTTPMethod
+	}
+
+	return map[string]interface{}{"healthmonitor": l}, nil
+}
+
+/*
+ Create is an operation which provisions a new Health Monitor. There are
+ different types of Monitor you can provision: PING, TCP or HTTP(S). Below
+ are examples of how to create each one.
+
+ Here is an example config struct to use when creating a PING or TCP Monitor:
+
+ CreateOpts{Type: TypePING, Delay: 20, Timeout: 10, MaxRetries: 3}
+ CreateOpts{Type: TypeTCP, Delay: 20, Timeout: 10, MaxRetries: 3}
+
+ Here is an example config struct to use when creating a HTTP(S) Monitor:
+
+ CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3,
+ HttpMethod: "HEAD", ExpectedCodes: "200", PoolID: "2c946bfc-1804-43ab-a2ff-58f6a762b505"}
+*/
+
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
+	var res CreateResult
+
+	reqBody, err := opts.ToMonitorCreateMap()
+	if err != nil {
+		res.Err = err
 		return res
 	}
 
-	type monitor struct {
-		Type          string  `json:"type"`
-		PoolID        string  `json:"pool_id"`
-		Delay         int     `json:"delay"`
-		Timeout       int     `json:"timeout"`
-		MaxRetries    int     `json:"max_retries"`
-		TenantID      *string `json:"tenant_id,omitempty"`
-		URLPath       *string `json:"url_path,omitempty"`
-		ExpectedCodes *string `json:"expected_codes,omitempty"`
-		HTTPMethod    *string `json:"http_method,omitempty"`
-		AdminStateUp  *bool   `json:"admin_state_up,omitempty"`
-	}
-
-	type request struct {
-		Monitor monitor `json:"health_monitor"`
-	}
-
-	reqBody := request{Monitor: monitor{
-		Type:          opts.Type,
-		PoolID:        opts.PoolID,
-		Delay:         opts.Delay,
-		Timeout:       opts.Timeout,
-		MaxRetries:    opts.MaxRetries,
-		TenantID:      gophercloud.MaybeString(opts.TenantID),
-		URLPath:       gophercloud.MaybeString(opts.URLPath),
-		ExpectedCodes: gophercloud.MaybeString(opts.ExpectedCodes),
-		HTTPMethod:    gophercloud.MaybeString(opts.HTTPMethod),
-		AdminStateUp:  opts.AdminStateUp,
-	}}
-
+	// Send request to API
 	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 	return res
 }
@@ -196,69 +234,61 @@
 	return res
 }
 
-// UpdateOpts contains all the values needed to update an existing Monitor.
-// Attributes not listed here but appear in CreateOpts are immutable and cannot
-// be updated.
-type UpdateOpts struct {
-	// Required. The time, in seconds, between sending probes to members.
-	Delay int
+// UpdateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the main Update operation in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type UpdateOptsBuilder interface {
+	ToMonitorUpdateMap() (map[string]interface{}, error)
+}
 
-	// Required. Maximum number of seconds for a Monitor to wait for a ping reply
-	// before it times out. The value must be less than the delay value.
-	Timeout int
+// UpdateOpts is the common options struct used in this package's Update
+// operation.
+type UpdateOpts monitorOpts
 
-	// Required. Number of permissible ping failures before changing the member's
-	// status to INACTIVE. Must be a number between 1 and 10.
-	MaxRetries int
+// ToMonitorUpdateMap casts a UpdateOpts struct to a map.
+func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]interface{}, error) {
+	l := make(map[string]interface{})
 
-	// Required for HTTP(S) types. URI path that will be accessed if Monitor type
-	// is HTTP or HTTPS.
-	URLPath string
+	if opts.URLPath != "" {
+		l["url_path"] = opts.URLPath
+	}
+	if opts.ExpectedCodes != "" {
+		l["expected_codes"] = opts.ExpectedCodes
+	}
+	if opts.Delay != 0 {
+		l["delay"] = opts.Delay
+	}
+	if opts.Timeout != 0 {
+		l["timeout"] = opts.Timeout
+	}
+	if opts.MaxRetries != 0 {
+		l["max_retries"] = opts.MaxRetries
+	}
+	if opts.AdminStateUp != nil {
+		l["admin_state_up"] = &opts.AdminStateUp
+	}
+	if opts.Name != "" {
+		l["name"] = opts.Name
+	}
+	if opts.HTTPMethod != "" {
+		l["http_method"] = opts.HTTPMethod
+	}
 
-	// Required for HTTP(S) types. The HTTP method used for requests by the
-	// Monitor. If this attribute is not specified, it defaults to "GET".
-	HTTPMethod string
-
-	// Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S)
-	// Monitor. You can either specify a single status like "200", or a range
-	// like "200-202".
-	ExpectedCodes string
-
-	AdminStateUp *bool
+	return map[string]interface{}{"healthmonitor": l}, nil
 }
 
 // Update is an operation which modifies the attributes of the specified Monitor.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
 	var res UpdateResult
 
-	if opts.Delay > 0 && opts.Timeout > 0 && opts.Delay < opts.Timeout {
-		res.Err = errDelayMustGETimeout
+	reqBody, err := opts.ToMonitorUpdateMap()
+	if err != nil {
+		res.Err = err
+		return res
 	}
 
-	type monitor struct {
-		Delay         int     `json:"delay"`
-		Timeout       int     `json:"timeout"`
-		MaxRetries    int     `json:"max_retries"`
-		URLPath       *string `json:"url_path,omitempty"`
-		ExpectedCodes *string `json:"expected_codes,omitempty"`
-		HTTPMethod    *string `json:"http_method,omitempty"`
-		AdminStateUp  *bool   `json:"admin_state_up,omitempty"`
-	}
-
-	type request struct {
-		Monitor monitor `json:"health_monitor"`
-	}
-
-	reqBody := request{Monitor: monitor{
-		Delay:         opts.Delay,
-		Timeout:       opts.Timeout,
-		MaxRetries:    opts.MaxRetries,
-		URLPath:       gophercloud.MaybeString(opts.URLPath),
-		ExpectedCodes: gophercloud.MaybeString(opts.ExpectedCodes),
-		HTTPMethod:    gophercloud.MaybeString(opts.HTTPMethod),
-		AdminStateUp:  opts.AdminStateUp,
-	}}
-
+	// Send request to API
 	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests_test.go
index 4e7df2a..202a182 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests_test.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests_test.go
@@ -1,8 +1,6 @@
 package monitors
 
 import (
-	"fmt"
-	"net/http"
 	"testing"
 
 	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
@@ -13,97 +11,128 @@
 func TestURLs(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
-	th.AssertEquals(t, th.Endpoint()+"v2.0/lbaas/health_monitors", rootURL(fake.ServiceClient()))
+	th.AssertEquals(t, th.Endpoint()+"v2.0/lbaas/healthmonitors", rootURL(fake.ServiceClient()))
 }
 
-func TestList(t *testing.T) {
+func TestListHealthmonitors(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandleHealthmonitorListSuccessfully(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/health_monitors", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+	pages := 0
+	err := List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
 
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "health_monitors":[
-      {
-         "admin_state_up":true,
-         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-         "delay":10,
-         "max_retries":1,
-         "timeout":1,
-         "type":"PING",
-         "pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}],
-         "id":"466c8345-28d8-4f84-a246-e04380b0461d"
-      },
-      {
-         "admin_state_up":true,
-         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-         "delay":5,
-         "expected_codes":"200",
-         "max_retries":2,
-         "http_method":"GET",
-         "timeout":2,
-         "url_path":"/",
-         "type":"HTTP",
-         "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
-         "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
-      }
-   ]
-}
-			`)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
 		actual, err := ExtractMonitors(page)
 		if err != nil {
-			t.Errorf("Failed to extract monitors: %v", err)
 			return false, err
 		}
 
-		expected := []Monitor{
-			{
-				AdminStateUp: true,
-				TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
-				Delay:        10,
-				MaxRetries:   1,
-				Timeout:      1,
-				Type:         "PING",
-				ID:           "466c8345-28d8-4f84-a246-e04380b0461d",
-				Pools:        []map[string]interface{}{{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}},
-			},
-			{
-				AdminStateUp:  true,
-				TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
-				Delay:         5,
-				ExpectedCodes: "200",
-				MaxRetries:    2,
-				Timeout:       2,
-				URLPath:       "/",
-				Type:          "HTTP",
-				HTTPMethod:    "GET",
-				ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
-				Pools:         []map[string]interface{}{{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}},
-			},
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 healthmonitors, got %d", len(actual))
 		}
-
-		th.CheckDeepEquals(t, expected, actual)
+		th.CheckDeepEquals(t, HealthmonitorWeb, actual[0])
+		th.CheckDeepEquals(t, HealthmonitorDb, actual[1])
 
 		return true, nil
 	})
 
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
+	th.AssertNoErr(t, err)
+
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
 	}
 }
 
+func TestListAllHealthmonitors(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleHealthmonitorListSuccessfully(t)
+
+	allPages, err := List(fake.ServiceClient(), ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := ExtractMonitors(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, HealthmonitorWeb, actual[0])
+	th.CheckDeepEquals(t, HealthmonitorDb, actual[1])
+}
+
+func TestCreateHealthmonitor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleHealthmonitorCreationSuccessfully(t, SingleHealthmonitorBody)
+
+	actual, err := Create(fake.ServiceClient(), CreateOpts{
+		Type:          "HTTP",
+		Name:          "db",
+		PoolID:        "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d",
+		TenantID:      "453105b9-1754-413f-aab1-55f1af620750",
+		Delay:         20,
+		Timeout:       10,
+		MaxRetries:    5,
+		URLPath:       "/check",
+		ExpectedCodes: "200-299",
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, HealthmonitorDb, *actual)
+}
+
+func TestRequiredCreateOpts(t *testing.T) {
+	res := Create(fake.ServiceClient(), CreateOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = Create(fake.ServiceClient(), CreateOpts{Type: TypeHTTP})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+}
+
+func TestGetHealthmonitor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleHealthmonitorGetSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := Get(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, HealthmonitorDb, *actual)
+}
+
+func TestDeleteHealthmonitor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleHealthmonitorDeletionSuccessfully(t)
+
+	res := Delete(fake.ServiceClient(), "5d4b5228-33b0-4e60-b225-9b727c1a20e7")
+	th.AssertNoErr(t, res.Err)
+}
+
+func TestUpdateHealthmonitor(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleHealthmonitorUpdateSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", UpdateOpts{
+		Name:          "NewHealthmonitorName",
+		Delay:         3,
+		Timeout:       20,
+		MaxRetries:    10,
+		URLPath:       "/another_check",
+		ExpectedCodes: "301",
+	}).Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Update error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, HealthmonitorUpdated, *actual)
+}
+
 func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) {
 	_, err := Create(fake.ServiceClient(), CreateOpts{
 		Type:          "HTTP",
@@ -128,196 +157,3 @@
 		t.Fatalf("Expected error, got none")
 	}
 }
-
-func TestCreate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/health_monitors", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-   "health_monitor":{
-      "type":"HTTP",
-      "pool_id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d",
-      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
-      "delay":20,
-      "timeout":10,
-      "max_retries":5,
-      "url_path":"/check",
-      "expected_codes":"200-299"
-   }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
-
-		fmt.Fprintf(w, `
-{
-   "health_monitor":{
-      "id":"f3eeab00-8367-4524-b662-55e64d4cacb5",
-      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
-      "type":"HTTP",
-      "pool_id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d",
-      "delay":20,
-      "timeout":10,
-      "max_retries":5,
-      "http_method":"GET",
-      "url_path":"/check",
-      "expected_codes":"200-299",
-      "admin_state_up":true,
-      "status":"ACTIVE"
-   }
-}
-		`)
-	})
-
-	_, err := Create(fake.ServiceClient(), CreateOpts{
-		Type:          "HTTP",
-		PoolID:        "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d",
-		TenantID:      "453105b9-1754-413f-aab1-55f1af620750",
-		Delay:         20,
-		Timeout:       10,
-		MaxRetries:    5,
-		URLPath:       "/check",
-		ExpectedCodes: "200-299",
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-}
-
-func TestRequiredCreateOpts(t *testing.T) {
-	res := Create(fake.ServiceClient(), CreateOpts{})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-	res = Create(fake.ServiceClient(), CreateOpts{Type: TypeHTTP})
-	if res.Err == nil {
-		t.Fatalf("Expected error, got none")
-	}
-}
-
-func TestGet(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/health_monitors/f3eeab00-8367-4524-b662-55e64d4cacb5", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "health_monitor":{
-      "id":"f3eeab00-8367-4524-b662-55e64d4cacb5",
-      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
-      "type":"HTTP",
-      "pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}],
-      "delay":20,
-      "timeout":10,
-      "max_retries":5,
-      "http_method":"GET",
-      "url_path":"/check",
-      "expected_codes":"200-299",
-      "admin_state_up":true,
-      "status":"ACTIVE"
-   }
-}
-			`)
-	})
-
-	hm, err := Get(fake.ServiceClient(), "f3eeab00-8367-4524-b662-55e64d4cacb5").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "f3eeab00-8367-4524-b662-55e64d4cacb5", hm.ID)
-	th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", hm.TenantID)
-	th.AssertEquals(t, "HTTP", hm.Type)
-	th.AssertEquals(t, "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", hm.Pools[0]["id"])
-	th.AssertEquals(t, 20, hm.Delay)
-	th.AssertEquals(t, 10, hm.Timeout)
-	th.AssertEquals(t, 5, hm.MaxRetries)
-	th.AssertEquals(t, "GET", hm.HTTPMethod)
-	th.AssertEquals(t, "/check", hm.URLPath)
-	th.AssertEquals(t, "200-299", hm.ExpectedCodes)
-	th.AssertEquals(t, true, hm.AdminStateUp)
-	th.AssertEquals(t, "ACTIVE", hm.Status)
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-   "health_monitor":{
-      "delay": 3,
-      "timeout": 20,
-      "max_retries": 10,
-      "url_path": "/another_check",
-      "expected_codes": "301"
-   }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusAccepted)
-
-		fmt.Fprintf(w, `
-{
-    "health_monitor": {
-        "admin_state_up": true,
-        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
-        "delay": 3,
-        "max_retries": 10,
-        "http_method": "GET",
-        "timeout": 20,
-        "pools": [
-            {
-                "status": "PENDING_CREATE",
-                "status_description": null,
-                "pool_id": "6e55751f-6ad4-4e53-b8d4-02e442cd21df"
-            }
-        ],
-        "type": "PING",
-        "pool_id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d",
-        "id": "b05e44b5-81f9-4551-b474-711a722698f7"
-    }
-}
-		`)
-	})
-
-	_, err := Update(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7", UpdateOpts{
-		Delay:         3,
-		Timeout:       20,
-		MaxRetries:    10,
-		URLPath:       "/another_check",
-		ExpectedCodes: "301",
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7")
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go
index 599c130..da363c0 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go
@@ -6,6 +6,10 @@
 	"github.com/rackspace/gophercloud/pagination"
 )
 
+type PoolID struct {
+	ID string `mapstructure:"id" json:"id"`
+}
+
 // Monitor represents a load balancer health monitor. A health monitor is used
 // to determine whether or not back-end members of the VIP's pool are usable
 // for processing a request. A pool can have several health monitors associated
@@ -22,10 +26,13 @@
 // won't participate in its pool's load balancing. In other words, ALL monitors
 // must declare the member to be healthy for it to stay ACTIVE.
 type Monitor struct {
-	// The unique ID for the VIP.
+	// The unique ID for the Monitor.
 	ID string
 
-	// Owner of the VIP. Only an administrative user can specify a tenant ID
+	// The Name of the Monitor.
+	Name string
+
+	// Only an administrative user can specify a tenant ID
 	// other than its own.
 	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
 
@@ -62,7 +69,10 @@
 	Status string
 
 	// List of pools that are associated with the health monitor.
-	Pools []map[string]interface{} `mapstructure:"pools" json:"pools"`
+	Pools []PoolID `mapstructure:"pools" json:"pools"`
+}
+
+type Pool struct {
 }
 
 // MonitorPage is the page returned by a pager when traversing over a
@@ -76,7 +86,7 @@
 // to do this, it needs to construct the next page's URL.
 func (p MonitorPage) NextPageURL() (string, error) {
 	type resp struct {
-		Links []gophercloud.Link `mapstructure:"health_monitors_links"`
+		Links []gophercloud.Link `mapstructure:"healthmonitors_links"`
 	}
 
 	var r resp
@@ -102,7 +112,7 @@
 // a generic collection is mapped into a relevant slice.
 func ExtractMonitors(page pagination.Page) ([]Monitor, error) {
 	var resp struct {
-		Monitors []Monitor `mapstructure:"health_monitors" json:"health_monitors"`
+		Monitors []Monitor `mapstructure:"healthmonitors" json:"healthmonitors"`
 	}
 
 	err := mapstructure.Decode(page.(MonitorPage).Body, &resp)
@@ -121,7 +131,7 @@
 	}
 
 	var res struct {
-		Monitor *Monitor `json:"health_monitor" mapstructure:"health_monitor"`
+		Monitor *Monitor `json:"healthmonitor" mapstructure:"healthmonitor"`
 	}
 
 	err := mapstructure.Decode(r.Body, &res)
diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go
index 5446a2d..a3abba4 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go
@@ -4,7 +4,7 @@
 
 const (
 	rootPath     = "lbaas"
-	resourcePath = "health_monitors"
+	resourcePath = "healthmonitors"
 )
 
 func rootURL(c *gophercloud.ServiceClient) string {
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/pools/fixtures.go
new file mode 100644
index 0000000..107ed14
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas_v2/pools/fixtures.go
@@ -0,0 +1,388 @@
+// +build fixtures
+package pools
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/rackspace/gophercloud/testhelper/client"
+)
+
+// PoolsListBody contains the canned body of a pool list response.
+const PoolsListBody = `
+{
+	"pools":[
+	         {
+			"lb_algorithm":"ROUND_ROBIN",
+			"protocol":"HTTP",
+			"description":"",
+			"healthmonitor_id": "466c8345-28d8-4f84-a246-e04380b0461d",
+			"members":[{"id": "53306cda-815d-4354-9fe4-59e09da9c3c5"}],
+			"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
+			"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+			"id":"72741b06-df4d-4715-b142-276b6bce75ab",
+			"name":"web",
+			"admin_state_up":true,
+			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+			"provider": "haproxy"
+		},
+		{
+			"lb_algorithm":"LEAST_CONNECTION",
+			"protocol":"HTTP",
+			"description":"",
+			"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
+			"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
+			"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
+			"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+			"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
+			"name":"db",
+			"admin_state_up":true,
+			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+			"provider": "haproxy"
+		}
+	]
+}
+`
+
+// SinglePoolBody is the canned body of a Get request on an existing pool.
+const SinglePoolBody = `
+{
+	"pool": {
+		"lb_algorithm":"LEAST_CONNECTION",
+		"protocol":"HTTP",
+		"description":"",
+		"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
+		"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
+		"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
+		"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+		"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
+		"name":"db",
+		"admin_state_up":true,
+		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+		"provider": "haproxy"
+	}
+}
+`
+
+// PostUpdatePoolBody is the canned response body of a Update request on an existing pool.
+const PostUpdatePoolBody = `
+{
+	"pool": {
+		"lb_algorithm":"LEAST_CONNECTION",
+		"protocol":"HTTP",
+		"description":"",
+		"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
+		"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
+		"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
+		"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
+		"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
+		"name":"db",
+		"admin_state_up":true,
+		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+		"provider": "haproxy"
+	}
+}
+`
+
+var (
+	PoolWeb = Pool{
+		LBMethod:      "ROUND_ROBIN",
+		Protocol:      "HTTP",
+		Description:   "",
+		MonitorID:     "466c8345-28d8-4f84-a246-e04380b0461d",
+		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+		AdminStateUp:  true,
+		Name:          "web",
+		Members:       []Member{{ID: "53306cda-815d-4354-9fe4-59e09da9c3c5"}},
+		ID:            "72741b06-df4d-4715-b142-276b6bce75ab",
+		Loadbalancers: []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
+		Listeners:     []ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
+		Provider:      "haproxy",
+	}
+	PoolDb = Pool{
+		LBMethod:      "LEAST_CONNECTION",
+		Protocol:      "HTTP",
+		Description:   "",
+		MonitorID:     "5f6c8345-28d8-4f84-a246-e04380b0461d",
+		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+		AdminStateUp:  true,
+		Name:          "db",
+		Members:       []Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}},
+		ID:            "c3741b06-df4d-4715-b142-276b6bce75ab",
+		Loadbalancers: []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
+		Listeners:     []ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
+		Provider:      "haproxy",
+	}
+	PoolUpdated = Pool{
+		LBMethod:      "LEAST_CONNECTION",
+		Protocol:      "HTTP",
+		Description:   "",
+		MonitorID:     "5f6c8345-28d8-4f84-a246-e04380b0461d",
+		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+		AdminStateUp:  true,
+		Name:          "db",
+		Members:       []Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}},
+		ID:            "c3741b06-df4d-4715-b142-276b6bce75ab",
+		Loadbalancers: []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
+		Listeners:     []ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
+		Provider:      "haproxy",
+	}
+)
+
+// HandlePoolListSuccessfully sets up the test server to respond to a pool List request.
+func HandlePoolListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, PoolsListBody)
+		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
+			fmt.Fprintf(w, `{ "pools": [] }`)
+		default:
+			t.Fatalf("/v2.0/lbaas/pools invoked with unexpected marker=[%s]", marker)
+		}
+	})
+}
+
+// HandlePoolCreationSuccessfully sets up the test server to respond to a pool creation request
+// with a given response.
+func HandlePoolCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"pool": {
+			        "lb_algorithm": "ROUND_ROBIN",
+			        "protocol": "HTTP",
+			        "name": "Example pool",
+			        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+			        "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab"
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandlePoolGetSuccessfully sets up the test server to respond to a pool Get request.
+func HandlePoolGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SinglePoolBody)
+	})
+}
+
+// HandlePoolDeletionSuccessfully sets up the test server to respond to a pool deletion request.
+func HandlePoolDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandlePoolUpdateSuccessfully sets up the test server to respond to a pool Update request.
+func HandlePoolUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `{
+			"pool": {
+				"name": "NewPoolName",
+                                "lb_algorithm": "LEAST_CONNECTIONS"
+			}
+		}`)
+
+		fmt.Fprintf(w, PostUpdatePoolBody)
+	})
+}
+
+// MembersListBody contains the canned body of a member list response.
+const MembersListBody = `
+{
+	"members":[
+		{
+			"id": "2a280670-c202-4b0b-a562-34077415aabf",
+			"address": "10.0.2.10",
+			"weight": 5,
+			"name": "web",
+			"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+			"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+			"admin_state_up":true,
+			"protocol_port": 80
+		},
+		{
+			"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
+			"address": "10.0.2.11",
+			"weight": 10,
+			"name": "db",
+			"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+			"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+			"admin_state_up":false,
+			"protocol_port": 80
+		}
+	]
+}
+`
+
+// SingleMemberBody is the canned body of a Get request on an existing member.
+const SingleMemberBody = `
+{
+	"member": {
+		"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
+		"address": "10.0.2.11",
+		"weight": 10,
+		"name": "db",
+		"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+		"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+		"admin_state_up":false,
+		"protocol_port": 80
+	}
+}
+`
+
+// PostUpdateMemberBody is the canned response body of a Update request on an existing member.
+const PostUpdateMemberBody = `
+{
+	"member": {
+		"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
+		"address": "10.0.2.11",
+		"weight": 10,
+		"name": "db",
+		"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+		"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+		"admin_state_up":false,
+		"protocol_port": 80
+	}
+}
+`
+
+var (
+	MemberWeb = Member{
+		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
+		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
+		AdminStateUp: true,
+		Name:         "web",
+		ID:           "2a280670-c202-4b0b-a562-34077415aabf",
+		Address:      "10.0.2.10",
+		Weight:       5,
+		ProtocolPort: 80,
+	}
+	MemberDb = Member{
+		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
+		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
+		AdminStateUp: false,
+		Name:         "db",
+		ID:           "fad389a3-9a4a-4762-a365-8c7038508b5d",
+		Address:      "10.0.2.11",
+		Weight:       10,
+		ProtocolPort: 80,
+	}
+	MemberUpdated = Member{
+		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
+		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
+		AdminStateUp: false,
+		Name:         "db",
+		ID:           "fad389a3-9a4a-4762-a365-8c7038508b5d",
+		Address:      "10.0.2.11",
+		Weight:       10,
+		ProtocolPort: 80,
+	}
+)
+
+// HandleMemberListSuccessfully sets up the test server to respond to a member List request.
+func HandleMemberListSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, MembersListBody)
+		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
+			fmt.Fprintf(w, `{ "members": [] }`)
+		default:
+			t.Fatalf("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members invoked with unexpected marker=[%s]", marker)
+		}
+	})
+}
+
+// HandleMemberCreationSuccessfully sets up the test server to respond to a member creation request
+// with a given response.
+func HandleMemberCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"member": {
+			        "address": "10.0.2.11",
+			        "weight": 10,
+			        "name": "db",
+			        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
+			        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
+			        "protocol_port": 80
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
+// HandleMemberGetSuccessfully sets up the test server to respond to a member Get request.
+func HandleMemberGetSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleMemberBody)
+	})
+}
+
+// HandleMemberDeletionSuccessfully sets up the test server to respond to a member deletion request.
+func HandleMemberDeletionSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+}
+
+// HandleMemberUpdateSuccessfully sets up the test server to respond to a member Update request.
+func HandleMemberUpdateSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestJSONRequest(t, r, `{
+			"member": {
+				"name": "newMemberName",
+                                "weight": 4
+			}
+		}`)
+
+		fmt.Fprintf(w, PostUpdateMemberBody)
+	})
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go
index d61e4ae..706ce96 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go
@@ -1,6 +1,7 @@
 package pools
 
 import (
+	"fmt"
 	"github.com/rackspace/gophercloud"
 	"github.com/rackspace/gophercloud/pagination"
 )
@@ -9,6 +10,42 @@
 // operations. It is recommended that users use the `Up` and `Down` enums.
 type AdminState *bool
 
+type poolOpts struct {
+	// Only required if the caller has an admin role and wants to create a pool
+	// for another tenant.
+	TenantID string
+
+	// Optional. Name of the pool.
+	Name string
+
+	// Optional. Human-readable description for the pool.
+	Description string
+
+	// Required. The protocol used by the pool members, you can use either
+	// ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS.
+	Protocol Protocol
+
+	// The Loadbalancer on which the members of the pool will be associated with.
+	// Note:  one of LoadbalancerID or ListenerID must be provided.
+	LoadbalancerID string
+
+	// The Listener on which the members of the pool will be associated with.
+	// Note:  one of LoadbalancerID or ListenerID must be provided.
+	ListenerID string
+
+	// Required. The algorithm used to distribute load between the members of the pool. The
+	// current specification supports LBMethodRoundRobin, LBMethodLeastConnections
+	// and LBMethodSourceIp as valid values for this attribute.
+	LBMethod LBMethod
+
+	// Optional. Omit this field to prevent session persistence.
+	Persistence *SessionPersistence
+
+	// Optional. The administrative state of the Pool. A valid value is true (UP)
+	// or false (DOWN).
+	AdminStateUp *bool
+}
+
 // Convenience vars for AdminStateUp values.
 var (
 	iTrue  = true
@@ -18,6 +55,12 @@
 	Down AdminState = &iFalse
 )
 
+// ListOptsBuilder allows extensions to add additional parameters to the
+// List request.
+type ListOptsBuilder interface {
+	ToPoolListQuery() (string, error)
+}
+
 // ListOpts allows the filtering and sorting of paginated collections through
 // the API. Filtering is achieved by passing in struct field values that map to
 // the Pool attributes you want to see returned. SortKey allows you to
@@ -26,124 +69,134 @@
 type ListOpts struct {
 	LBMethod       string `q:"lb_algorithm"`
 	Protocol       string `q:"protocol"`
-	SubnetID       string `q:"subnet_id"`
 	TenantID       string `q:"tenant_id"`
 	AdminStateUp   *bool  `q:"admin_state_up"`
 	Name           string `q:"name"`
 	ID             string `q:"id"`
 	LoadbalancerID string `q:"loadbalancer_id"`
+	ListenerID     string `q:"listener_id"`
 	Limit          int    `q:"limit"`
 	Marker         string `q:"marker"`
 	SortKey        string `q:"sort_key"`
 	SortDir        string `q:"sort_dir"`
 }
 
+// ToPoolListQuery formats a ListOpts into a query string.
+func (opts ListOpts) ToPoolListQuery() (string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return "", err
+	}
+	return q.String(), nil
+}
+
 // List returns a Pager which allows you to iterate over a collection of
 // pools. It accepts a ListOpts struct, which allows you to filter and sort
 // the returned collection for greater efficiency.
 //
 // Default policy settings return only those pools that are owned by the
 // tenant who submits the request, unless an admin user submits the request.
-func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
-	q, err := gophercloud.BuildQueryString(&opts)
-	if err != nil {
-		return pagination.Pager{Err: err}
+func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
+	url := rootURL(c)
+	if opts != nil {
+		query, err := opts.ToPoolListQuery()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		url += query
 	}
-	u := rootURL(c) + q.String()
-	return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
+
+	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		return PoolPage{pagination.LinkedPageBase{PageResult: r}}
 	})
 }
 
+type LBMethod string
+type Protocol string
+
 // Supported attributes for create/update operations.
 const (
-	LBMethodRoundRobin       = "ROUND_ROBIN"
-	LBMethodLeastConnections = "LEAST_CONNECTIONS"
-	LBMethodSourceIp         = "SOURCE_IP"
+	LBMethodRoundRobin       LBMethod = "ROUND_ROBIN"
+	LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS"
+	LBMethodSourceIp         LBMethod = "SOURCE_IP"
 
-	ProtocolTCP   = "TCP"
-	ProtocolHTTP  = "HTTP"
-	ProtocolHTTPS = "HTTPS"
+	ProtocolTCP   Protocol = "TCP"
+	ProtocolHTTP  Protocol = "HTTP"
+	ProtocolHTTPS Protocol = "HTTPS"
 )
 
-// CreateOpts contains all the values needed to create a new pool.
-type CreateOpts struct {
-	// Only required if the caller has an admin role and wants to create a pool
-	// for another tenant.
-	TenantID string
+var (
+	errLoadbalancerOrListenerRequired = fmt.Errorf("A ListenerID or LoadbalancerID is required")
+	errValidLBMethodRequired          = fmt.Errorf("A valid LBMethod is required. Supported values are ROUND_ROBIN, LEAST_CONNECTIONS, SOURCE_IP")
+	errValidProtocolRequired          = fmt.Errorf("A valid Protocol is required. Supported values are TCP, HTTP, HTTPS")
+)
 
-	// Optional. The network on which the members of the pool will be located.
-	// Only members that are on this network can be added to the pool.
-	SubnetID string
+// CreateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the main Create operation in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type CreateOptsBuilder interface {
+	ToPoolCreateMap() (map[string]interface{}, error)
+}
 
-	// Optional. Name of the pool.
-	Name string
+// CreateOpts is the common options struct used in this package's Create
+// operation.
+type CreateOpts poolOpts
 
-	// Optional. Human-readable description for the pool.
-	Description string
+// ToPoolCreateMap casts a CreateOpts struct to a map.
+func (opts CreateOpts) ToPoolCreateMap() (map[string]interface{}, error) {
+	l := make(map[string]interface{})
+	allowedLBMethod := map[LBMethod]bool{LBMethodRoundRobin: true, LBMethodLeastConnections: true, LBMethodSourceIp: true}
+	allowedProtocol := map[Protocol]bool{ProtocolTCP: true, ProtocolHTTP: true, ProtocolHTTPS: true}
 
-	// Required. The protocol used by the pool members, you can use either
-	// ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS.
-	Protocol string
+	if allowedLBMethod[opts.LBMethod] {
+		l["lb_algorithm"] = opts.LBMethod
+	} else {
+		return nil, errValidLBMethodRequired
+	}
+	if allowedProtocol[opts.Protocol] {
+		l["protocol"] = opts.Protocol
+	} else {
+		return nil, errValidProtocolRequired
+	}
+	if opts.LoadbalancerID == "" && opts.ListenerID == "" {
+		return nil, errLoadbalancerOrListenerRequired
+	} else {
+		if opts.LoadbalancerID != "" {
+			l["loadbalancer_id"] = opts.LoadbalancerID
+		}
+		if opts.ListenerID != "" {
+			l["listener_id"] = opts.ListenerID
+		}
+	}
+	if opts.AdminStateUp != nil {
+		l["admin_state_up"] = &opts.AdminStateUp
+	}
+	if opts.Name != "" {
+		l["name"] = opts.Name
+	}
+	if opts.TenantID != "" {
+		l["tenant_id"] = opts.TenantID
+	}
+	if opts.Persistence != nil {
+		l["session_persistence"] = &opts.Persistence
+	}
 
-	// The Loadbalancer on which the members of the pool will be associated with.
-	// Note:  one of LoadbalancerID or ListenerID must be provided.
-	LoadbalancerID string
-
-	// The Listener on which the members of the pool will be associated with.
-	// Note:  one of LoadbalancerID or ListenerID must be provided.
-	ListenerID string
-
-	// The algorithm used to distribute load between the members of the pool. The
-	// current specification supports LBMethodRoundRobin, LBMethodLeastConnections
-	// and LBMethodSourceIp as valid values for this attribute.
-	LBMethod string
-
-	// Optional. Omit this field to prevent session persistence.
-	Persistence *SessionPersistence
-
-	// Optional. The administrative state of the Listener. A valid value is true (UP)
-	// or false (DOWN).
-	AdminStateUp *bool
+	return map[string]interface{}{"pool": l}, nil
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new
 // load balancer pool.
 func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
-	type pool struct {
-		Name           string              `json:"name,omitempty"`
-		Description    string              `json:"description,omitempty"`
-		TenantID       string              `json:"tenant_id,omitempty"`
-		SubnetID       string              `json:"subnet_id,omitempty"`
-		Protocol       string              `json:"protocol"`
-		LoadbalancerID string              `json:"loadbalancer_id,omitempty"`
-		ListenerID     string              `json:"listener_id,omitempty"`
-		LBMethod       string              `json:"lb_algorithm"`
-		Persistence    *SessionPersistence `json:"session_persistence,omitempty"`
-		AdminStateUp   *bool               `json:"admin_state_up,omitempty"`
-	}
-
-	type request struct {
-		Pool pool `json:"pool"`
-	}
-
-	reqBody := request{Pool: pool{
-		Name:           opts.Name,
-		Description:    opts.Description,
-		TenantID:       opts.TenantID,
-		SubnetID:       opts.SubnetID,
-		Protocol:       opts.Protocol,
-		LoadbalancerID: opts.LoadbalancerID,
-		ListenerID:     opts.ListenerID,
-		LBMethod:       opts.LBMethod,
-		AdminStateUp:   opts.AdminStateUp,
-	}}
-
-	if opts.Persistence != nil {
-		reqBody.Pool.Persistence = opts.Persistence
-	}
-
 	var res CreateResult
+
+	reqBody, err := opts.ToPoolCreateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	// Send request to API
 	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 	return res
 }
@@ -155,45 +208,54 @@
 	return res
 }
 
-// UpdateOpts contains the values used when updating a pool.
-type UpdateOpts struct {
-	// Optional. Name of the pool.
-	Name string
+// UpdateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the main Update operation in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type UpdateOptsBuilder interface {
+	ToPoolUpdateMap() (map[string]interface{}, error)
+}
 
-	// Optional. Human-readable description for the pool.
-	Description string
+// UpdateOpts is the common options struct used in this package's Update
+// operation.
+type UpdateOpts poolOpts
 
-	// The algorithm used to distribute load between the members of the pool. The
-	// current specification supports LBMethodRoundRobin, LBMethodLeastConnections
-	// and LBMethodSourceIp as valid values for this attribute.
-	LBMethod string
+// ToPoolUpdateMap casts a UpdateOpts struct to a map.
+func (opts UpdateOpts) ToPoolUpdateMap() (map[string]interface{}, error) {
+	l := make(map[string]interface{})
+	allowedLBMethod := map[LBMethod]bool{LBMethodRoundRobin: true, LBMethodLeastConnections: true, LBMethodSourceIp: true}
 
-	// Optional. The administrative state of the Listener. A valid value is true (UP)
-	// or false (DOWN).
-	AdminStateUp *bool
+	if opts.LBMethod != "" {
+		if allowedLBMethod[opts.LBMethod] {
+			l["lb_algorithm"] = opts.LBMethod
+		} else {
+			return nil, errValidLBMethodRequired
+		}
+	}
+	if opts.Name != "" {
+		l["name"] = opts.Name
+	}
+	if opts.Description != "" {
+		l["description"] = opts.Description
+	}
+	if opts.AdminStateUp != nil {
+		l["admin_state_up"] = &opts.AdminStateUp
+	}
+
+	return map[string]interface{}{"pool": l}, nil
 }
 
 // Update allows pools to be updated.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
-	type pool struct {
-		Name         string `json:"name,omitempty"`
-		Description  string `json:"description,omitempty"`
-		LBMethod     string `json:"lb_algorithm,omitempty"`
-		AdminStateUp *bool  `json:"admin_state_up,omitempty"`
-	}
-	type request struct {
-		Pool pool `json:"pool"`
-	}
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
+	var res UpdateResult
 
-	reqBody := request{Pool: pool{
-		Name:         opts.Name,
-		Description:  opts.Description,
-		LBMethod:     opts.LBMethod,
-		AdminStateUp: opts.AdminStateUp,
-	}}
+	reqBody, err := opts.ToPoolUpdateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
 
 	// Send request to API
-	var res UpdateResult
 	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
@@ -207,45 +269,8 @@
 	return res
 }
 
-// MemberListOpts allows the filtering and sorting of paginated collections through
-// the API. Filtering is achieved by passing in struct field values that map to
-// the Member attributes you want to see returned. SortKey allows you to
-// sort by a particular Member attribute. SortDir sets the direction, and is
-// either `asc' or `desc'. Marker and Limit are used for pagination.
-type MemberListOpts struct {
-	Name         string `q:"name"`
-	Weight       int    `q:"weight"`
-	AdminStateUp *bool  `q:"admin_state_up"`
-	TenantID     string `q:"tenant_id"`
-	SubnetID     string `q:"subnet_id"`
-	Address      string `q:"address"`
-	ProtocolPort int    `q:"protocol_port"`
-	ID           string `q:"id"`
-	Limit        int    `q:"limit"`
-	Marker       string `q:"marker"`
-	SortKey      string `q:"sort_key"`
-	SortDir      string `q:"sort_dir"`
-}
-
-// List returns a Pager which allows you to iterate over a collection of
-// members. It accepts a ListOpts struct, which allows you to filter and sort
-// the returned collection for greater efficiency.
-//
-// Default policy settings return only those members that are owned by the
-// tenant who submits the request, unless an admin user submits the request.
-func ListAssociateMembers(c *gophercloud.ServiceClient, poolID string, opts MemberListOpts) pagination.Pager {
-	q, err := gophercloud.BuildQueryString(&opts)
-	if err != nil {
-		return pagination.Pager{Err: err}
-	}
-	u := memberRootURL(c, poolID) + q.String()
-	return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
-		return MemberPage{pagination.LinkedPageBase{PageResult: r}}
-	})
-}
-
 // CreateOpts contains all the values needed to create a new Member for a Pool.
-type MemberCreateOpts struct {
+type memberOpts struct {
 	// Optional. Name of the Member.
 	Name string
 
@@ -269,37 +294,132 @@
 	// parameter value for the subnet UUID.
 	SubnetID string
 
-	// Optional. The administrative state of the Listener. A valid value is true (UP)
+	// Optional. The administrative state of the Pool. A valid value is true (UP)
 	// or false (DOWN).
 	AdminStateUp *bool
 }
 
+// MemberListOptsBuilder allows extensions to add additional parameters to the
+// Member List request.
+type MemberListOptsBuilder interface {
+	ToMemberListQuery() (string, error)
+}
+
+// MemberListOpts allows the filtering and sorting of paginated collections through
+// the API. Filtering is achieved by passing in struct field values that map to
+// the Member attributes you want to see returned. SortKey allows you to
+// sort by a particular Member attribute. SortDir sets the direction, and is
+// either `asc' or `desc'. Marker and Limit are used for pagination.
+type MemberListOpts struct {
+	Name         string `q:"name"`
+	Weight       int    `q:"weight"`
+	AdminStateUp *bool  `q:"admin_state_up"`
+	TenantID     string `q:"tenant_id"`
+	Address      string `q:"address"`
+	ProtocolPort int    `q:"protocol_port"`
+	ID           string `q:"id"`
+	Limit        int    `q:"limit"`
+	Marker       string `q:"marker"`
+	SortKey      string `q:"sort_key"`
+	SortDir      string `q:"sort_dir"`
+}
+
+// ToMemberListQuery formats a ListOpts into a query string.
+func (opts MemberListOpts) ToMemberListQuery() (string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return "", err
+	}
+	return q.String(), nil
+}
+
+// List returns a Pager which allows you to iterate over a collection of
+// members. It accepts a ListOpts struct, which allows you to filter and sort
+// the returned collection for greater efficiency.
+//
+// Default policy settings return only those members that are owned by the
+// tenant who submits the request, unless an admin user submits the request.
+func ListAssociateMembers(c *gophercloud.ServiceClient, poolID string, opts MemberListOptsBuilder) pagination.Pager {
+	url := memberRootURL(c, poolID)
+	if opts != nil {
+		query, err := opts.ToMemberListQuery()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		url += query
+	}
+
+	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
+		return MemberPage{pagination.LinkedPageBase{PageResult: r}}
+	})
+}
+
+var (
+	errPoolIdRequired       = fmt.Errorf("PoolID is required")
+	errAddressRequired      = fmt.Errorf("Address is required")
+	errProtocolPortRequired = fmt.Errorf("ProtocolPort is required")
+)
+
+// MemberCreateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the main Create operation in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type MemberCreateOptsBuilder interface {
+	ToMemberCreateMap() (map[string]interface{}, error)
+}
+
+// MemberCreateOpts is the common options struct used in this package's Create
+// operation.
+type MemberCreateOpts memberOpts
+
+// ToMemberCreateMap casts a CreateOpts struct to a map.
+func (opts MemberCreateOpts) ToMemberCreateMap() (map[string]interface{}, error) {
+	l := make(map[string]interface{})
+
+	if opts.Address != "" {
+		l["address"] = opts.Address
+	} else {
+		return nil, errAddressRequired
+	}
+	if opts.ProtocolPort != 0 {
+		l["protocol_port"] = opts.ProtocolPort
+	} else {
+		return nil, errProtocolPortRequired
+	}
+	if opts.AdminStateUp != nil {
+		l["admin_state_up"] = &opts.AdminStateUp
+	}
+	if opts.Name != "" {
+		l["name"] = opts.Name
+	}
+	if opts.TenantID != "" {
+		l["tenant_id"] = opts.TenantID
+	}
+	if opts.SubnetID != "" {
+		l["subnet_id"] = opts.SubnetID
+	}
+	if opts.Weight != 0 {
+		l["weight"] = opts.Weight
+	}
+
+	return map[string]interface{}{"member": l}, nil
+}
+
 // CreateAssociateMember will create and associate a Member with a particular Pool.
 func CreateAssociateMember(c *gophercloud.ServiceClient, poolID string, opts MemberCreateOpts) AssociateResult {
-	type member struct {
-		Name         string `json:"name,omitempty"`
-		TenantID     string `json:"tenant_id,omitempty"`
-		Address      string `json:"address,omitempty"`
-		ProtocolPort int    `json:"protocol_port,omitempty"`
-		Weight       int    `json:"weight,omitempty"`
-		SubnetID     string `json:"subnet_id,omitempty"`
-		AdminStateUp *bool  `json:"admin_state_up,omitempty"`
-	}
-	type request struct {
-		Member member `json:"member"`
-	}
-
-	reqBody := request{Member: member{
-		Name:         opts.Name,
-		TenantID:     opts.TenantID,
-		Address:      opts.Address,
-		ProtocolPort: opts.ProtocolPort,
-		Weight:       opts.Weight,
-		SubnetID:     opts.SubnetID,
-		AdminStateUp: opts.AdminStateUp,
-	}}
-
 	var res AssociateResult
+
+	if poolID == "" {
+		res.Err = errPoolIdRequired
+		return res
+	}
+
+	reqBody, err := opts.ToMemberCreateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
 	_, res.Err = c.Post(memberRootURL(c, poolID), reqBody, &res.Body, nil)
 	return res
 }
@@ -311,40 +431,46 @@
 	return res
 }
 
-// UpdateOpts contains the values used when updating a Pool Member.
-type MemberUpdateOpts struct {
-	// Name of the Member.
-	Name string
+// MemberUpdateOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the main Update operation in this package. Since many
+// extensions decorate or modify the common logic, it is useful for them to
+// satisfy a basic interface in order for them to be used.
+type MemberUpdateOptsBuilder interface {
+	ToMemberUpdateMap() (map[string]interface{}, error)
+}
 
-	// A positive integer value that indicates the relative portion of
-	// traffic that this member should receive from the pool. For example, a
-	// member with a weight of 10 receives five times as much traffic as a member
-	// with a weight of 2.
-	Weight int
+// UpdateOpts is the common options struct used in this package's Update
+// operation.
+type MemberUpdateOpts memberOpts
 
-	// The administrative state of the member, which is up (true) or down (false).
-	AdminStateUp *bool
+// ToMemberUpdateMap casts a UpdateOpts struct to a map.
+func (opts MemberUpdateOpts) ToMemberUpdateMap() (map[string]interface{}, error) {
+	l := make(map[string]interface{})
+
+	if opts.AdminStateUp != nil {
+		l["admin_state_up"] = &opts.AdminStateUp
+	}
+	if opts.Name != "" {
+		l["name"] = opts.Name
+	}
+	if opts.Weight != 0 {
+		l["weight"] = opts.Weight
+	}
+
+	return map[string]interface{}{"member": l}, nil
 }
 
 // Update allows Member to be updated.
 func UpdateAssociateMember(c *gophercloud.ServiceClient, poolID string, memberID string, opts MemberUpdateOpts) UpdateResult {
-	type member struct {
-		Name         string `json:"name,omitempty"`
-		Weight       int    `json:"weight,omitempty"`
-		AdminStateUp *bool  `json:"admin_state_up,omitempty"`
-	}
-	type request struct {
-		Member member `json:"member"`
-	}
+	var res UpdateResult
 
-	reqBody := request{Member: member{
-		Name:         opts.Name,
-		Weight:       opts.Weight,
-		AdminStateUp: opts.AdminStateUp,
-	}}
+	reqBody, err := opts.ToMemberUpdateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
 
 	// Send request to API
-	var res UpdateResult
 	_, res.Err = c.Put(memberResourceURL(c, poolID, memberID), reqBody, &res.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 201, 202},
 	})
@@ -352,8 +478,8 @@
 }
 
 // DisassociateMember will remove and disassociate a Member from a particular Pool.
-func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) AssociateResult {
-	var res AssociateResult
+func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) DeleteResult {
+	var res DeleteResult
 	_, res.Err = c.Delete(memberResourceURL(c, poolID, memberID), nil)
 	return res
 }
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests_test.go
index 5ee8762..288637d 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests_test.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests_test.go
@@ -1,8 +1,6 @@
 package pools
 
 import (
-	"fmt"
-	"net/http"
 	"testing"
 
 	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
@@ -17,478 +15,241 @@
 	th.AssertEquals(t, th.Endpoint()+"v2.0/lbaas/pools", rootURL(fake.ServiceClient()))
 }
 
-func TestList(t *testing.T) {
+func TestListPools(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandlePoolListSuccessfully(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+	pages := 0
+	err := List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
 
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "pools":[
-      {
-         "lb_algorithm":"ROUND_ROBIN",
-         "protocol":"HTTP",
-         "description":"",
-         "health_monitors":[
-            "466c8345-28d8-4f84-a246-e04380b0461d",
-            "5d4b5228-33b0-4e60-b225-9b727c1a20e7"
-         ],
-         "members":[{"id": "53306cda-815d-4354-9fe4-59e09da9c3c5"}],
-         "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
-         "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
-         "id":"72741b06-df4d-4715-b142-276b6bce75ab",
-         "name":"app_pool",
-         "admin_state_up":true,
-         "subnet_id":"8032909d-47a1-4715-90af-5153ffe39861",
-         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-         "provider": "haproxy"
-      }
-   ]
-}
-			`)
-	})
-
-	count := 0
-
-	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
 		actual, err := ExtractPools(page)
 		if err != nil {
-			t.Errorf("Failed to extract pools: %v", err)
 			return false, err
 		}
 
-		expected := []Pool{
-			{
-				LBMethod:    "ROUND_ROBIN",
-				Protocol:    "HTTP",
-				Description: "",
-				MonitorIDs: []string{
-					"466c8345-28d8-4f84-a246-e04380b0461d",
-					"5d4b5228-33b0-4e60-b225-9b727c1a20e7",
-				},
-				SubnetID:      "8032909d-47a1-4715-90af-5153ffe39861",
-				TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
-				AdminStateUp:  true,
-				Name:          "app_pool",
-				Members:       []map[string]interface{}{{"id": "53306cda-815d-4354-9fe4-59e09da9c3c5"}},
-				ID:            "72741b06-df4d-4715-b142-276b6bce75ab",
-				Loadbalancers: []map[string]interface{}{{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}},
-				Listeners:     []map[string]interface{}{{"id": "2a280670-c202-4b0b-a562-34077415aabf"}},
-				Provider:      "haproxy",
-			},
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 pools, got %d", len(actual))
 		}
-
-		th.CheckDeepEquals(t, expected, actual)
+		th.CheckDeepEquals(t, PoolWeb, actual[0])
+		th.CheckDeepEquals(t, PoolDb, actual[1])
 
 		return true, nil
 	})
 
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
+	th.AssertNoErr(t, err)
+
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
 	}
 }
 
-func TestCreate(t *testing.T) {
+func TestListAllPools(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandlePoolListSuccessfully(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "pool": {
-        "lb_algorithm": "ROUND_ROBIN",
-        "protocol": "HTTP",
-        "name": "Example pool",
-        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-        "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab"
-    }
+	allPages, err := List(fake.ServiceClient(), ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := ExtractPools(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, PoolWeb, actual[0])
+	th.CheckDeepEquals(t, PoolDb, actual[1])
 }
-			`)
 
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
+func TestCreatePool(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePoolCreationSuccessfully(t, SinglePoolBody)
 
-		fmt.Fprintf(w, `
-{
-    "pool": {
-        "lb_algorithm": "ROUND_ROBIN",
-        "protocol": "HTTP",
-        "description": "",
-        "health_monitors": [],
-        "members": [{}],
-        "id": "69055154-f603-4a28-8951-7cc2d9e54a9a",
-        "name": "Example pool",
-        "admin_state_up": true,
-        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-        "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
-        "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}]
-    }
-}
-		`)
-	})
-
-	options := CreateOpts{
+	actual, err := Create(fake.ServiceClient(), CreateOpts{
 		LBMethod:       LBMethodRoundRobin,
 		Protocol:       "HTTP",
 		Name:           "Example pool",
-		SubnetID:       "1981f108-3c48-48d2-b908-30f7d28532c9",
 		TenantID:       "2ffc6e22aae24e4795f87155d24c896f",
 		LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab",
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, PoolDb, *actual)
+}
+
+func TestGetPool(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePoolGetSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := Get(client, "c3741b06-df4d-4715-b142-276b6bce75ab").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
 	}
-	p, err := Create(fake.ServiceClient(), options).Extract()
-	th.AssertNoErr(t, err)
 
-	th.AssertEquals(t, "ROUND_ROBIN", p.LBMethod)
-	th.AssertEquals(t, "HTTP", p.Protocol)
-	th.AssertEquals(t, "", p.Description)
-	th.AssertDeepEquals(t, []string{}, p.MonitorIDs)
-	th.AssertDeepEquals(t, []map[string]interface{}{{}}, p.Members)
-	th.AssertEquals(t, "69055154-f603-4a28-8951-7cc2d9e54a9a", p.ID)
-	th.AssertEquals(t, "79e05663-7f03-45d2-a092-8b94062f22ab", p.Loadbalancers[0]["id"])
-	th.AssertEquals(t, "Example pool", p.Name)
-	th.AssertEquals(t, "1981f108-3c48-48d2-b908-30f7d28532c9", p.SubnetID)
-	th.AssertEquals(t, "2ffc6e22aae24e4795f87155d24c896f", p.TenantID)
+	th.CheckDeepEquals(t, PoolDb, *actual)
 }
 
-func TestGet(t *testing.T) {
+func TestDeletePool(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandlePoolDeletionSuccessfully(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "pool":{
-      "id":"332abe93-f488-41ba-870b-2ac66be7f853",
-      "tenant_id":"19eaa775-cf5d-49bc-902e-2f85f668d995",
-      "name":"Example pool",
-      "description":"",
-      "protocol":"tcp",
-      "lb_algorithm":"ROUND_ROBIN",
-      "session_persistence":{
-      },
-      "members":[{}],
-      "admin_state_up":true
-   }
-}
-			`)
-	})
-
-	n, err := Get(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853").Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.ID, "332abe93-f488-41ba-870b-2ac66be7f853")
-}
-
-func TestUpdate(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-   "pool":{
-      "name": "SuperPool",
-      "lb_algorithm": "LEAST_CONNECTIONS"
-   }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "pool":{
-      "lb_algorithm":"LEAST_CONNECTIONS",
-      "protocol":"TCP",
-      "description":"",
-      "health_monitors":[],
-      "subnet_id":"8032909d-47a1-4715-90af-5153ffe39861",
-      "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
-      "admin_state_up":true,
-      "name":"SuperPool",
-      "members":[{}],
-      "id":"61b1f87a-7a21-4ad3-9dda-7f81d249944f"
-   }
-}
-		`)
-	})
-
-	options := UpdateOpts{Name: "SuperPool", LBMethod: LBMethodLeastConnections}
-
-	n, err := Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "SuperPool", n.Name)
-	th.AssertDeepEquals(t, "LEAST_CONNECTIONS", n.LBMethod)
-}
-
-func TestDelete(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
-
-	res := Delete(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853")
+	res := Delete(fake.ServiceClient(), "c3741b06-df4d-4715-b142-276b6bce75ab")
 	th.AssertNoErr(t, res.Err)
 }
 
-func TestListAssociateMembers(t *testing.T) {
+func TestUpdatePool(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandlePoolUpdateSuccessfully(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+	client := fake.ServiceClient()
+	actual, err := Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", UpdateOpts{
+		Name:     "NewPoolName",
+		LBMethod: LBMethodLeastConnections,
+	}).Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Update error: %v", err)
+	}
 
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "members":[
-      {
-        "id": "2a280670-c202-4b0b-a562-34077415aabf",
-        "address": "10.0.2.10",
-        "weight": 5,
-        "name": "member1",
-        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-        "admin_state_up":true,
-        "protocol_port": 80
-      },
-      {
-        "id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
-        "address": "10.0.2.11",
-        "weight": 10,
-        "name": "member2",
-        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-        "admin_state_up":false,
-        "protocol_port": 80
-      }
-   ]
+	th.CheckDeepEquals(t, PoolUpdated, *actual)
 }
-			`)
-	})
 
-	count := 0
+func TestRequiredPoolCreateOpts(t *testing.T) {
+	res := Create(fake.ServiceClient(), CreateOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
+	}
+	res = Create(fake.ServiceClient(), CreateOpts{LBMethod: LBMethod("invalid"), Protocol: ProtocolHTTPS, LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a"})
+	if res.Err == nil || res.Err != errValidLBMethodRequired {
+		t.Fatalf("Expected '%s' error, but got '%s'", errValidLBMethodRequired, res.Err)
+	}
+	res = Create(fake.ServiceClient(), CreateOpts{LBMethod: LBMethodRoundRobin, Protocol: Protocol("invalid"), LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a"})
+	if res.Err == nil || res.Err != errValidProtocolRequired {
+		t.Fatalf("Expected '%s' error, but got '%s'", errValidProtocolRequired, res.Err)
+	}
+	res = Create(fake.ServiceClient(), CreateOpts{LBMethod: LBMethodRoundRobin, Protocol: ProtocolHTTPS})
+	if res.Err == nil || res.Err != errLoadbalancerOrListenerRequired {
+		t.Fatalf("Expected '%s' error, but got '%s'", errLoadbalancerOrListenerRequired, res.Err)
+	}
+}
 
-	ListAssociateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", MemberListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		count++
+func TestListMembers(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleMemberListSuccessfully(t)
+
+	pages := 0
+	err := ListAssociateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", MemberListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
 		actual, err := ExtractMembers(page)
 		if err != nil {
-			t.Errorf("Failed to extract members: %v", err)
 			return false, err
 		}
 
-		expected := []Member{
-			{
-				SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
-				TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
-				AdminStateUp: true,
-				Name:         "member1",
-				ID:           "2a280670-c202-4b0b-a562-34077415aabf",
-				Address:      "10.0.2.10",
-				Weight:       5,
-				ProtocolPort: 80,
-			},
-			{
-				SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
-				TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
-				AdminStateUp: false,
-				Name:         "member2",
-				ID:           "fad389a3-9a4a-4762-a365-8c7038508b5d",
-				Address:      "10.0.2.11",
-				Weight:       10,
-				ProtocolPort: 80,
-			},
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 members, got %d", len(actual))
 		}
-
-		th.CheckDeepEquals(t, expected, actual)
+		th.CheckDeepEquals(t, MemberWeb, actual[0])
+		th.CheckDeepEquals(t, MemberDb, actual[1])
 
 		return true, nil
 	})
 
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
+	th.AssertNoErr(t, err)
+
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
 	}
 }
 
-func TestCreateAssociateMember(t *testing.T) {
+func TestListAllMembers(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandleMemberListSuccessfully(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "POST")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-    "member": {
-        "address": "10.0.2.10",
-        "weight": 5,
-        "name": "Example member",
-        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-        "protocol_port": 80
-    }
+	allPages, err := ListAssociateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", MemberListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := ExtractMembers(allPages)
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, MemberWeb, actual[0])
+	th.CheckDeepEquals(t, MemberDb, actual[1])
 }
-			`)
 
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusCreated)
+func TestCreateMember(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleMemberCreationSuccessfully(t, SingleMemberBody)
 
-		fmt.Fprintf(w, `
-{
-    "member": {
-        "id": "2a280670-c202-4b0b-a562-34077415aabf",
-        "address": "10.0.2.10",
-        "weight": 5,
-        "name": "Example member",
-        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-        "admin_state_up":true,
-        "protocol_port": 80
-    }
-}
-		`)
-	})
-
-	options := MemberCreateOpts{
-		Name:         "Example member",
+	actual, err := CreateAssociateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", MemberCreateOpts{
+		Name:         "db",
 		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
 		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
-		Address:      "10.0.2.10",
+		Address:      "10.0.2.11",
 		ProtocolPort: 80,
-		Weight:       5,
+		Weight:       10,
+	}).ExtractMember()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, MemberDb, *actual)
+}
+
+func TestRequiredMemberCreateOpts(t *testing.T) {
+	res := CreateAssociateMember(fake.ServiceClient(), "", MemberCreateOpts{})
+	if res.Err == nil {
+		t.Fatalf("Expected error, got none")
 	}
-	p, err := CreateAssociateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).ExtractMember()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "2a280670-c202-4b0b-a562-34077415aabf", p.ID)
-	th.AssertEquals(t, "Example member", p.Name)
-	th.AssertEquals(t, "1981f108-3c48-48d2-b908-30f7d28532c9", p.SubnetID)
-	th.AssertEquals(t, "2ffc6e22aae24e4795f87155d24c896f", p.TenantID)
+	res = CreateAssociateMember(fake.ServiceClient(), "", MemberCreateOpts{Address: "1.2.3.4", ProtocolPort: 80})
+	if res.Err == nil || res.Err != errPoolIdRequired {
+		t.Fatalf("Expected '%s' error, but got '%s'", errPoolIdRequired, res.Err)
+	}
+	res = CreateAssociateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", MemberCreateOpts{ProtocolPort: 80})
+	if res.Err == nil || res.Err != errAddressRequired {
+		t.Fatalf("Expected '%s' error, but got '%s'", errAddressRequired, res.Err)
+	}
+	res = CreateAssociateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", MemberCreateOpts{Address: "1.2.3.4"})
+	if res.Err == nil || res.Err != errProtocolPortRequired {
+		t.Fatalf("Expected '%s' error, but got '%s'", errProtocolPortRequired, res.Err)
+	}
 }
 
-func TestGetAssociateMember(t *testing.T) {
+func TestGetMember(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
+	HandleMemberGetSuccessfully(t)
 
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "GET")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+	client := fake.ServiceClient()
+	actual, err := GetAssociateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").ExtractMember()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
 
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "member": {
-        "id": "2a280670-c202-4b0b-a562-34077415aabf",
-        "address": "10.0.2.10",
-        "weight": 5,
-        "name": "Example member",
-        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-        "admin_state_up":true,
-        "protocol_port": 80
-    }
-}
-			`)
-	})
-
-	n, err := GetAssociateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").ExtractMember()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, n.ID, "2a280670-c202-4b0b-a562-34077415aabf")
-}
-
-func TestUpdateAssociateMember(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "PUT")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		th.TestHeader(t, r, "Content-Type", "application/json")
-		th.TestHeader(t, r, "Accept", "application/json")
-		th.TestJSONRequest(t, r, `
-{
-   "member":{
-      "name": "newMemberName",
-      "weight": 4
-   }
-}
-			`)
-
-		w.Header().Add("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-
-		fmt.Fprintf(w, `
-{
-   "member": {
-        "id": "2a280670-c202-4b0b-a562-34077415aabf",
-        "address": "10.0.2.10",
-        "weight": 4,
-        "name": "newMemberName",
-        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
-        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
-        "admin_state_up":true,
-        "protocol_port": 80
-    }
-}
-		`)
-	})
-
-	options := MemberUpdateOpts{Name: "newMemberName", Weight: 4}
-
-	n, err := UpdateAssociateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", options).ExtractMember()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, "newMemberName", n.Name)
-	th.AssertDeepEquals(t, 4, n.Weight)
+	th.CheckDeepEquals(t, MemberDb, *actual)
 }
 
 func TestDeleteMember(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
-
-	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
-		th.TestMethod(t, r, "DELETE")
-		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
-		w.WriteHeader(http.StatusNoContent)
-	})
+	HandleMemberDeletionSuccessfully(t)
 
 	res := DeleteMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf")
 	th.AssertNoErr(t, res.Err)
 }
+
+func TestUpdateMember(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleMemberUpdateSuccessfully(t)
+
+	client := fake.ServiceClient()
+	actual, err := UpdateAssociateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", MemberUpdateOpts{
+		Name:   "newMemberName",
+		Weight: 4,
+	}).ExtractMember()
+	if err != nil {
+		t.Fatalf("Unexpected Update error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, MemberUpdated, *actual)
+}
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go
index 040be79..17f677d 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go
@@ -1,9 +1,9 @@
 package pools
 
 import (
-	"github.com/davecgh/go-spew/spew"
 	"github.com/mitchellh/mapstructure"
 	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
 	"github.com/rackspace/gophercloud/pagination"
 )
 
@@ -30,6 +30,14 @@
 	CookieName string `mapstructure:"cookie_name" json:"cookie_name,omitempty"`
 }
 
+type LoadBalancerID struct {
+	ID string `mapstructure:"id" json:"id"`
+}
+
+type ListenerID struct {
+	ID string `mapstructure:"id" json:"id"`
+}
+
 // Pool represents a logical set of devices, such as web servers, that you
 // group together to receive and process traffic. The load balancing function
 // chooses a Member of the Pool according to the configured load balancing
@@ -47,13 +55,13 @@
 	Description string
 
 	// A list of listeners objects IDs.
-	Listeners []map[string]interface{} `mapstructure:"listeners" json:"listeners"`
+	Listeners []ListenerID `mapstructure:"listeners" json:"listeners"` //[]map[string]interface{}
 
 	// A list of member objects IDs.
-	Members []map[string]interface{} `mapstructure:"members" json:"members"`
+	Members []Member `mapstructure:"members" json:"members"`
 
-	// The IDs of associated monitors which check the health of the Pool.
-	MonitorIDs []string `json:"health_monitors" mapstructure:"health_monitors"`
+	// The ID of associated health monitor.
+	MonitorID string `json:"healthmonitor_id" mapstructure:"healthmonitor_id"`
 
 	// The network on which the members of the Pool will be located. Only members
 	// that are on this network can be added to the Pool.
@@ -69,14 +77,11 @@
 	// Pool name. Does not have to be unique.
 	Name string
 
-	// List of member IDs that belong to the Pool.
-	// MemberIDs []string `json:"members" mapstructure:"members"`
-
 	// The unique ID for the Pool.
 	ID string
 
 	// A list of load balancer objects IDs.
-	Loadbalancers []map[string]interface{} `mapstructure:"loadbalancers" json:"loadbalancers"`
+	Loadbalancers []LoadBalancerID `mapstructure:"loadbalancers" json:"loadbalancers"`
 
 	// Indicates whether connections in the same session will be processed by the
 	// same Pool member or not.
@@ -84,6 +89,8 @@
 
 	// The provider
 	Provider string
+
+	Monitor monitors.Monitor `mapstructure:"healthmonitor" json:"healthmonitor"`
 }
 
 // PoolPage is the page returned by a pager when traversing over a
@@ -112,7 +119,6 @@
 // IsEmpty checks whether a PoolPage struct is empty.
 func (p PoolPage) IsEmpty() (bool, error) {
 	is, err := ExtractPools(p)
-	spew.Dump(err)
 	if err != nil {
 		return true, nil
 	}
@@ -173,7 +179,7 @@
 	PoolID string `json:"pool_id" mapstructure:"pool_id"`
 
 	// The IP address of the Member.
-	Address string
+	Address string `json:"address" mapstructure:"address"`
 
 	// The port on which the application is hosted.
 	ProtocolPort int `json:"protocol_port" mapstructure:"protocol_port"`
diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go b/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go
index 0cdbc23..e206406 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go
@@ -5,7 +5,7 @@
 const (
 	rootPath     = "lbaas"
 	resourcePath = "pools"
-	memeberPath  = "members"
+	memberPath   = "members"
 )
 
 func rootURL(c *gophercloud.ServiceClient) string {
@@ -17,9 +17,9 @@
 }
 
 func memberRootURL(c *gophercloud.ServiceClient, poolId string) string {
-	return c.ServiceURL(rootPath, resourcePath, poolId, memeberPath)
+	return c.ServiceURL(rootPath, resourcePath, poolId, memberPath)
 }
 
 func memberResourceURL(c *gophercloud.ServiceClient, poolID string, memeberID string) string {
-	return c.ServiceURL(rootPath, resourcePath, poolID, memeberPath, memeberID)
+	return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memeberID)
 }