Adding list pools operation
diff --git a/openstack/networking/v2/extensions/lbaas/pools/requests.go b/openstack/networking/v2/extensions/lbaas/pools/requests.go
index 058ebec..7337911 100644
--- a/openstack/networking/v2/extensions/lbaas/pools/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/pools/requests.go
@@ -1 +1,85 @@
package pools
+
+import (
+ "strconv"
+
+ "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 {
+ Status string
+ LBMethod string
+ Protocol string
+ SubnetID string
+ TenantID string
+ AdminStateUp *bool
+ Name string
+ ID string
+ VIPID string
+ Limit int
+ Marker string
+ SortKey string
+ SortDir string
+}
+
+// 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 := make(map[string]string)
+
+ if opts.Status != "" {
+ q["status"] = opts.Status
+ }
+ if opts.LBMethod != "" {
+ q["lb_method"] = opts.LBMethod
+ }
+ if opts.Protocol != "" {
+ q["protocol"] = opts.Protocol
+ }
+ if opts.SubnetID != "" {
+ q["subnet_id"] = opts.SubnetID
+ }
+ if opts.TenantID != "" {
+ q["tenant_id"] = opts.TenantID
+ }
+ if opts.AdminStateUp != nil {
+ q["admin_state_up"] = strconv.FormatBool(*opts.AdminStateUp)
+ }
+ if opts.Name != "" {
+ q["name"] = opts.Name
+ }
+ if opts.ID != "" {
+ q["id"] = opts.ID
+ }
+ if opts.VIPID != "" {
+ q["vip_id"] = opts.VIPID
+ }
+ 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 PoolPage{pagination.LinkedPageBase{LastHTTPResponse: r}}
+ })
+}
diff --git a/openstack/networking/v2/extensions/lbaas/pools/requests_test.go b/openstack/networking/v2/extensions/lbaas/pools/requests_test.go
index 058ebec..6c3b23d 100644
--- a/openstack/networking/v2/extensions/lbaas/pools/requests_test.go
+++ b/openstack/networking/v2/extensions/lbaas/pools/requests_test.go
@@ -1 +1,112 @@
package pools
+
+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/pools", rootURL(serviceClient()))
+}
+
+func TestList(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/v2.0/lb/pools", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "GET")
+ th.TestHeader(t, r, "X-Auth-Token", tokenID)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+
+ fmt.Fprintf(w, `
+{
+ "pools":[
+ {
+ "status":"ACTIVE",
+ "lb_method":"ROUND_ROBIN",
+ "protocol":"HTTP",
+ "description":"",
+ "health_monitors":[
+ "466c8345-28d8-4f84-a246-e04380b0461d",
+ "5d4b5228-33b0-4e60-b225-9b727c1a20e7"
+ ],
+ "members":[
+ "701b531b-111a-4f21-ad85-4795b7b12af6",
+ "beb53b4d-230b-4abd-8118-575b8fa006ef"
+ ],
+ "status_description": null,
+ "id":"72741b06-df4d-4715-b142-276b6bce75ab",
+ "vip_id":"4ec89087-d057-4e2c-911f-60a3b47ee304",
+ "name":"app_pool",
+ "admin_state_up":true,
+ "subnet_id":"8032909d-47a1-4715-90af-5153ffe39861",
+ "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
+ "health_monitors_status": [],
+ "provider": "haproxy"
+ }
+ ]
+}
+ `)
+ })
+
+ count := 0
+
+ List(serviceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+ count++
+ actual, err := ExtractPools(page)
+ if err != nil {
+ t.Errorf("Failed to extract pools: %v", err)
+ return false, err
+ }
+
+ expected := []Pool{
+ Pool{
+ Status: "ACTIVE",
+ LBMethod: "ROUND_ROBIN",
+ Protocol: "HTTP",
+ Description: "",
+ MonitorIDs: []string{
+ "466c8345-28d8-4f84-a246-e04380b0461d",
+ "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
+ },
+ SubnetID: "8032909d-47a1-4715-90af-5153ffe39861",
+ TenantID: "83657cfcdfe44cd5920adaf26c48ceea",
+ AdminStateUp: true,
+ Name: "app_pool",
+ MemberIDs: []string{
+ "701b531b-111a-4f21-ad85-4795b7b12af6",
+ "beb53b4d-230b-4abd-8118-575b8fa006ef",
+ },
+ ID: "72741b06-df4d-4715-b142-276b6bce75ab",
+ VIPID: "4ec89087-d057-4e2c-911f-60a3b47ee304",
+ },
+ }
+
+ th.CheckDeepEquals(t, expected, actual)
+
+ return true, nil
+ })
+
+ if count != 1 {
+ t.Errorf("Expected 1 page, got %d", count)
+ }
+}
diff --git a/openstack/networking/v2/extensions/lbaas/pools/results.go b/openstack/networking/v2/extensions/lbaas/pools/results.go
index 058ebec..a9298b7 100644
--- a/openstack/networking/v2/extensions/lbaas/pools/results.go
+++ b/openstack/networking/v2/extensions/lbaas/pools/results.go
@@ -1 +1,109 @@
package pools
+
+import (
+ "github.com/mitchellh/mapstructure"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+type Pool struct {
+ // The status of the pool. Indicates whether the pool is operational.
+ Status string
+
+ // The load-balancer algorithm, which is round-robin, least-connections, and
+ // so on. This value, which must be supported, is dependent on the provider.
+ // Round-robin must be supported.
+ LBMethod string `json:"lb_method" mapstructure:"lb_method"`
+
+ // The protocol of the pool, which is TCP, HTTP, or HTTPS.
+ Protocol string
+
+ // Description for the pool.
+ Description string
+
+ // The IDs of associated monitors which check the health of the pool members.
+ MonitorIDs []string `json:"health_monitors" mapstructure:"health_monitors"`
+
+ // The ID of the subnet associated with the pool
+ SubnetID string `json:"subnet_id" mapstructure:"subnet_id"`
+
+ // Owner of the pool. Only an administrative user can specify a tenant ID
+ // other than its own.
+ TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+
+ // The administrative state of the pool, which is up (true) or down (false).
+ AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
+
+ // Pool name. Does not have to be unique.
+ Name string
+
+ // List of member IDs that belong to the pool.
+ MemberIDs []string `json:"members" mapstructure:"members"`
+
+ // The unique ID for the pool.
+ ID string
+
+ // The ID of the virtual IP associated with this pool
+ VIPID string `json:"vip_id" mapstructure:"vip_id"`
+}
+
+// PoolPage is the page returned by a pager when traversing over a
+// collection of pools.
+type PoolPage struct {
+ pagination.LinkedPageBase
+}
+
+// NextPageURL is invoked when a paginated collection of pools has reached
+// the end of a page and the pager seeks to traverse over a new one. In order
+// to do this, it needs to construct the next page's URL.
+func (p PoolPage) NextPageURL() (string, error) {
+ type link struct {
+ Href string `mapstructure:"href"`
+ Rel string `mapstructure:"rel"`
+ }
+ type resp struct {
+ Links []link `mapstructure:"pools_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 PoolPage) IsEmpty() (bool, error) {
+ is, err := ExtractPools(p)
+ if err != nil {
+ return true, nil
+ }
+ return len(is) == 0, nil
+}
+
+// ExtractRouters accepts a Page struct, specifically a RouterPage struct,
+// and extracts the elements into a slice of Router structs. In other words,
+// a generic collection is mapped into a relevant slice.
+func ExtractPools(page pagination.Page) ([]Pool, error) {
+ var resp struct {
+ Pools []Pool `mapstructure:"pools" json:"pools"`
+ }
+
+ err := mapstructure.Decode(page.(PoolPage).Body, &resp)
+ if err != nil {
+ return nil, err
+ }
+
+ return resp.Pools, nil
+}
diff --git a/openstack/networking/v2/extensions/lbaas/pools/urls.go b/openstack/networking/v2/extensions/lbaas/pools/urls.go
index 058ebec..8b73508 100644
--- a/openstack/networking/v2/extensions/lbaas/pools/urls.go
+++ b/openstack/networking/v2/extensions/lbaas/pools/urls.go
@@ -1 +1,17 @@
package pools
+
+import "github.com/rackspace/gophercloud"
+
+const (
+ version = "v2.0"
+ rootPath = "lb"
+ resourcePath = "pools"
+)
+
+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/vips/requests.go b/openstack/networking/v2/extensions/lbaas/vips/requests.go
index 02a2e60..31ed975 100644
--- a/openstack/networking/v2/extensions/lbaas/vips/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/vips/requests.go
@@ -10,9 +10,16 @@
"github.com/rackspace/gophercloud/pagination"
)
-const (
- Up = true
- Down = false
+type AdminState *bool
+
+// Convenience vars for AdminStateUp values.
+var (
+ iTrue = true
+ iFalse = false
+
+ Nothing AdminState = nil
+ Up AdminState = &iTrue
+ Down AdminState = &iFalse
)
// ListOpts allows the filtering and sorting of paginated collections through
diff --git a/openstack/networking/v2/extensions/lbaas/vips/requests_test.go b/openstack/networking/v2/extensions/lbaas/vips/requests_test.go
index 1419a09..5bfd7f3 100644
--- a/openstack/networking/v2/extensions/lbaas/vips/requests_test.go
+++ b/openstack/networking/v2/extensions/lbaas/vips/requests_test.go
@@ -177,11 +177,10 @@
`)
})
- iTrue := true
opts := CreateOpts{
Protocol: "HTTP",
Name: "NewVip",
- AdminStateUp: &iTrue,
+ AdminStateUp: Up,
SubnetID: "8032909d-47a1-4715-90af-5153ffe39861",
PoolID: "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
ProtocolPort: 80,