Adding health monitors :mag_right:
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests.go b/openstack/networking/v2/extensions/lbaas/monitors/requests.go
index 99fa30a..dcdb0cc 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/requests.go
@@ -1 +1,320 @@
 package monitors
+
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/racker/perigee"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/openstack/utils"
+	"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 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
+	TenantID      string
+	Type          string
+	Delay         int
+	Timeout       int
+	MaxRetries    int
+	HttpMethod    string
+	UrlPath       string
+	ExpectedCodes string
+	AdminStateUp  *bool
+	Status        string
+	Limit         int
+	Marker        string
+	SortKey       string
+	SortDir       string
+}
+
+// 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 := make(map[string]string)
+	if opts.ID != "" {
+		q["id"] = opts.ID
+	}
+	if opts.TenantID != "" {
+		q["tenant_id"] = opts.TenantID
+	}
+	if opts.Type != "" {
+		q["type"] = opts.Type
+	}
+	if opts.Delay != 0 {
+		q["delay"] = strconv.Itoa(opts.Delay)
+	}
+	if opts.Timeout != 0 {
+		q["timeout"] = strconv.Itoa(opts.Timeout)
+	}
+	if opts.MaxRetries != 0 {
+		q["max_retries"] = strconv.Itoa(opts.MaxRetries)
+	}
+	if opts.HttpMethod != "" {
+		q["http_method"] = opts.HttpMethod
+	}
+	if opts.UrlPath != "" {
+		q["url_path"] = opts.UrlPath
+	}
+	if opts.AdminStateUp != nil {
+		q["admin_state_up"] = strconv.FormatBool(*opts.AdminStateUp)
+	}
+	if opts.Status != "" {
+		q["status"] = opts.Status
+	}
+	if opts.ExpectedCodes != "" {
+		q["expected_codes"] = opts.ExpectedCodes
+	}
+	if opts.Marker != "" {
+		q["marker"] = opts.Marker
+	}
+	if opts.Limit != 0 {
+		q["limit"] = strconv.Itoa(opts.Limit)
+	}
+	if opts.SortKey != "" {
+		q["sort_key"] = opts.SortKey
+	}
+	if opts.SortDir != "" {
+		q["sort_dir"] = opts.SortDir
+	}
+
+	u := rootURL(c) + utils.BuildQuery(q)
+
+	return pagination.NewPager(c, u, func(r pagination.LastHTTPResponse) pagination.Page {
+		return MonitorPage{pagination.LinkedPageBase{LastHTTPResponse: r}}
+	})
+}
+
+const (
+	TypePING  = "PING"
+	TypeTCP   = "TCP"
+	TypeHTTP  = "HTTP"
+	TypeHTTPS = "HTTPS"
+)
+
+var (
+	errValidTypeRequired     = fmt.Errorf("A valid Type is required. Supported values are PING, TCP, HTTP and HTTPS")
+	errDelayRequired         = fmt.Errorf("Delay is required")
+	errTimeoutRequired       = fmt.Errorf("Timeout is required")
+	errMaxRetriesRequired    = fmt.Errorf("MaxRetries is required")
+	errURLPathRequired       = fmt.Errorf("URL path is required")
+	errExpectedCodesRequired = fmt.Errorf("ExpectedCodes is required")
+)
+
+// CreateOpts contains all the values needed to create a new health monitor.
+type CreateOpts struct {
+	// Required for admins. Indicates the owner of the VIP.
+	TenantID string
+
+	// Required. The type of probe, which is PING, TCP, HTTP, or HTTPS, that is
+	// sent by the load balancer to verify the member state.
+	Type string
+
+	// Required. The time, in seconds, between sending probes to members.
+	Delay int
+
+	// Required. Maximum number of seconds for a monitor to wait for a ping reply
+	// before it times out. The value must be less than the delay value.
+	Timeout int
+
+	// Required. Number of permissible ping failures before changing the member's
+	// status to INACTIVE. Must be a number between 1 and 10.
+	MaxRetries int
+
+	// Required for HTTP(S) types. URI path that will be accessed if monitor type
+	// is HTTP or HTTPS.
+	URLPath string
+
+	// Required for HTTP(S) types. The HTTP method used for requests by the
+	// monitor. If this attribute is not specified, it defaults to "GET".
+	HTTPMethod string
+
+	// Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S)
+	// monitor. You can either specify a single status like "200", or a range
+	// like "200-202".
+	ExpectedCodes string
+
+	AdminStateUp *bool
+}
+
+// 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"}
+//
+func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
+	var res CreateResult
+
+	// Validate inputs
+	allowed := map[string]bool{TypeHTTP: true, TypeHTTPS: true, TypeTCP: true, TypePING: true}
+	if opts.Type == "" || allowed[opts.Type] == false {
+		res.Err = errValidTypeRequired
+	}
+	if opts.Delay == 0 {
+		res.Err = errDelayRequired
+	}
+	if opts.Timeout == 0 {
+		res.Err = errTimeoutRequired
+	}
+	if opts.MaxRetries == 0 {
+		res.Err = errMaxRetriesRequired
+	}
+	if opts.Type == TypeHTTP || opts.Type == TypeHTTPS {
+		if opts.URLPath == "" {
+			res.Err = errURLPathRequired
+		}
+		if opts.ExpectedCodes == "" {
+			res.Err = errExpectedCodesRequired
+		}
+	}
+	if res.Err != nil {
+		return res
+	}
+
+	type monitor struct {
+		Type          string  `json:"type"`
+		Delay         int     `json:"delay"`
+		Timeout       int     `json:"timeout"`
+		MaxRetries    int     `json:"max_retries"`
+		TenantID      *string `json:"tenant_id,omitempty"`
+		URLPath       *string `json:"url_path,omitempty"`
+		ExpectedCodes *string `json:"expected_codes,omitempty"`
+		HTTPMethod    *string `json:"http_method,omitempty"`
+		AdminStateUp  *bool   `json:"admin_state_up,omitempty"`
+	}
+
+	type request struct {
+		Monitor monitor `json:"healthmonitor"`
+	}
+
+	reqBody := request{Monitor: monitor{
+		Type:          opts.Type,
+		Delay:         opts.Delay,
+		Timeout:       opts.Timeout,
+		MaxRetries:    opts.MaxRetries,
+		TenantID:      gophercloud.MaybeString(opts.TenantID),
+		URLPath:       gophercloud.MaybeString(opts.URLPath),
+		ExpectedCodes: gophercloud.MaybeString(opts.ExpectedCodes),
+		HTTPMethod:    gophercloud.MaybeString(opts.HTTPMethod),
+		AdminStateUp:  opts.AdminStateUp,
+	}}
+
+	_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		ReqBody:     &reqBody,
+		Results:     &res.Resp,
+		OkCodes:     []int{201},
+	})
+
+	return res
+}
+
+// Get retrieves a particular health monitor based on its unique ID.
+func Get(c *gophercloud.ServiceClient, id string) GetResult {
+	var res GetResult
+	_, err := perigee.Request("GET", resourceURL(c, id), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		Results:     &res.Resp,
+		OkCodes:     []int{200},
+	})
+	res.Err = err
+	return res
+}
+
+// UpdateOpts contains all the values needed to update an existing virtual IP.
+// Attributes not listed here but appear in CreateOpts are immutable and cannot
+// be updated.
+type UpdateOpts struct {
+	// Required. The time, in seconds, between sending probes to members.
+	Delay int
+
+	// Required. Maximum number of seconds for a monitor to wait for a ping reply
+	// before it times out. The value must be less than the delay value.
+	Timeout int
+
+	// Required. Number of permissible ping failures before changing the member's
+	// status to INACTIVE. Must be a number between 1 and 10.
+	MaxRetries int
+
+	// Required for HTTP(S) types. URI path that will be accessed if monitor type
+	// is HTTP or HTTPS.
+	URLPath string
+
+	// Required for HTTP(S) types. The HTTP method used for requests by the
+	// monitor. If this attribute is not specified, it defaults to "GET".
+	HTTPMethod string
+
+	// Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S)
+	// monitor. You can either specify a single status like "200", or a range
+	// like "200-202".
+	ExpectedCodes string
+
+	AdminStateUp *bool
+}
+
+// Update is an operation which modifies the attributes of the specified monitor.
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
+	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:"healthmonitor"`
+	}
+
+	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,
+	}}
+
+	var res UpdateResult
+
+	_, res.Err = perigee.Request("PUT", resourceURL(c, id), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		ReqBody:     &reqBody,
+		Results:     &res.Resp,
+		OkCodes:     []int{202},
+	})
+
+	return res
+}
+
+// Delete will permanently delete a particular monitor based on its unique ID.
+func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
+	var res DeleteResult
+	_, err := perigee.Request("DELETE", resourceURL(c, id), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{204},
+	})
+	res.Err = err
+	return res
+}
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go b/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go
index 99fa30a..3124729 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go
@@ -1 +1,286 @@
 package monitors
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+	th "github.com/rackspace/gophercloud/testhelper"
+)
+
+const tokenID = "123"
+
+func serviceClient() *gophercloud.ServiceClient {
+	return &gophercloud.ServiceClient{
+		Provider: &gophercloud.ProviderClient{TokenID: tokenID},
+		Endpoint: th.Endpoint(),
+	}
+}
+
+func TestURLs(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	th.AssertEquals(t, th.Endpoint()+"v2.0/lb/healthmonitors", rootURL(serviceClient()))
+}
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/healthmonitors", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", tokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+   "healthmonitors":[
+      {
+         "admin_state_up":true,
+         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+         "delay":10,
+         "max_retries":1,
+         "timeout":1,
+         "type":"PING",
+         "id":"466c8345-28d8-4f84-a246-e04380b0461d"
+      },
+      {
+         "admin_state_up":true,
+         "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+         "delay":5,
+         "expected_codes":"200",
+         "max_retries":2,
+         "http_method":"GET",
+         "timeout":2,
+         "url_path":"/",
+         "type":"HTTP",
+         "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
+      }
+   ]
+}
+			`)
+	})
+
+	count := 0
+
+	List(serviceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := ExtractMonitors(page)
+		if err != nil {
+			t.Errorf("Failed to extract monitors: %v", err)
+			return false, err
+		}
+
+		expected := []Monitor{
+			Monitor{
+				AdminStateUp: true,
+				TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
+				Delay:        10,
+				MaxRetries:   1,
+				Timeout:      1,
+				Type:         "PING",
+				ID:           "466c8345-28d8-4f84-a246-e04380b0461d",
+			},
+			Monitor{
+				AdminStateUp:  true,
+				TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
+				Delay:         5,
+				ExpectedCodes: "200",
+				MaxRetries:    2,
+				Timeout:       2,
+				URLPath:       "/",
+				Type:          "HTTP",
+				HTTPMethod:    "GET",
+				ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/healthmonitors", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", tokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+   "healthmonitor":{
+      "type":"HTTP",
+      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
+      "delay":20,
+      "timeout":10,
+      "max_retries":5,
+      "url_path":"/check",
+      "expected_codes":"200-299"
+   }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+   "healthmonitor":{
+      "id":"f3eeab00-8367-4524-b662-55e64d4cacb5",
+      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
+      "type":"HTTP",
+      "delay":20,
+      "timeout":10,
+      "max_retries":5,
+      "http_method":"GET",
+      "url_path":"/check",
+      "expected_codes":"200-299",
+      "admin_state_up":true,
+      "status":"ACTIVE"
+   }
+}
+		`)
+	})
+
+	_, err := Create(serviceClient(), CreateOpts{
+		Type:          "HTTP",
+		TenantID:      "453105b9-1754-413f-aab1-55f1af620750",
+		Delay:         20,
+		Timeout:       10,
+		MaxRetries:    5,
+		URLPath:       "/check",
+		ExpectedCodes: "200-299",
+	}).Extract()
+
+	th.AssertNoErr(t, err)
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/healthmonitors/f3eeab00-8367-4524-b662-55e64d4cacb5", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", tokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+   "healthmonitor":{
+      "id":"f3eeab00-8367-4524-b662-55e64d4cacb5",
+      "tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
+      "type":"HTTP",
+      "delay":20,
+      "timeout":10,
+      "max_retries":5,
+      "http_method":"GET",
+      "url_path":"/check",
+      "expected_codes":"200-299",
+      "admin_state_up":true,
+      "status":"ACTIVE"
+   }
+}
+			`)
+	})
+
+	hm, err := Get(serviceClient(), "f3eeab00-8367-4524-b662-55e64d4cacb5").Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "f3eeab00-8367-4524-b662-55e64d4cacb5", hm.ID)
+	th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", hm.TenantID)
+	th.AssertEquals(t, "HTTP", hm.Type)
+	th.AssertEquals(t, 20, hm.Delay)
+	th.AssertEquals(t, 10, hm.Timeout)
+	th.AssertEquals(t, 5, hm.MaxRetries)
+	th.AssertEquals(t, "GET", hm.HTTPMethod)
+	th.AssertEquals(t, "/check", hm.URLPath)
+	th.AssertEquals(t, "200-299", hm.ExpectedCodes)
+	th.AssertEquals(t, true, hm.AdminStateUp)
+	th.AssertEquals(t, "ACTIVE", hm.Status)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/healthmonitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", tokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+   "healthmonitor":{
+      "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, `
+{
+    "healthmonitor": {
+        "admin_state_up": true,
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "delay": 3,
+        "max_retries": 10,
+        "http_method": "GET",
+        "timeout": 20,
+        "pools": [
+            {
+                "status": "PENDING_CREATE",
+                "status_description": null,
+                "pool_id": "6e55751f-6ad4-4e53-b8d4-02e442cd21df"
+            }
+        ],
+        "type": "PING",
+        "id": "b05e44b5-81f9-4551-b474-711a722698f7"
+    }
+}
+		`)
+	})
+
+	_, err := Update(serviceClient(), "b05e44b5-81f9-4551-b474-711a722698f7", UpdateOpts{
+		Delay:         3,
+		Timeout:       20,
+		MaxRetries:    10,
+		URLPath:       "/another_check",
+		ExpectedCodes: "301",
+	}).Extract()
+
+	th.AssertNoErr(t, err)
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/lb/healthmonitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", tokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	res := Delete(serviceClient(), "b05e44b5-81f9-4551-b474-711a722698f7")
+	th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/results.go b/openstack/networking/v2/extensions/lbaas/monitors/results.go
index 99fa30a..44c1fff 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/results.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/results.go
@@ -1 +1,167 @@
 package monitors
+
+import (
+	"fmt"
+
+	"github.com/mitchellh/mapstructure"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// 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
+// with it. There are different types of health monitors supported:
+//
+// PING: used to ping the members using ICMP.
+// TCP: used to connect to the members using TCP.
+// HTTP: used to send an HTTP request to the member.
+// HTTPS: used to send a secure HTTP request to the member.
+//
+// When a pool has several monitors associated with it, each member of the pool
+// is monitored by all these monitors. If any monitor declares the member as
+// unhealthy, then the member status is changed to INACTIVE and the member
+// 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.
+	ID 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"`
+
+	// The type of probe sent by the load balancer to verify the member state,
+	// which is PING, TCP, HTTP, or HTTPS.
+	Type string
+
+	// The time, in seconds, between sending probes to members.
+	Delay int
+
+	// The maximum number of seconds for a monitor to wait for a connection to be
+	// established before it times out. This value must be less than the delay value.
+	Timeout int
+
+	// Number of allowed connection failures before changing the status of the
+	// member to INACTIVE. A valid value is from 1 to 10.
+	MaxRetries int `json:"max_retries" mapstructure:"max_retries"`
+
+	// The HTTP method that the monitor uses for requests.
+	HTTPMethod string `json:"http_method" mapstructure:"http_method"`
+
+	// The HTTP path of the request sent by the monitor to test the health of a
+	// member. Must be a string beginning with a forward slash (/).
+	URLPath string `json:"url_path" mapstructure:"url_path"`
+
+	// Expected HTTP codes for a passing HTTP(S) monitor.
+	ExpectedCodes string `json:"expected_codes" mapstructure:"expected_codes"`
+
+	// The administrative state of the health monitor, which is up (true) or down (false).
+	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
+
+	// The status of the health monitor. Indicates whether the health monitor is
+	// operational.
+	Status string
+}
+
+// MonitorPage is the page returned by a pager when traversing over a
+// collection of health monitors.
+type MonitorPage struct {
+	pagination.LinkedPageBase
+}
+
+// NextPageURL is invoked when a paginated collection of monitors has reached
+// the end of a page and the pager seeks to traverse over a new one. In order
+// to do this, it needs to construct the next page's URL.
+func (p MonitorPage) NextPageURL() (string, error) {
+	type link struct {
+		Href string `mapstructure:"href"`
+		Rel  string `mapstructure:"rel"`
+	}
+	type resp struct {
+		Links []link `mapstructure:"healthmonitors_links"`
+	}
+
+	var r resp
+	err := mapstructure.Decode(p.Body, &r)
+	if err != nil {
+		return "", err
+	}
+
+	var url string
+	for _, l := range r.Links {
+		if l.Rel == "next" {
+			url = l.Href
+		}
+	}
+	if url == "" {
+		return "", nil
+	}
+
+	return url, nil
+}
+
+// IsEmpty checks whether a PoolPage struct is empty.
+func (p MonitorPage) IsEmpty() (bool, error) {
+	is, err := ExtractMonitors(p)
+	if err != nil {
+		return true, nil
+	}
+	return len(is) == 0, nil
+}
+
+// ExtractPools accepts a Page struct, specifically a RouterPage struct,
+// and extracts the elements into a slice of Router structs. In other words,
+// a generic collection is mapped into a relevant slice.
+func ExtractMonitors(page pagination.Page) ([]Monitor, error) {
+	var resp struct {
+		Monitors []Monitor `mapstructure:"healthmonitors" json:"healthmonitors"`
+	}
+
+	err := mapstructure.Decode(page.(MonitorPage).Body, &resp)
+	if err != nil {
+		return nil, err
+	}
+
+	return resp.Monitors, nil
+}
+
+type commonResult struct {
+	gophercloud.CommonResult
+}
+
+// Extract is a function that accepts a result and extracts a monitor.
+func (r commonResult) Extract() (*Monitor, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+
+	var res struct {
+		Monitor *Monitor `json:"healthmonitor" mapstructure:"healthmonitor"`
+	}
+
+	err := mapstructure.Decode(r.Resp, &res)
+	if err != nil {
+		return nil, fmt.Errorf("Error decoding Neutron monitor: %v", err)
+	}
+
+	return res.Monitor, nil
+}
+
+// CreateResult represents the result of a create operation.
+type CreateResult struct {
+	commonResult
+}
+
+// GetResult represents the result of a get operation.
+type GetResult struct {
+	commonResult
+}
+
+// UpdateResult represents the result of an update operation.
+type UpdateResult struct {
+	commonResult
+}
+
+// DeleteResult represents the result of a delete operation.
+type DeleteResult commonResult
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/urls.go b/openstack/networking/v2/extensions/lbaas/monitors/urls.go
index 99fa30a..b6eeb24 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/urls.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/urls.go
@@ -1 +1,17 @@
 package monitors
+
+import "github.com/rackspace/gophercloud"
+
+const (
+	version      = "v2.0"
+	rootPath     = "lb"
+	resourcePath = "healthmonitors"
+)
+
+func rootURL(c *gophercloud.ServiceClient) string {
+	return c.ServiceURL(version, rootPath, resourcePath)
+}
+
+func resourceURL(c *gophercloud.ServiceClient, id string) string {
+	return c.ServiceURL(version, rootPath, resourcePath, id)
+}
diff --git a/openstack/networking/v2/extensions/lbaas/pools/results.go b/openstack/networking/v2/extensions/lbaas/pools/results.go
index 6cec6df..76f8f5d 100644
--- a/openstack/networking/v2/extensions/lbaas/pools/results.go
+++ b/openstack/networking/v2/extensions/lbaas/pools/results.go
@@ -53,6 +53,9 @@
 
 	// The ID of the virtual IP associated with this pool
 	VIPID string `json:"vip_id" mapstructure:"vip_id"`
+
+	// The provider
+	Provider string
 }
 
 // PoolPage is the page returned by a pager when traversing over a